001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.compile.CoalesceFunctionNode
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.impl.sql.compile;
023:
024: import org.apache.derby.iapi.reference.ClassName;
025: import org.apache.derby.iapi.reference.SQLState;
026:
027: import org.apache.derby.iapi.services.classfile.VMOpcode;
028:
029: import org.apache.derby.iapi.services.sanity.SanityManager;
030:
031: import org.apache.derby.iapi.error.StandardException;
032:
033: import org.apache.derby.impl.sql.compile.ExpressionClassBuilder;
034:
035: import org.apache.derby.iapi.services.compiler.LocalField;
036: import org.apache.derby.iapi.services.compiler.MethodBuilder;
037: import org.apache.derby.iapi.sql.compile.Visitable;
038: import org.apache.derby.iapi.sql.compile.Visitor;
039:
040: import java.lang.reflect.Modifier;
041:
042: import java.util.Iterator;
043: import java.util.Vector;
044:
045: /**
046: * This node represents coalesce/value function which returns the first argument that is not null.
047: * The arguments are evaluated in the order in which they are specified, and the result of the
048: * function is the first argument that is not null. The result can be null only if all the arguments
049: * can be null. The selected argument is converted, if necessary, to the attributes of the result.
050: *
051: *
052: * SQL Reference Guide for DB2 has section titled "Rules for result data types" at the following url
053: * http://publib.boulder.ibm.com/infocenter/db2help/index.jsp?topic=/com.ibm.db2.udb.doc/admin/r0008480.htm
054:
055: * I have constructed following table based on various tables and information under "Rules for result data types"
056: * This table has FOR BIT DATA TYPES broken out into separate columns for clarity
057: *
058: * Note that are few differences between Cloudscape and DB2
059: * 1)there are few differences between what datatypes are consdiered compatible
060: * In DB2, CHAR FOR BIT DATA datatypes are compatible with CHAR datatypes
061: * ie in addition to following table, CHAR is compatible with CHAR FOR BIT DATA, VARCHAR FOR BIT DATA and LONG VARCHAR FOR BIT DATA
062: * ie in addition to following table, VARCHAR is compatible with CHAR FOR BIT DATA, VARCHAR FOR BIT DATA and LONG VARCHAR FOR BIT DATA
063: * 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
064: * ie in addition to following table, CHAR FOR BIT DATA is compatible with DATE, TIME, TIMESTAMP
065: * ie in addition to following table, VARCHAR FOR BIT DATA is compatible with DATE, TIME, TIMESTAMP
066: *
067: * 2)few datatypes donot have matching precision in Cloudscape and DB2
068: * In DB2, precision of TIME is 8. In Cloudscape, precision of TIME is 0.
069: * In DB2, precision,scale of TIMESTAMP is 26,6. In Cloudscape, precision of TIMESTAMP is 0,0.
070: * In DB2, precision of DOUBLE is 15. In Cloudscape, precision of DOUBLE is 52.
071: * In DB2, precision of REAL is 23. In Cloudscape, precision of REAL is 7.
072: * In DB2, precision calculation equation is incorrect when we have int and decimal arguments.
073: * 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)
074: *
075: * Types. S I B D R D C V L C V L C D T T B
076: * M N I E E O H A O H A O L A I I L
077: * A T G C A U A R N A R N O T M M O
078: * L E I I L B R C G R C G B E E E B
079: * L G N M L H V . H V S
080: * I E T A E A A B A A T
081: * N R L R R I R R A
082: * T C T . . M
083: * H B B P
084: * A I I
085: * R T T
086: * SMALLINT { "SMALLINT", "INTEGER", "BIGINT", "DECIMAL", "DOUBLE", "DOUBLE", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR" },
087: * INTEGER { "INTEGER", "INTEGER", "BIGINT", "DECIMAL", "DOUBLE", "DOUBLE", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR" },
088: * BIGINT { "BIGINT", "BIGINT", "BIGINT", "DECIMAL", "DOUBLE", "DOUBLE", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR" },
089: * DECIMAL { "DECIMAL", "DECIMAL", "DECIMAL", "DECIMAL", "DOUBLE", "DOUBLE", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR" },
090: * REAL { "DOUBLE", "DOUBLE", "DOUBLE", "DOUBLE", "REAL", "DOUBLE", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR" },
091: * DOUBLE { "DOUBLE", "DOUBLE", "DOUBLE", "DOUBLE", "DOUBLE", "DOUBLE", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR" },
092: * CHAR { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "CHAR", "VARCHAR", "LONG VARCHAR", "ERROR", "ERROR", "ERROR", "CLOB", "DATE", "TIME", "TIMESTAMP", "ERROR" },
093: * VARCHAR { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "VARCHAR", "VARCHAR","LONG VARCHAR", "ERROR", "ERROR", "ERROR", "CLOB", "DATE", "TIME", "TIMESTAMP", "ERROR" },
094: * LONGVARCHAR { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "LONG VARCHAR", "LONG VARCHAR", "LONG VARCHAR", "ERROR", "ERROR", "ERROR", "CLOB", "ERROR", "ERROR", "ERROR", "ERROR" },
095: * CHAR FOR BIT { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "BIT", "BIT VARYING", "LONG BIT VARYING", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR" },
096: * VARCH. BIT { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "BIT VARYING", "BIT VARYING", "LONG BIT VARYING", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR" },
097: * 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" },
098: * CLOB { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "CLOB", "CLOB", "CLOB", "ERROR", "ERROR", "ERROR", "CLOB", "ERROR", "ERROR", "ERROR", "ERROR" },
099: * 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: String functionName; //Are we here because of COALESCE function or VALUE function
107: ValueNodeList argumentsList; //this is the list of arguments to the function. We are interested in the first not-null argument
108: ValueNode firstNonParameterNode;//The generated method will generate code to call coalesce on this non-parameter argument
109:
110: /**
111: * Initializer for a CalesceFunctionNode
112: *
113: * @param functionName Tells if the function was called with name COALESCE or with name VALUE
114: * @param argumentsList The list of arguments to the coalesce/value function
115: */
116: public void init(Object functionName, Object argumentsList) {
117: this .functionName = (String) functionName;
118: this .argumentsList = (ValueNodeList) argumentsList;
119: }
120:
121: /**
122: * Binding this expression means setting the result DataTypeServices.
123: * In this case, the result type is based on the rules in the table listed earlier.
124: *
125: * @param fromList The FROM list for the statement.
126: * @param subqueryList The subquery list being built as we find SubqueryNodes.
127: * @param aggregateVector The aggregate vector being built as we find AggregateNodes.
128: *
129: * @return The new top of the expression tree.
130: *
131: * @exception StandardException Thrown on error
132: */
133: public ValueNode bindExpression(FromList fromList,
134: SubqueryList subqueryList, Vector aggregateVector)
135: throws StandardException {
136: //bind all the arguments
137: argumentsList.bindExpression(fromList, subqueryList,
138: aggregateVector);
139:
140: //There should be more than one argument
141: if (argumentsList.size() < 2)
142: throw StandardException.newException(
143: SQLState.LANG_DB2_NUMBER_OF_ARGS_INVALID,
144: functionName);
145:
146: //check if all the arguments are parameters. If yes, then throw an exception
147: if (argumentsList.containsAllParameterNodes())
148: throw StandardException
149: .newException(SQLState.LANG_DB2_COALESCE_FUNCTION_ALL_PARAMS);
150:
151: int argumentsListSize = argumentsList.size();
152: //find the first non-param argument. The generated method will generate code to call coalesce on this argument
153: for (int index = 0; index < argumentsListSize; index++) {
154: if (!(((ValueNode) argumentsList.elementAt(index))
155: .requiresTypeFromContext())) {
156: firstNonParameterNode = (ValueNode) argumentsList
157: .elementAt(index);
158: break;
159: }
160: }
161:
162: //make sure these arguments are compatible to each other before coalesce can be allowed
163: for (int index = 0; index < argumentsListSize; index++) {
164: if (((ValueNode) argumentsList.elementAt(index))
165: .requiresTypeFromContext()) //since we don't know the type of param, can't check for compatibility
166: continue;
167: argumentsList.compatible((ValueNode) argumentsList
168: .elementAt(index));
169: }
170:
171: //set the result type to the most dominant datatype in the arguments list and based on the table listed above
172: setType(argumentsList.getDominantTypeServices());
173:
174: //set all the parameter types to the type of the result type
175: for (int index = 0; index < argumentsListSize; index++) {
176: if (((ValueNode) argumentsList.elementAt(index))
177: .requiresTypeFromContext()) {
178: ((ValueNode) argumentsList.elementAt(index))
179: .setType(getTypeServices());
180: break;
181: }
182: }
183: return this ;
184: }
185:
186: /**
187: * Do code generation for coalese/value
188: *
189: * @param acb The ExpressionClassBuilder for the class we're generating
190: * @param mb The method the expression will go into
191: *
192: * @exception StandardException Thrown on error
193: */
194:
195: public void generateExpression(ExpressionClassBuilder acb,
196: MethodBuilder mb) throws StandardException {
197: int argumentsListSize = argumentsList.size();
198: String receiverType = ClassName.DataValueDescriptor;
199: String argumentsListInterfaceType = ClassName.DataValueDescriptor
200: + "[]";
201:
202: // Generate the code to build the array
203: LocalField arrayField = acb.newFieldDeclaration(
204: Modifier.PRIVATE, argumentsListInterfaceType);
205:
206: /* The array gets created in the constructor.
207: * All constant elements in the array are initialized
208: * in the constructor.
209: */
210: /* Assign the initializer to the DataValueDescriptor[] field */
211: MethodBuilder cb = acb.getConstructor();
212: cb.pushNewArray(ClassName.DataValueDescriptor,
213: argumentsListSize);
214: cb.setField(arrayField);
215:
216: /* Set the array elements that are constant */
217: int numConstants = 0;
218: MethodBuilder nonConstantMethod = null;
219: MethodBuilder currentConstMethod = cb;
220: for (int index = 0; index < argumentsListSize; index++) {
221: MethodBuilder setArrayMethod;
222:
223: if (argumentsList.elementAt(index) instanceof ConstantNode) {
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: MethodBuilder genConstantMethod = acb
236: .newGeneratedFun("void", Modifier.PRIVATE);
237: currentConstMethod.pushThis();
238: currentConstMethod.callMethod(
239: VMOpcode.INVOKEVIRTUAL, (String) null,
240: genConstantMethod.getName(), "void", 0);
241: //if it is a generate function, close the metod.
242: if (currentConstMethod != cb) {
243: currentConstMethod.methodReturn();
244: currentConstMethod.complete();
245: }
246: currentConstMethod = genConstantMethod;
247: }
248: setArrayMethod = currentConstMethod;
249: } else {
250: if (nonConstantMethod == null)
251: nonConstantMethod = acb.newGeneratedFun("void",
252: Modifier.PROTECTED);
253: setArrayMethod = nonConstantMethod;
254:
255: }
256:
257: setArrayMethod.getField(arrayField);
258: ((ValueNode) argumentsList.elementAt(index))
259: .generateExpression(acb, setArrayMethod);
260: setArrayMethod.upCast(receiverType);
261: setArrayMethod.setArrayElement(index);
262: }
263:
264: //if a generated function was created to reduce the size of the methods close the functions.
265: if (currentConstMethod != cb) {
266: currentConstMethod.methodReturn();
267: currentConstMethod.complete();
268: }
269:
270: if (nonConstantMethod != null) {
271: nonConstantMethod.methodReturn();
272: nonConstantMethod.complete();
273: mb.pushThis();
274: mb.callMethod(VMOpcode.INVOKEVIRTUAL, (String) null,
275: nonConstantMethod.getName(), "void", 0);
276: }
277:
278: /*
279: ** Call the method for coalesce/value function.
280: ** First generate following
281: ** <first non-param argument in the list>.method(<all the arguments>, <resultType>)
282: ** Next, if we are dealing with result type that is variable length, then generate a call to setWidth.
283: */
284:
285: firstNonParameterNode.generateExpression(acb, mb); //coalesce will be called on this non-parameter argument
286: mb.upCast(ClassName.DataValueDescriptor);
287:
288: mb.getField(arrayField); // first arg to the coalesce function
289:
290: //Following is for the second arg. This arg will be used to pass the return value.
291: //COALESCE method expects this to be initialized to NULL SQLxxx type object.
292: LocalField field = acb.newFieldDeclaration(Modifier.PRIVATE,
293: receiverType);
294: acb.generateNull(mb, getTypeCompiler());
295: mb.upCast(ClassName.DataValueDescriptor);
296: mb.putField(field);
297:
298: mb.callMethod(VMOpcode.INVOKEINTERFACE, receiverType,
299: "coalesce", receiverType, 2);
300: if (getTypeId().variableLength())//since result type is variable length, generate setWidth code.
301: {
302: boolean isNumber = getTypeId().isNumericTypeId();
303: // to leave the DataValueDescriptor value on the stack, since setWidth is void
304: mb.dup();
305:
306: mb.push(isNumber ? getTypeServices().getPrecision()
307: : getTypeServices().getMaximumWidth());
308: mb.push(getTypeServices().getScale());
309: mb.push(true);
310: mb.callMethod(VMOpcode.INVOKEINTERFACE,
311: ClassName.VariableSizeDataValue, "setWidth",
312: "void", 3);
313: }
314: }
315:
316: /*
317: print the non-node subfields
318: */
319: public String toString() {
320: if (SanityManager.DEBUG) {
321: return super .toString() + functionName + "("
322: + argumentsList + ")\n";
323: } else {
324: return "";
325: }
326: }
327:
328: /**
329: * {@inheritDoc}
330: */
331: protected boolean isEquivalent(ValueNode o)
332: throws StandardException {
333: if (!isSameNodeType(o)) {
334: return false;
335: }
336:
337: CoalesceFunctionNode other = (CoalesceFunctionNode) o;
338: if (other.argumentsList.size() != argumentsList.size()) {
339: return false;
340:
341: }
342:
343: int size = argumentsList.size();
344: for (int index = 0; index < size; index++) {
345: ValueNode v1 = (ValueNode) argumentsList.elementAt(index);
346: ValueNode v2 = (ValueNode) other.argumentsList
347: .elementAt(index);
348: if (!v1.isEquivalent(v2)) {
349: return false;
350: }
351: }
352: return true;
353: }
354:
355: public Visitable accept(Visitor v) throws StandardException {
356: Visitable returnNode = v.visit(this );
357:
358: if (v.skipChildren(this ) || v.stopTraversal()) {
359: return returnNode;
360: }
361:
362: int size = argumentsList.size();
363: for (int index = 0; index < size; index++) {
364: argumentsList.setElementAt((QueryTreeNode) (argumentsList
365: .elementAt(index)).accept(v), index);
366: }
367: return returnNode;
368: }
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, FromList outerFromList,
386: SubqueryList outerSubqueryList,
387: PredicateList outerPredicateList) throws StandardException {
388: int argumentsListSize = argumentsList.size();
389: for (int i = 0; i < argumentsListSize; i++) {
390: ((ValueNode) argumentsList.elementAt(i)).preprocess(
391: numTables, outerFromList, outerSubqueryList,
392: outerPredicateList);
393: }
394: return this ;
395: }
396:
397: /**
398: * Prints the sub-nodes of this object. See QueryTreeNode.java for
399: * how tree printing is supposed to work.
400: *
401: * @param depth The depth of this node in the tree
402: */
403:
404: public void printSubNodes(int depth) {
405: if (SanityManager.DEBUG) {
406: super .printSubNodes(depth);
407: printLabel(depth, "argumentsList: ");
408: int argumentsListSize = argumentsList.size();
409: for (int i = 0; i < argumentsListSize; i++) {
410: ((ValueNode) argumentsList.elementAt(i))
411: .treePrint(depth + 1);
412: }
413: }
414: }
415:
416: }
|