Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/apache/derby/impl/sql/compile/CoalesceFunctionNode.java


1   /*
2   
3      Derby - Class org.apache.derby.impl.sql.compile.CoalesceFunctionNode
4   
5      Copyright 2004 The Apache Software Foundation or its licensors, as applicable.
6   
7      Licensed under the Apache License, Version 2.0 (the "License");
8      you may not use this file except in compliance with the License.
9      You may obtain a copy of the License at
10  
11        http://www.apache.org/licenses/LICENSE-2.0
12  
13     Unless required by applicable law or agreed to in writing, software
14     distributed under the License is distributed on an "AS IS" BASIS,
15     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16     See the License for the specific language governing permissions and
17     limitations under the License.
18  
19   */
20  
21  package  org.apache.derby.impl.sql.compile;
22  
23  import org.apache.derby.iapi.reference.ClassName;
24  import org.apache.derby.iapi.reference.SQLState;
25  
26  import org.apache.derby.iapi.services.classfile.VMOpcode;
27  
28  import org.apache.derby.iapi.services.sanity.SanityManager;
29  
30  import org.apache.derby.iapi.error.StandardException;
31  
32  import org.apache.derby.impl.sql.compile.ExpressionClassBuilder;
33  
34  import org.apache.derby.iapi.services.compiler.LocalField;
35  import org.apache.derby.iapi.services.compiler.MethodBuilder;
36  
37  import java.lang.reflect.Modifier;
38  
39  import java.util.Vector;
40  
41  /**
42   * This node represents coalesce/value function which returns the first argument that is not null.
43   * The arguments are evaluated in the order in which they are specified, and the result of the
44   * function is the first argument that is not null. The result can be null only if all the arguments
45   * can be null. The selected argument is converted, if necessary, to the attributes of the result.
46   *
47   *
48   * SQL Reference Guide for DB2 has section titled "Rules for result data types" at the following url
49   * http://publib.boulder.ibm.com/infocenter/db2help/index.jsp?topic=/com.ibm.db2.udb.doc/admin/r0008480.htm
50  
51   * I have constructed following table based on various tables and information under "Rules for result data types"
52   * This table has FOR BIT DATA TYPES broken out into separate columns for clarity
53   *
54   * Note that are few differences between Cloudscape and DB2
55   * 1)there are few differences between what datatypes are consdiered compatible
56   * In DB2, CHAR FOR BIT DATA datatypes are compatible with CHAR datatypes
57   * ie in addition to following table, CHAR is compatible with CHAR FOR BIT DATA, VARCHAR FOR BIT DATA and LONG VARCHAR FOR BIT DATA
58   * ie in addition to following table, VARCHAR is compatible with CHAR FOR BIT DATA, VARCHAR FOR BIT DATA and LONG VARCHAR FOR BIT DATA
59   * ie in addition to following table, LONG VARCHAR is compatible with CHAR FOR BIT DATA, VARCHAR FOR BIT DATA and LONG VARCHAR FOR BIT DATA
60   * ie in addition to following table, CHAR FOR BIT DATA is compatible with DATE, TIME, TIMESTAMP
61   * ie in addition to following table, VARCHAR FOR BIT DATA is compatible with DATE, TIME, TIMESTAMP
62   *
63   * 2)few datatypes donot have matching precision in Cloudscape and DB2
64   * In DB2, precision of TIME is 8. In Cloudscape, precision of TIME is 0.
65   * In DB2, precision,scale of TIMESTAMP is 26,6. In Cloudscape, precision of TIMESTAMP is 0,0.
66   * In DB2, precision of DOUBLE is 15. In Cloudscape, precision of DOUBLE is 52.
67   * In DB2, precision of REAL is 23. In Cloudscape, precision of REAL is 7.
68   * In DB2, precision calculation equation is incorrect when we have int and decimal arguments.
69   * The equation should be p=x+max(w-x,10) since precision of integer is 10 in both db2 and cloudscape. Instead, DB2 has p=x+max(w-x,11) 
70   *
71   * Types.             S  I  B  D  R  D  C  V  L  C  V  L  C  D  T  T  B
72   *                    M  N  I  E  E  O  H  A  O  H  A  O  L  A  I  I  L
73   *                    A  T  G  C  A  U  A  R  N  A  R  N  O  T  M  M  O
74   *                    L  E  I  I  L  B  R  C  G  R  C  G  B  E  E  E  B
75   *                    L  G  N  M     L     H  V  .  H  V           S
76   *                    I  E  T  A     E     A  A  B  A  A           T
77   *                    N  R     L           R  R  I  R  R           A
78   *                    T                       C  T  .  .           M
79   *                                            H     B  B           P
80   *                                            A     I  I
81   *                                            R     T   T
82   * SMALLINT         { "SMALLINT", "INTEGER", "BIGINT", "DECIMAL", "DOUBLE", "DOUBLE", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR" },
83   * INTEGER          { "INTEGER", "INTEGER", "BIGINT", "DECIMAL", "DOUBLE", "DOUBLE", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR" },
84   * BIGINT           { "BIGINT", "BIGINT", "BIGINT", "DECIMAL", "DOUBLE", "DOUBLE", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR" },
85   * DECIMAL          { "DECIMAL", "DECIMAL", "DECIMAL", "DECIMAL", "DOUBLE", "DOUBLE", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR" },
86   * REAL             { "DOUBLE", "DOUBLE", "DOUBLE", "DOUBLE", "REAL", "DOUBLE", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR" },
87   * DOUBLE           { "DOUBLE", "DOUBLE", "DOUBLE", "DOUBLE", "DOUBLE", "DOUBLE", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR" },
88   * CHAR             { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "CHAR", "VARCHAR", "LONG VARCHAR", "ERROR", "ERROR", "ERROR", "CLOB", "DATE", "TIME", "TIMESTAMP", "ERROR" },
89   * VARCHAR          { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "VARCHAR", "VARCHAR","LONG VARCHAR", "ERROR", "ERROR", "ERROR", "CLOB", "DATE", "TIME", "TIMESTAMP", "ERROR" },
90   * LONGVARCHAR      { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "LONG VARCHAR", "LONG VARCHAR", "LONG VARCHAR", "ERROR", "ERROR", "ERROR", "CLOB", "ERROR", "ERROR", "ERROR", "ERROR" },
91   * CHAR FOR BIT     { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "BIT", "BIT VARYING", "LONG BIT VARYING", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR" },
92   * VARCH. BIT       { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "BIT VARYING", "BIT VARYING", "LONG BIT VARYING", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR" },
93   * LONGVAR. BIT     { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "LONG BIT VARYING", "LONG BIT VARYING", "LONG BIT VARYING", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR" },
94   * CLOB             { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "CLOB", "CLOB", "CLOB", "ERROR", "ERROR", "ERROR", "CLOB", "ERROR", "ERROR", "ERROR", "ERROR" },
95   * DATE             { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "DATE", "DATE", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "DATE", "ERROR", "ERROR", "ERROR" },
96   * TIME             { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "TIME", "TIME", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "TIME", "ERROR", "ERROR" },
97   * TIMESTAMP        { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "TIMESTAMP", "TIMESTAMP", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "TIMESTAMP", "ERROR" },
98   * BLOB             { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "BLOB" }
99   */
100 
101 public class CoalesceFunctionNode extends ValueNode
102 {
103   String  functionName; //Are we here because of COALESCE function or VALUE function
104   ValueNodeList  argumentsList; //this is the list of arguments to the function. We are interested in the first not-null argument
105   ValueNode firstNonParameterNode;//The generated method will generate code to call coalesce on this non-parameter argument
106 
107   /**
108    * Initializer for a CalesceFunctionNode
109    *
110    * @param functionName  Tells if the function was called with name COALESCE or with name VALUE
111    * @param argumentsList  The list of arguments to the coalesce/value function
112    */
113   public void init(Object functionName, Object argumentsList)
114   {
115     this.functionName = (String) functionName;
116     this.argumentsList = (ValueNodeList) argumentsList;
117   }
118 
119   /**
120    * Binding this expression means setting the result DataTypeServices.
121    * In this case, the result type is based on the rules in the table listed earlier.
122    *
123    * @param fromList      The FROM list for the statement.
124    * @param subqueryList    The subquery list being built as we find SubqueryNodes.
125    * @param aggregateVector  The aggregate vector being built as we find AggregateNodes.
126    *
127    * @return  The new top of the expression tree.
128    *
129    * @exception StandardException    Thrown on error
130    */
131   public ValueNode bindExpression(FromList fromList, SubqueryList subqueryList,
132               Vector  aggregateVector)
133           throws StandardException
134   {
135     //bind all the arguments
136     argumentsList.bindExpression(fromList, subqueryList, aggregateVector);
137 
138     //There should be more than one argument
139     if (argumentsList.size() < 2)
140       throw StandardException.newException(SQLState.LANG_DB2_NUMBER_OF_ARGS_INVALID, functionName);
141 
142     //check if all the arguments are parameters. If yes, then throw an exception
143     if (argumentsList.containsAllParameterNodes())
144       throw StandardException.newException(SQLState.LANG_DB2_COALESCE_FUNCTION_ALL_PARAMS);
145 
146     int argumentsListSize = argumentsList.size();
147     //find the first non-param argument. The generated method will generate code to call coalesce on this argument
148     for (int index = 0; index < argumentsListSize; index++)
149     {
150       if (!(((ValueNode) argumentsList.elementAt(index)).isParameterNode()))
151       {
152         firstNonParameterNode = (ValueNode) argumentsList.elementAt(index);
153         break;
154       }
155     }
156 
157     //make sure these arguments are compatible to each other before coalesce can be allowed
158     for (int index = 0; index < argumentsListSize; index++)
159     {
160       if (((ValueNode) argumentsList.elementAt(index)).isParameterNode()) //since we don't know the type of param, can't check for compatibility
161         continue;
162         argumentsList.compatible((ValueNode) argumentsList.elementAt(index));
163     }
164 
165     //set the result type to the most dominant datatype in the arguments list and based on the table listed above
166     setType(argumentsList.getDominantTypeServices());
167 
168     //set all the parameter types to the type of the result type
169     for (int index = 0; index < argumentsListSize; index++)
170     {
171       if (((ValueNode) argumentsList.elementAt(index)).isParameterNode())
172       {
173         ((ParameterNode) argumentsList.elementAt(index)).setDescriptor(getTypeServices());
174         break;
175       }
176     }
177     return this;
178   }
179 
180   /**
181    * Do code generation for coalese/value
182    *
183    * @param acb  The ExpressionClassBuilder for the class we're generating
184    * @param mb  The method the expression will go into
185    *
186    * @exception StandardException    Thrown on error
187    */
188 
189   public void generateExpression(ExpressionClassBuilder acb,
190                       MethodBuilder mb)
191                   throws StandardException
192   {
193     int      argumentsListSize = argumentsList.size();
194     String    receiverType = ClassName.DataValueDescriptor;
195     String    argumentsListInterfaceType = ClassName.DataValueDescriptor + "[]";
196 
197     // Generate the code to build the array
198     LocalField arrayField =
199       acb.newFieldDeclaration(Modifier.PRIVATE, argumentsListInterfaceType);
200 
201     /* The array gets created in the constructor.
202      * All constant elements in the array are initialized
203      * in the constructor.  
204      */
205     /* Assign the initializer to the DataValueDescriptor[] field */
206     MethodBuilder cb = acb.getConstructor();
207     cb.pushNewArray(ClassName.DataValueDescriptor, argumentsListSize);
208     cb.setField(arrayField);
209 
210     /* Set the array elements that are constant */
211     int numConstants = 0;
212     MethodBuilder nonConstantMethod = null;
213     MethodBuilder currentConstMethod = cb;
214     for (int index = 0; index < argumentsListSize; index++)
215     {
216       MethodBuilder setArrayMethod;
217   
218       if (argumentsList.elementAt(index) instanceof ConstantNode)
219       {
220         numConstants++;
221     
222         /*if too many statements are added  to a  method, 
223         *size of method can hit  65k limit, which will
224         *lead to the class format errors at load time.
225         *To avoid this problem, when number of statements added 
226         *to a method is > 2048, remaing statements are added to  a new function
227         *and called from the function which created the function.
228         *See Beetle 5135 or 4293 for further details on this type of problem.
229         */
230         if(currentConstMethod.statementNumHitLimit(1))
231         {
232           MethodBuilder genConstantMethod = acb.newGeneratedFun("void", Modifier.PRIVATE);
233           currentConstMethod.pushThis();
234           currentConstMethod.callMethod(VMOpcode.INVOKEVIRTUAL,
235                           (String) null, 
236                           genConstantMethod.getName(),
237                           "void", 0);
238           //if it is a generate function, close the metod.
239           if(currentConstMethod != cb){
240             currentConstMethod.methodReturn();
241             currentConstMethod.complete();
242           }
243           currentConstMethod = genConstantMethod;
244         }
245         setArrayMethod = currentConstMethod;
246       } else {
247         if (nonConstantMethod == null)
248           nonConstantMethod = acb.newGeneratedFun("void", Modifier.PROTECTED);
249         setArrayMethod = nonConstantMethod;
250 
251       }
252 
253       setArrayMethod.getField(arrayField); 
254       ((ValueNode) argumentsList.elementAt(index)).generateExpression(acb, setArrayMethod);
255       setArrayMethod.upCast(receiverType);
256       setArrayMethod.setArrayElement(index);
257     }
258 
259     //if a generated function was created to reduce the size of the methods close the functions.
260     if(currentConstMethod != cb){
261       currentConstMethod.methodReturn();
262       currentConstMethod.complete();
263     }
264 
265     if (nonConstantMethod != null) {
266       nonConstantMethod.methodReturn();
267       nonConstantMethod.complete();
268       mb.pushThis();
269       mb.callMethod(VMOpcode.INVOKEVIRTUAL, (String) null, nonConstantMethod.getName(), "void", 0);
270     }
271 
272     /*
273     **  Call the method for coalesce/value function.
274     **  First generate following
275     **  <first non-param argument in the list>.method(<all the arguments>, <resultType>)
276     **  Next, if we are dealing with result type that is variable length, then generate a call to setWidth.
277     */
278 
279     firstNonParameterNode.generateExpression(acb, mb); //coalesce will be called on this non-parameter argument
280     mb.upCast(ClassName.DataValueDescriptor);
281 
282     mb.getField(arrayField); // first arg to the coalesce function
283 
284     //Following is for the second arg. This arg will be used to pass the return value.
285     //COALESCE method expects this to be initialized to NULL SQLxxx type object.
286     LocalField field = acb.newFieldDeclaration(Modifier.PRIVATE, receiverType);
287     acb.generateNull(mb, getTypeCompiler());
288     mb.upCast(ClassName.DataValueDescriptor);
289     mb.putField(field);
290 
291     mb.callMethod(VMOpcode.INVOKEINTERFACE, receiverType, "coalesce", receiverType, 2);
292     if (getTypeId().variableLength())//since result type is variable length, generate setWidth code.
293     {
294       boolean isNumber = getTypeId().isNumericTypeId();
295       mb.push(isNumber ? getTypeServices().getPrecision() : getTypeServices().getMaximumWidth());
296       mb.push(getTypeServices().getScale());
297       mb.push(true);
298       mb.callMethod(VMOpcode.INVOKEINTERFACE, ClassName.VariableSizeDataValue, "setWidth", ClassName.DataValueDescriptor, 3);
299     }
300   }
301 
302   /*
303     print the non-node subfields
304    */
305   public String toString() {
306     if (SanityManager.DEBUG)
307     {
308       return super.toString()+functionName+"("+argumentsList+")\n";
309     }
310     else
311     {
312       return "";
313     }
314   }
315 
316 }