--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.sql.compile.CoalesceFunctionNode\r
+\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to you under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+\r
+ */\r
+\r
+package org.apache.derby.impl.sql.compile;\r
+\r
+import org.apache.derby.iapi.reference.ClassName;\r
+import org.apache.derby.iapi.reference.SQLState;\r
+\r
+import org.apache.derby.iapi.services.classfile.VMOpcode;\r
+\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+\r
+import org.apache.derby.impl.sql.compile.ExpressionClassBuilder;\r
+\r
+import org.apache.derby.iapi.services.compiler.LocalField;\r
+import org.apache.derby.iapi.services.compiler.MethodBuilder;\r
+import org.apache.derby.iapi.sql.compile.Visitable;\r
+import org.apache.derby.iapi.sql.compile.Visitor;\r
+\r
+import java.lang.reflect.Modifier;\r
+\r
+import java.util.Vector;\r
+\r
+/**\r
+ * This node represents coalesce/value function which returns the first argument that is not null.\r
+ * The arguments are evaluated in the order in which they are specified, and the result of the\r
+ * function is the first argument that is not null. The result can be null only if all the arguments\r
+ * can be null. The selected argument is converted, if necessary, to the attributes of the result.\r
+ *\r
+ *\r
+ * SQL Reference Guide for DB2 has section titled "Rules for result data types" at the following url\r
+ * http://publib.boulder.ibm.com/infocenter/db2help/index.jsp?topic=/com.ibm.db2.udb.doc/admin/r0008480.htm\r
+\r
+ * I have constructed following table based on various tables and information under "Rules for result data types"\r
+ * This table has FOR BIT DATA TYPES broken out into separate columns for clarity\r
+ *\r
+ * Note that are few differences between Derby and DB2\r
+ * 1)there are few differences between what datatypes are consdiered compatible\r
+ * In DB2, CHAR FOR BIT DATA datatypes are compatible with CHAR datatypes\r
+ * ie in addition to following table, CHAR is compatible with CHAR FOR BIT DATA, VARCHAR FOR BIT DATA and LONG VARCHAR FOR BIT DATA\r
+ * ie in addition to following table, VARCHAR is compatible with CHAR FOR BIT DATA, VARCHAR FOR BIT DATA and LONG VARCHAR FOR BIT DATA\r
+ * 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\r
+ * ie in addition to following table, CHAR FOR BIT DATA is compatible with DATE, TIME, TIMESTAMP\r
+ * ie in addition to following table, VARCHAR FOR BIT DATA is compatible with DATE, TIME, TIMESTAMP\r
+ *\r
+ * 2)few datatypes donot have matching precision in Derby and DB2\r
+ * In DB2, precision of TIME is 8. In Derby, precision of TIME is 0.\r
+ * In DB2, precision,scale of TIMESTAMP is 26,6. In Derby, precision of TIMESTAMP is 0,0.\r
+ * In DB2, precision of DOUBLE is 15. In Derby, precision of DOUBLE is 52.\r
+ * In DB2, precision of REAL is 23. In Derby, precision of REAL is 7.\r
+ * In DB2, precision calculation equation is incorrect when we have int and decimal arguments.\r
+ * The equation should be p=x+max(w-x,10) since precision of integer is 10 in both DB2 and Derby. Instead, DB2 has p=x+max(w-x,11) \r
+ *\r
+ * Types. S I B D R D C V L C V L C D T T B\r
+ * M N I E E O H A O H A O L A I I L\r
+ * A T G C A U A R N A R N O T M M O\r
+ * L E I I L B R C G R C G B E E E B\r
+ * L G N M L H V . H V S\r
+ * I E T A E A A B A A T\r
+ * N R L R R I R R A\r
+ * T C T . . M\r
+ * H B B P\r
+ * A I I\r
+ * R T T\r
+ * SMALLINT { "SMALLINT", "INTEGER", "BIGINT", "DECIMAL", "DOUBLE", "DOUBLE", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR" },\r
+ * INTEGER { "INTEGER", "INTEGER", "BIGINT", "DECIMAL", "DOUBLE", "DOUBLE", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR" },\r
+ * BIGINT { "BIGINT", "BIGINT", "BIGINT", "DECIMAL", "DOUBLE", "DOUBLE", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR" },\r
+ * DECIMAL { "DECIMAL", "DECIMAL", "DECIMAL", "DECIMAL", "DOUBLE", "DOUBLE", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR" },\r
+ * REAL { "DOUBLE", "DOUBLE", "DOUBLE", "DOUBLE", "REAL", "DOUBLE", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR" },\r
+ * DOUBLE { "DOUBLE", "DOUBLE", "DOUBLE", "DOUBLE", "DOUBLE", "DOUBLE", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR" },\r
+ * CHAR { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "CHAR", "VARCHAR", "LONG VARCHAR", "ERROR", "ERROR", "ERROR", "CLOB", "DATE", "TIME", "TIMESTAMP", "ERROR" },\r
+ * VARCHAR { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "VARCHAR", "VARCHAR","LONG VARCHAR", "ERROR", "ERROR", "ERROR", "CLOB", "DATE", "TIME", "TIMESTAMP", "ERROR" },\r
+ * LONGVARCHAR { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "LONG VARCHAR", "LONG VARCHAR", "LONG VARCHAR", "ERROR", "ERROR", "ERROR", "CLOB", "ERROR", "ERROR", "ERROR", "ERROR" },\r
+ * CHAR FOR BIT { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "BIT", "BIT VARYING", "LONG BIT VARYING", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR" },\r
+ * VARCH. BIT { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "BIT VARYING", "BIT VARYING", "LONG BIT VARYING", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR" },\r
+ * 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" },\r
+ * CLOB { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "CLOB", "CLOB", "CLOB", "ERROR", "ERROR", "ERROR", "CLOB", "ERROR", "ERROR", "ERROR", "ERROR" },\r
+ * DATE { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "DATE", "DATE", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "DATE", "ERROR", "ERROR", "ERROR" },\r
+ * TIME { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "TIME", "TIME", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "TIME", "ERROR", "ERROR" },\r
+ * TIMESTAMP { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "TIMESTAMP", "TIMESTAMP", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "TIMESTAMP", "ERROR" },\r
+ * BLOB { "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "ERROR", "BLOB" }\r
+ */\r
+\r
+public class CoalesceFunctionNode extends ValueNode\r
+{\r
+ String functionName; //Are we here because of COALESCE function or VALUE function\r
+ ValueNodeList argumentsList; //this is the list of arguments to the function. We are interested in the first not-null argument\r
+\r
+ /**\r
+ * The generated method will generate code to call coalesce on\r
+ * this non-parameter argument.\r
+ */\r
+ private int firstNonParameterNodeIdx = -1;\r
+\r
+ /**\r
+ * Initializer for a CalesceFunctionNode\r
+ *\r
+ * @param functionName Tells if the function was called with name COALESCE or with name VALUE\r
+ * @param argumentsList The list of arguments to the coalesce/value function\r
+ */\r
+ public void init(Object functionName, Object argumentsList)\r
+ {\r
+ this.functionName = (String) functionName;\r
+ this.argumentsList = (ValueNodeList) argumentsList;\r
+ }\r
+\r
+ /**\r
+ * Binding this expression means setting the result DataTypeServices.\r
+ * In this case, the result type is based on the rules in the table listed earlier.\r
+ *\r
+ * @param fromList The FROM list for the statement.\r
+ * @param subqueryList The subquery list being built as we find SubqueryNodes.\r
+ * @param aggregateVector The aggregate vector being built as we find AggregateNodes.\r
+ *\r
+ * @return The new top of the expression tree.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public ValueNode bindExpression(FromList fromList, SubqueryList subqueryList,\r
+ Vector aggregateVector)\r
+ throws StandardException\r
+ {\r
+ //bind all the arguments\r
+ argumentsList.bindExpression(fromList, subqueryList, aggregateVector);\r
+\r
+ //There should be more than one argument\r
+ if (argumentsList.size() < 2)\r
+ throw StandardException.newException(SQLState.LANG_DB2_NUMBER_OF_ARGS_INVALID, functionName);\r
+\r
+ //check if all the arguments are parameters. If yes, then throw an exception\r
+ if (argumentsList.containsAllParameterNodes())\r
+ throw StandardException.newException(SQLState.LANG_DB2_COALESCE_FUNCTION_ALL_PARAMS);\r
+\r
+ int argumentsListSize = argumentsList.size();\r
+ //find the first non-param argument. The generated method will generate code to call coalesce on this argument\r
+ for (int index = 0; index < argumentsListSize; index++)\r
+ {\r
+ if (!(((ValueNode) argumentsList.elementAt(index)).requiresTypeFromContext()))\r
+ {\r
+ firstNonParameterNodeIdx = index;\r
+ break;\r
+ }\r
+ }\r
+\r
+ //make sure these arguments are compatible to each other before coalesce can be allowed\r
+ for (int index = 0; index < argumentsListSize; index++)\r
+ {\r
+ if (((ValueNode) argumentsList.elementAt(index)).requiresTypeFromContext()) //since we don't know the type of param, can't check for compatibility\r
+ continue;\r
+ argumentsList.compatible((ValueNode) argumentsList.elementAt(index));\r
+ }\r
+\r
+ //set the result type to the most dominant datatype in the arguments list and based on the table listed above\r
+ setType(argumentsList.getDominantTypeServices());\r
+\r
+ //set all the parameter types to the type of the result type\r
+ for (int index = 0; index < argumentsListSize; index++)\r
+ {\r
+ if (((ValueNode) argumentsList.elementAt(index)).requiresTypeFromContext())\r
+ {\r
+ ((ValueNode)argumentsList.elementAt(index)).setType(getTypeServices());\r
+ break;\r
+ }\r
+ }\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Do code generation for coalese/value\r
+ *\r
+ * @param acb The ExpressionClassBuilder for the class we're generating\r
+ * @param mb The method the expression will go into\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+\r
+ public void generateExpression(ExpressionClassBuilder acb,\r
+ MethodBuilder mb)\r
+ throws StandardException\r
+ {\r
+ int argumentsListSize = argumentsList.size();\r
+ String receiverType = ClassName.DataValueDescriptor;\r
+ String argumentsListInterfaceType = ClassName.DataValueDescriptor + "[]";\r
+\r
+ // Generate the code to build the array\r
+ LocalField arrayField =\r
+ acb.newFieldDeclaration(Modifier.PRIVATE, argumentsListInterfaceType);\r
+\r
+ /* The array gets created in the constructor.\r
+ * All constant elements in the array are initialized\r
+ * in the constructor. \r
+ */\r
+ /* Assign the initializer to the DataValueDescriptor[] field */\r
+ MethodBuilder cb = acb.getConstructor();\r
+ cb.pushNewArray(ClassName.DataValueDescriptor, argumentsListSize);\r
+ cb.setField(arrayField);\r
+\r
+ /* Set the array elements that are constant */\r
+ int numConstants = 0;\r
+ MethodBuilder nonConstantMethod = null;\r
+ MethodBuilder currentConstMethod = cb;\r
+ for (int index = 0; index < argumentsListSize; index++)\r
+ {\r
+ MethodBuilder setArrayMethod;\r
+ \r
+ if (argumentsList.elementAt(index) instanceof ConstantNode)\r
+ {\r
+ numConstants++;\r
+ \r
+ /*if too many statements are added to a method, \r
+ *size of method can hit 65k limit, which will\r
+ *lead to the class format errors at load time.\r
+ *To avoid this problem, when number of statements added \r
+ *to a method is > 2048, remaing statements are added to a new function\r
+ *and called from the function which created the function.\r
+ *See Beetle 5135 or 4293 for further details on this type of problem.\r
+ */\r
+ if(currentConstMethod.statementNumHitLimit(1))\r
+ {\r
+ MethodBuilder genConstantMethod = acb.newGeneratedFun("void", Modifier.PRIVATE);\r
+ currentConstMethod.pushThis();\r
+ currentConstMethod.callMethod(VMOpcode.INVOKEVIRTUAL,\r
+ (String) null, \r
+ genConstantMethod.getName(),\r
+ "void", 0);\r
+ //if it is a generate function, close the metod.\r
+ if(currentConstMethod != cb){\r
+ currentConstMethod.methodReturn();\r
+ currentConstMethod.complete();\r
+ }\r
+ currentConstMethod = genConstantMethod;\r
+ }\r
+ setArrayMethod = currentConstMethod;\r
+ } else {\r
+ if (nonConstantMethod == null)\r
+ nonConstantMethod = acb.newGeneratedFun("void", Modifier.PROTECTED);\r
+ setArrayMethod = nonConstantMethod;\r
+\r
+ }\r
+\r
+ setArrayMethod.getField(arrayField); \r
+ ((ValueNode) argumentsList.elementAt(index)).generateExpression(acb, setArrayMethod);\r
+ setArrayMethod.upCast(receiverType);\r
+ setArrayMethod.setArrayElement(index);\r
+ }\r
+\r
+ //if a generated function was created to reduce the size of the methods close the functions.\r
+ if(currentConstMethod != cb){\r
+ currentConstMethod.methodReturn();\r
+ currentConstMethod.complete();\r
+ }\r
+\r
+ if (nonConstantMethod != null) {\r
+ nonConstantMethod.methodReturn();\r
+ nonConstantMethod.complete();\r
+ mb.pushThis();\r
+ mb.callMethod(VMOpcode.INVOKEVIRTUAL, (String) null, nonConstantMethod.getName(), "void", 0);\r
+ }\r
+\r
+ /*\r
+ ** Call the method for coalesce/value function.\r
+ ** First generate following\r
+ ** <first non-param argument in the list>.method(<all the arguments>, <resultType>)\r
+ ** Next, if we are dealing with result type that is variable length, then generate a call to setWidth.\r
+ */\r
+\r
+ // coalesce will be called on this non-parameter argument\r
+ ((ValueNode) argumentsList.elementAt(firstNonParameterNodeIdx)).\r
+ generateExpression(acb, mb);\r
+\r
+ mb.upCast(ClassName.DataValueDescriptor);\r
+\r
+ mb.getField(arrayField); // first arg to the coalesce function\r
+\r
+ //Following is for the second arg. This arg will be used to pass the return value.\r
+ //COALESCE method expects this to be initialized to NULL SQLxxx type object.\r
+ LocalField field = acb.newFieldDeclaration(Modifier.PRIVATE, receiverType);\r
+ acb.generateNull(mb, getTypeCompiler(), getTypeServices().getCollationType());\r
+ mb.upCast(ClassName.DataValueDescriptor);\r
+ mb.putField(field);\r
+\r
+ mb.callMethod(VMOpcode.INVOKEINTERFACE, receiverType, "coalesce", receiverType, 2);\r
+ if (getTypeId().variableLength())//since result type is variable length, generate setWidth code.\r
+ {\r
+ boolean isNumber = getTypeId().isNumericTypeId();\r
+ // to leave the DataValueDescriptor value on the stack, since setWidth is void\r
+ mb.dup();\r
+\r
+ mb.push(isNumber ? getTypeServices().getPrecision() : getTypeServices().getMaximumWidth());\r
+ mb.push(getTypeServices().getScale());\r
+ mb.push(true);\r
+ mb.callMethod(VMOpcode.INVOKEINTERFACE, ClassName.VariableSizeDataValue, "setWidth", "void", 3);\r
+ }\r
+ }\r
+\r
+ /*\r
+ print the non-node subfields\r
+ */\r
+ public String toString() \r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ return super.toString()+functionName+"("+argumentsList+")\n";\r
+ }\r
+ else\r
+ {\r
+ return "";\r
+ }\r
+ }\r
+ \r
+ /**\r
+ * {@inheritDoc}\r
+ */\r
+ protected boolean isEquivalent(ValueNode o) throws StandardException\r
+ {\r
+ if (!isSameNodeType(o))\r
+ {\r
+ return false;\r
+ }\r
+ \r
+ CoalesceFunctionNode other = (CoalesceFunctionNode)o;\r
+ if (other.argumentsList.size() != argumentsList.size())\r
+ {\r
+ return false;\r
+ \r
+ }\r
+ \r
+ int size = argumentsList.size();\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ValueNode v1 = (ValueNode)argumentsList.elementAt(index);\r
+ ValueNode v2 = (ValueNode)other.argumentsList.elementAt(index);\r
+ if (!v1.isEquivalent(v2)) \r
+ {\r
+ return false;\r
+ }\r
+ }\r
+ return true;\r
+ }\r
+ public Visitable accept(Visitor v) throws StandardException \r
+ {\r
+ Visitable returnNode = v.visit(this);\r
+ \r
+ if (v.skipChildren(this) || v.stopTraversal())\r
+ {\r
+ return returnNode;\r
+ }\r
+ \r
+ int size = argumentsList.size();\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ argumentsList.setElementAt(\r
+ (QueryTreeNode)(argumentsList.elementAt(index)).accept(v), index);\r
+ }\r
+ return returnNode;\r
+ }\r
+ /**\r
+ * Preprocess an expression tree. We do a number of transformations\r
+ * here (including subqueries, IN lists, LIKE and BETWEEN) plus\r
+ * subquery flattening.\r
+ * NOTE: This is done before the outer ResultSetNode is preprocessed.\r
+ *\r
+ * @param numTables Number of tables in the DML Statement\r
+ * @param outerFromList FromList from outer query block\r
+ * @param outerSubqueryList SubqueryList from outer query block\r
+ * @param outerPredicateList PredicateList from outer query block\r
+ *\r
+ * @return The modified expression\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public ValueNode preprocess(int numTables,\r
+ FromList outerFromList,\r
+ SubqueryList outerSubqueryList,\r
+ PredicateList outerPredicateList) \r
+ throws StandardException\r
+ {\r
+ int argumentsListSize = argumentsList.size();\r
+ for (int i=0; i < argumentsListSize; i++) {\r
+ ((ValueNode)argumentsList.elementAt(i)).preprocess\r
+ (numTables,\r
+ outerFromList,\r
+ outerSubqueryList,\r
+ outerPredicateList);\r
+ }\r
+ return this;\r
+ }\r
+\r
+\r
+ /**\r
+ * Prints the sub-nodes of this object. See QueryTreeNode.java for\r
+ * how tree printing is supposed to work.\r
+ *\r
+ * @param depth The depth of this node in the tree\r
+ */\r
+\r
+ public void printSubNodes(int depth)\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ super.printSubNodes(depth);\r
+ printLabel(depth, "argumentsList: [firstNonParameterNodeIdx=" +\r
+ firstNonParameterNodeIdx + "]" );\r
+ int argumentsListSize = argumentsList.size();\r
+ for (int i=0; i < argumentsListSize; i++) {\r
+ ((ValueNode)argumentsList.elementAt(i)).treePrint(depth+1);\r
+ }\r
+ }\r
+ }\r
+ \r
+}\r