KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > derby > impl > sql > compile > CoalesceFunctionNode


1 /*
2
3    Derby - Class org.apache.derby.impl.sql.compile.CoalesceFunctionNode
4
5    Licensed to the Apache Software Foundation (ASF) under one or more
6    contributor license agreements. See the NOTICE file distributed with
7    this work for additional information regarding copyright ownership.
8    The ASF licenses this file to you under the Apache License, Version 2.0
9    (the "License"); you may not use this file except in compliance with
10    the License. You may obtain a copy of the License at
11
12       http://www.apache.org/licenses/LICENSE-2.0
13
14    Unless required by applicable law or agreed to in writing, software
15    distributed under the License is distributed on an "AS IS" BASIS,
16    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17    See the License for the specific language governing permissions and
18    limitations under the License.
19
20  */

21
22 package org.apache.derby.impl.sql.compile;
23
24 import org.apache.derby.iapi.reference.ClassName;
25 import org.apache.derby.iapi.reference.SQLState;
26
27 import org.apache.derby.iapi.services.classfile.VMOpcode;
28
29 import org.apache.derby.iapi.services.sanity.SanityManager;
30
31 import org.apache.derby.iapi.error.StandardException;
32
33 import org.apache.derby.impl.sql.compile.ExpressionClassBuilder;
34
35 import org.apache.derby.iapi.services.compiler.LocalField;
36 import org.apache.derby.iapi.services.compiler.MethodBuilder;
37 import org.apache.derby.iapi.sql.compile.Visitable;
38 import org.apache.derby.iapi.sql.compile.Visitor;
39
40 import java.lang.reflect.Modifier JavaDoc;
41
42 import java.util.Iterator JavaDoc;
43 import java.util.Vector JavaDoc;
44
45 /**
46  * This node represents coalesce/value function which returns the first argument that is not null.
47  * The arguments are evaluated in the order in which they are specified, and the result of the
48  * function is the first argument that is not null. The result can be null only if all the arguments
49  * can be null. The selected argument is converted, if necessary, to the attributes of the result.
50  *
51  *
52  * SQL Reference Guide for DB2 has section titled "Rules for result data types" at the following url
53  * http://publib.boulder.ibm.com/infocenter/db2help/index.jsp?topic=/com.ibm.db2.udb.doc/admin/r0008480.htm
54
55  * I have constructed following table based on various tables and information under "Rules for result data types"
56  * This table has FOR BIT DATA TYPES broken out into separate columns for clarity
57  *
58  * Note that are few differences between Cloudscape and DB2
59  * 1)there are few differences between what datatypes are consdiered compatible
60  * In DB2, CHAR FOR BIT DATA datatypes are compatible with CHAR datatypes
61  * ie in addition to following table, CHAR is compatible with CHAR FOR BIT DATA, VARCHAR FOR BIT DATA and LONG VARCHAR FOR BIT DATA
62  * ie in addition to following table, VARCHAR is compatible with CHAR FOR BIT DATA, VARCHAR FOR BIT DATA and LONG VARCHAR FOR BIT DATA
63  * 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
64  * ie in addition to following table, CHAR FOR BIT DATA is compatible with DATE, TIME, TIMESTAMP
65  * ie in addition to following table, VARCHAR FOR BIT DATA is compatible with DATE, TIME, TIMESTAMP
66  *
67  * 2)few datatypes donot have matching precision in Cloudscape and DB2
68  * In DB2, precision of TIME is 8. In Cloudscape, precision of TIME is 0.
69  * In DB2, precision,scale of TIMESTAMP is 26,6. In Cloudscape, precision of TIMESTAMP is 0,0.
70  * In DB2, precision of DOUBLE is 15. In Cloudscape, precision of DOUBLE is 52.
71  * In DB2, precision of REAL is 23. In Cloudscape, precision of REAL is 7.
72  * In DB2, precision calculation equation is incorrect when we have int and decimal arguments.
73  * 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)
74  *
75  * Types. S I B D R D C V L C V L C D T T B
76  * M N I E E O H A O H A O L A I I L
77  * A T G C A U A R N A R N O T M M O
78  * L E I I L B R C G R C G B E E E B
79  * L G N M L H V . H V S
80  * I E T A E A A B A A T
81  * N R L R R I R R A
82  * T C T . . M
83  * H B B P
84  * A I I
85  * R T T
86  * SMALLINT { "SMALLINT", "INTEGER", "BIGINT", "DECIMAL", "DOUBLE", "DOUBLE", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR" },
87  * INTEGER { "INTEGER", "INTEGER", "BIGINT", "DECIMAL", "DOUBLE", "DOUBLE", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR" },
88  * BIGINT { "BIGINT", "BIGINT", "BIGINT", "DECIMAL", "DOUBLE", "DOUBLE", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR" },
89  * DECIMAL { "DECIMAL", "DECIMAL", "DECIMAL", "DECIMAL", "DOUBLE", "DOUBLE", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR" },
90  * REAL { "DOUBLE", "DOUBLE", "DOUBLE", "DOUBLE", "REAL", "DOUBLE", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR" },
91  * DOUBLE { "DOUBLE", "DOUBLE", "DOUBLE", "DOUBLE", "DOUBLE", "DOUBLE", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR" },
92  * CHAR { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "CHAR", "VARCHAR", "LONG VARCHAR", "ERROR", "ERROR", "ERROR", "CLOB", "DATE", "TIME", "TIMESTAMP", "ERROR" },
93  * VARCHAR { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "VARCHAR", "VARCHAR","LONG VARCHAR", "ERROR", "ERROR", "ERROR", "CLOB", "DATE", "TIME", "TIMESTAMP", "ERROR" },
94  * LONGVARCHAR { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "LONG VARCHAR", "LONG VARCHAR", "LONG VARCHAR", "ERROR", "ERROR", "ERROR", "CLOB", "ERROR", "ERROR", "ERROR", "ERROR" },
95  * CHAR FOR BIT { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "BIT", "BIT VARYING", "LONG BIT VARYING", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR" },
96  * VARCH. BIT { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "BIT VARYING", "BIT VARYING", "LONG BIT VARYING", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR" },
97  * 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" },
98  * CLOB { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "CLOB", "CLOB", "CLOB", "ERROR", "ERROR", "ERROR", "CLOB", "ERROR", "ERROR", "ERROR", "ERROR" },
99  * DATE { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "DATE", "DATE", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "DATE", "ERROR", "ERROR", "ERROR" },
100  * TIME { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "TIME", "TIME", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "TIME", "ERROR", "ERROR" },
101  * TIMESTAMP { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "TIMESTAMP", "TIMESTAMP", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "TIMESTAMP", "ERROR" },
102  * BLOB { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "BLOB" }
103  */

104
105 public class CoalesceFunctionNode extends ValueNode
106 {
107     String JavaDoc functionName; //Are we here because of COALESCE function or VALUE function
108
ValueNodeList argumentsList; //this is the list of arguments to the function. We are interested in the first not-null argument
109
ValueNode firstNonParameterNode;//The generated method will generate code to call coalesce on this non-parameter argument
110

111     /**
112      * Initializer for a CalesceFunctionNode
113      *
114      * @param functionName Tells if the function was called with name COALESCE or with name VALUE
115      * @param argumentsList The list of arguments to the coalesce/value function
116      */

117     public void init(Object JavaDoc functionName, Object JavaDoc argumentsList)
118     {
119         this.functionName = (String JavaDoc) functionName;
120         this.argumentsList = (ValueNodeList) argumentsList;
121     }
122
123     /**
124      * Binding this expression means setting the result DataTypeServices.
125      * In this case, the result type is based on the rules in the table listed earlier.
126      *
127      * @param fromList The FROM list for the statement.
128      * @param subqueryList The subquery list being built as we find SubqueryNodes.
129      * @param aggregateVector The aggregate vector being built as we find AggregateNodes.
130      *
131      * @return The new top of the expression tree.
132      *
133      * @exception StandardException Thrown on error
134      */

135     public ValueNode bindExpression(FromList fromList, SubqueryList subqueryList,
136                             Vector JavaDoc aggregateVector)
137                     throws StandardException
138     {
139         //bind all the arguments
140
argumentsList.bindExpression(fromList, subqueryList, aggregateVector);
141
142         //There should be more than one argument
143
if (argumentsList.size() < 2)
144             throw StandardException.newException(SQLState.LANG_DB2_NUMBER_OF_ARGS_INVALID, functionName);
145
146         //check if all the arguments are parameters. If yes, then throw an exception
147
if (argumentsList.containsAllParameterNodes())
148             throw StandardException.newException(SQLState.LANG_DB2_COALESCE_FUNCTION_ALL_PARAMS);
149
150         int argumentsListSize = argumentsList.size();
151         //find the first non-param argument. The generated method will generate code to call coalesce on this argument
152
for (int index = 0; index < argumentsListSize; index++)
153         {
154             if (!(((ValueNode) argumentsList.elementAt(index)).requiresTypeFromContext()))
155             {
156                 firstNonParameterNode = (ValueNode) argumentsList.elementAt(index);
157                 break;
158             }
159         }
160
161         //make sure these arguments are compatible to each other before coalesce can be allowed
162
for (int index = 0; index < argumentsListSize; index++)
163         {
164             if (((ValueNode) argumentsList.elementAt(index)).requiresTypeFromContext()) //since we don't know the type of param, can't check for compatibility
165
continue;
166                 argumentsList.compatible((ValueNode) argumentsList.elementAt(index));
167         }
168
169         //set the result type to the most dominant datatype in the arguments list and based on the table listed above
170
setType(argumentsList.getDominantTypeServices());
171
172         //set all the parameter types to the type of the result type
173
for (int index = 0; index < argumentsListSize; index++)
174         {
175             if (((ValueNode) argumentsList.elementAt(index)).requiresTypeFromContext())
176             {
177                 ((ValueNode)argumentsList.elementAt(index)).setType(getTypeServices());
178                 break;
179             }
180         }
181         return this;
182     }
183
184     /**
185      * Do code generation for coalese/value
186      *
187      * @param acb The ExpressionClassBuilder for the class we're generating
188      * @param mb The method the expression will go into
189      *
190      * @exception StandardException Thrown on error
191      */

192
193     public void generateExpression(ExpressionClassBuilder acb,
194                                             MethodBuilder mb)
195                                     throws StandardException
196     {
197         int argumentsListSize = argumentsList.size();
198         String JavaDoc receiverType = ClassName.DataValueDescriptor;
199         String JavaDoc argumentsListInterfaceType = ClassName.DataValueDescriptor + "[]";
200
201         // Generate the code to build the array
202
LocalField arrayField =
203             acb.newFieldDeclaration(Modifier.PRIVATE, argumentsListInterfaceType);
204
205         /* The array gets created in the constructor.
206          * All constant elements in the array are initialized
207          * in the constructor.
208          */

209         /* Assign the initializer to the DataValueDescriptor[] field */
210         MethodBuilder cb = acb.getConstructor();
211         cb.pushNewArray(ClassName.DataValueDescriptor, argumentsListSize);
212         cb.setField(arrayField);
213
214         /* Set the array elements that are constant */
215         int numConstants = 0;
216         MethodBuilder nonConstantMethod = null;
217         MethodBuilder currentConstMethod = cb;
218         for (int index = 0; index < argumentsListSize; index++)
219         {
220             MethodBuilder setArrayMethod;
221     
222             if (argumentsList.elementAt(index) instanceof ConstantNode)
223             {
224                 numConstants++;
225         
226                 /*if too many statements are added to a method,
227                 *size of method can hit 65k limit, which will
228                 *lead to the class format errors at load time.
229                 *To avoid this problem, when number of statements added
230                 *to a method is > 2048, remaing statements are added to a new function
231                 *and called from the function which created the function.
232                 *See Beetle 5135 or 4293 for further details on this type of problem.
233                 */

234                 if(currentConstMethod.statementNumHitLimit(1))
235                 {
236                     MethodBuilder genConstantMethod = acb.newGeneratedFun("void", Modifier.PRIVATE);
237                     currentConstMethod.pushThis();
238                     currentConstMethod.callMethod(VMOpcode.INVOKEVIRTUAL,
239                                                   (String JavaDoc) null,
240                                                   genConstantMethod.getName(),
241                                                   "void", 0);
242                     //if it is a generate function, close the metod.
243
if(currentConstMethod != cb){
244                         currentConstMethod.methodReturn();
245                         currentConstMethod.complete();
246                     }
247                     currentConstMethod = genConstantMethod;
248                 }
249                 setArrayMethod = currentConstMethod;
250             } else {
251                 if (nonConstantMethod == null)
252                     nonConstantMethod = acb.newGeneratedFun("void", Modifier.PROTECTED);
253                 setArrayMethod = nonConstantMethod;
254
255             }
256
257             setArrayMethod.getField(arrayField);
258             ((ValueNode) argumentsList.elementAt(index)).generateExpression(acb, setArrayMethod);
259             setArrayMethod.upCast(receiverType);
260             setArrayMethod.setArrayElement(index);
261         }
262
263         //if a generated function was created to reduce the size of the methods close the functions.
264
if(currentConstMethod != cb){
265             currentConstMethod.methodReturn();
266             currentConstMethod.complete();
267         }
268
269         if (nonConstantMethod != null) {
270             nonConstantMethod.methodReturn();
271             nonConstantMethod.complete();
272             mb.pushThis();
273             mb.callMethod(VMOpcode.INVOKEVIRTUAL, (String JavaDoc) null, nonConstantMethod.getName(), "void", 0);
274         }
275
276         /*
277         ** Call the method for coalesce/value function.
278         ** First generate following
279         ** <first non-param argument in the list>.method(<all the arguments>, <resultType>)
280         ** Next, if we are dealing with result type that is variable length, then generate a call to setWidth.
281         */

282
283         firstNonParameterNode.generateExpression(acb, mb); //coalesce will be called on this non-parameter argument
284
mb.upCast(ClassName.DataValueDescriptor);
285
286         mb.getField(arrayField); // first arg to the coalesce function
287

288         //Following is for the second arg. This arg will be used to pass the return value.
289
//COALESCE method expects this to be initialized to NULL SQLxxx type object.
290
LocalField field = acb.newFieldDeclaration(Modifier.PRIVATE, receiverType);
291         acb.generateNull(mb, getTypeCompiler());
292         mb.upCast(ClassName.DataValueDescriptor);
293         mb.putField(field);
294
295         mb.callMethod(VMOpcode.INVOKEINTERFACE, receiverType, "coalesce", receiverType, 2);
296         if (getTypeId().variableLength())//since result type is variable length, generate setWidth code.
297
{
298             boolean isNumber = getTypeId().isNumericTypeId();
299             // to leave the DataValueDescriptor value on the stack, since setWidth is void
300
mb.dup();
301
302             mb.push(isNumber ? getTypeServices().getPrecision() : getTypeServices().getMaximumWidth());
303             mb.push(getTypeServices().getScale());
304             mb.push(true);
305             mb.callMethod(VMOpcode.INVOKEINTERFACE, ClassName.VariableSizeDataValue, "setWidth", "void", 3);
306         }
307     }
308
309     /*
310         print the non-node subfields
311      */

312     public String JavaDoc toString()
313     {
314         if (SanityManager.DEBUG)
315         {
316             return super.toString()+functionName+"("+argumentsList+")\n";
317         }
318         else
319         {
320             return "";
321         }
322     }
323         
324     /**
325      * {@inheritDoc}
326      */

327     protected boolean isEquivalent(ValueNode o) throws StandardException
328     {
329         if (!isSameNodeType(o))
330         {
331             return false;
332         }
333         
334         CoalesceFunctionNode other = (CoalesceFunctionNode)o;
335         if (other.argumentsList.size() != argumentsList.size())
336         {
337             return false;
338             
339         }
340         
341         int size = argumentsList.size();
342         for (int index = 0; index < size; index++)
343         {
344             ValueNode v1 = (ValueNode)argumentsList.elementAt(index);
345             ValueNode v2 = (ValueNode)other.argumentsList.elementAt(index);
346             if (!v1.isEquivalent(v2))
347             {
348                 return false;
349             }
350         }
351         return true;
352     }
353     public Visitable accept(Visitor v) throws StandardException
354     {
355         Visitable returnNode = v.visit(this);
356         
357         if (v.skipChildren(this) || v.stopTraversal())
358         {
359             return returnNode;
360         }
361         
362         int size = argumentsList.size();
363         for (int index = 0; index < size; index++)
364         {
365             argumentsList.setElementAt(
366                     (QueryTreeNode)(argumentsList.elementAt(index)).accept(v), index);
367         }
368         return returnNode;
369     }
370     /**
371      * Preprocess an expression tree. We do a number of transformations
372      * here (including subqueries, IN lists, LIKE and BETWEEN) plus
373      * subquery flattening.
374      * NOTE: This is done before the outer ResultSetNode is preprocessed.
375      *
376      * @param numTables Number of tables in the DML Statement
377      * @param outerFromList FromList from outer query block
378      * @param outerSubqueryList SubqueryList from outer query block
379      * @param outerPredicateList PredicateList from outer query block
380      *
381      * @return The modified expression
382      *
383      * @exception StandardException Thrown on error
384      */

385     public ValueNode preprocess(int numTables,
386                                 FromList outerFromList,
387                                 SubqueryList outerSubqueryList,
388                                 PredicateList outerPredicateList)
389                     throws StandardException
390     {
391         int argumentsListSize = argumentsList.size();
392         for (int i=0; i < argumentsListSize; i++) {
393             ((ValueNode)argumentsList.elementAt(i)).preprocess
394                 (numTables,
395                  outerFromList,
396                  outerSubqueryList,
397                  outerPredicateList);
398         }
399         return this;
400     }
401
402
403     /**
404      * Prints the sub-nodes of this object. See QueryTreeNode.java for
405      * how tree printing is supposed to work.
406      *
407      * @param depth The depth of this node in the tree
408      */

409
410     public void printSubNodes(int depth)
411     {
412         if (SanityManager.DEBUG)
413         {
414             super.printSubNodes(depth);
415             printLabel(depth, "argumentsList: ");
416             int argumentsListSize = argumentsList.size();
417             for (int i=0; i < argumentsListSize; i++) {
418                 ((ValueNode)argumentsList.elementAt(i)).treePrint(depth+1);
419             }
420         }
421     }
422         
423 }
424
Popular Tags