--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.sql.compile.SQLToJavaValueNode\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.services.compiler.MethodBuilder;\r
+import org.apache.derby.iapi.services.compiler.LocalField;\r
+\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+\r
+import org.apache.derby.iapi.types.JSQLType;\r
+\r
+import org.apache.derby.iapi.types.DataValueDescriptor;\r
+import org.apache.derby.iapi.types.DataTypeDescriptor;\r
+import org.apache.derby.iapi.types.StringDataValue;\r
+\r
+import org.apache.derby.iapi.sql.compile.TypeCompiler;\r
+\r
+import org.apache.derby.iapi.sql.Activation;\r
+\r
+import org.apache.derby.iapi.sql.dictionary.DataDictionary;\r
+import org.apache.derby.iapi.error.StandardException;\r
+import org.apache.derby.impl.sql.compile.ExpressionClassBuilder;\r
+import org.apache.derby.iapi.sql.compile.Visitable;\r
+import org.apache.derby.iapi.sql.compile.Visitor;\r
+\r
+import org.apache.derby.iapi.reference.ClassName;\r
+\r
+import org.apache.derby.iapi.util.JBitSet;\r
+import org.apache.derby.iapi.services.classfile.VMOpcode;\r
+\r
+import java.lang.reflect.Modifier;\r
+\r
+import java.util.Vector;\r
+\r
+/**\r
+ * This node type converts a value in the SQL domain to a value in the Java\r
+ * domain.\r
+ */\r
+\r
+public class SQLToJavaValueNode extends JavaValueNode\r
+{\r
+ ValueNode value;\r
+\r
+ /**\r
+ * If set then this SQL value is being passed into a SQL function\r
+ * declared RETURNS NULL ON NULL input. In this case this node\r
+ * performs NULL checking logic in addition simple translation\r
+ * from the SQL domain to the Java domain. Thus if this\r
+ * is set then this node can not be removed when it\r
+ * is paired with a JavaToSQLValueNode.\r
+ * This field is set at generate time of the\r
+ * enclosing StaticMethodCallNode.\r
+ */\r
+ LocalField returnsNullOnNullState;\r
+\r
+ /**\r
+ * Constructor for a SQLToJavaValueNode\r
+ *\r
+ * @param value A ValueNode representing a SQL value to convert to\r
+ * the Java domain.\r
+ */\r
+\r
+ public void init(Object value)\r
+ {\r
+ this.value = (ValueNode) value;\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
+ int parm;\r
+\r
+ super.printSubNodes(depth);\r
+ if (value != null)\r
+ {\r
+ printLabel(depth, "value: ");\r
+ value.treePrint(depth + 1);\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Returns the name of the java class type that this node coerces to.\r
+ *\r
+ * @return name of java class type\r
+ *\r
+ */\r
+ public String getJavaTypeName()\r
+ throws StandardException\r
+ {\r
+ JSQLType myType = getJSQLType();\r
+\r
+ if ( myType == null ) { return ""; }\r
+ else { return mapToTypeID( myType ).getCorrespondingJavaTypeName(); }\r
+ }\r
+\r
+ /**\r
+ * Returns the name of the java primitive type that this node coerces to.\r
+ *\r
+ * @return name of java primitive type\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public String getPrimitiveTypeName()\r
+ throws StandardException\r
+ {\r
+ JSQLType myType = getJSQLType();\r
+\r
+ if ( myType == null )\r
+ {\r
+ return "";\r
+ }\r
+ else\r
+ {\r
+ return\r
+ getTypeCompiler(mapToTypeID( myType )).\r
+ getCorrespondingPrimitiveTypeName();\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Get the JSQLType that corresponds to this node. Could be a SQLTYPE,\r
+ * a Java primitive, or a Java class.\r
+ *\r
+ * Overrides method in JavaValueNode.\r
+ *\r
+ * @return the corresponding JSQLType\r
+ *\r
+ */\r
+ public JSQLType getJSQLType () throws StandardException\r
+ {\r
+ if ( jsqlType == null )\r
+ {\r
+ if ( value.requiresTypeFromContext()) \r
+ {\r
+ ParameterNode pn;\r
+ if (value instanceof UnaryOperatorNode) \r
+ pn = ((UnaryOperatorNode)value).getParameterOperand();\r
+ else\r
+ pn = (ParameterNode) (value);\r
+ jsqlType = pn.getJSQLType();\r
+ \r
+ }\r
+ else\r
+ {\r
+ DataTypeDescriptor dtd = value.getTypeServices();\r
+ if (dtd != null)\r
+ jsqlType = new JSQLType( dtd );\r
+ }\r
+ }\r
+\r
+ return jsqlType;\r
+ }\r
+\r
+\r
+ /**\r
+ * Bind this expression. This means binding the sub-expressions,\r
+ * as well as figuring out what the return type is for this expression.\r
+ *\r
+ * @param fromList The FROM list for the query this\r
+ * expression is in, for binding columns.\r
+ * @param subqueryList The subquery list being built as we find\r
+ * SubqueryNodes\r
+ * @param aggregateVector The aggregate vector being built as we find AggregateNodes\r
+ *\r
+ * @return this \r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+\r
+ public JavaValueNode bindExpression(\r
+ FromList fromList, SubqueryList subqueryList,\r
+ Vector aggregateVector) \r
+ throws StandardException\r
+ {\r
+ /* Bind the expression under us */\r
+ value = value.bindExpression(fromList, subqueryList,\r
+ aggregateVector);\r
+\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Remap all ColumnReferences in this tree to be clones of the\r
+ * underlying expression.\r
+ *\r
+ * @return JavaValueNode The remapped expression tree.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public JavaValueNode remapColumnReferencesToExpressions()\r
+ throws StandardException\r
+ {\r
+ value = value.remapColumnReferencesToExpressions();\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Categorize this predicate. Initially, this means\r
+ * building a bit map of the referenced tables for each predicate.\r
+ * If the source of this ColumnReference (at the next underlying level) \r
+ * is not a ColumnReference or a VirtualColumnNode then this predicate\r
+ * will not be pushed down.\r
+ *\r
+ * For example, in:\r
+ * select * from (select 1 from s) a (x) where x = 1\r
+ * we will not push down x = 1.\r
+ * NOTE: It would be easy to handle the case of a constant, but if the\r
+ * inner SELECT returns an arbitrary expression, then we would have to copy\r
+ * that tree into the pushed predicate, and that tree could contain\r
+ * subqueries and method calls.\r
+ * RESOLVE - revisit this issue once we have views.\r
+ *\r
+ * @param referencedTabs JBitSet with bit map of referenced FromTables\r
+ * @param simplePredsOnly Whether or not to consider method\r
+ * calls, field references and conditional nodes\r
+ * when building bit map\r
+ *\r
+ * @return boolean Whether or not source.expression is a ColumnReference\r
+ * or a VirtualColumnNode.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public boolean categorize(JBitSet referencedTabs, boolean simplePredsOnly)\r
+ throws StandardException\r
+ {\r
+ return value.categorize(referencedTabs, simplePredsOnly);\r
+ }\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
+ * @exception StandardException Thrown on error\r
+ */\r
+ public void preprocess(int numTables,\r
+ FromList outerFromList,\r
+ SubqueryList outerSubqueryList,\r
+ PredicateList outerPredicateList) \r
+ throws StandardException\r
+ {\r
+ value.preprocess(numTables,\r
+ outerFromList, outerSubqueryList,\r
+ outerPredicateList);\r
+ }\r
+\r
+ /**\r
+ * Return the variant type for the underlying expression.\r
+ * The variant type can be:\r
+ * VARIANT - variant within a scan\r
+ * (method calls and non-static field access)\r
+ * SCAN_INVARIANT - invariant within a scan\r
+ * (column references from outer tables)\r
+ * QUERY_INVARIANT - invariant within the life of a query\r
+ * (constant expressions)\r
+ *\r
+ * @return The variant type for the underlying expression.\r
+ * @exception StandardException thrown on error\r
+ */\r
+ protected int getOrderableVariantType() throws StandardException\r
+ {\r
+ return value.getOrderableVariantType();\r
+ }\r
+\r
+ ///////////////////////////////////////////////////////////////////////\r
+ //\r
+ // CODE GENERATION METHODS\r
+ //\r
+ ///////////////////////////////////////////////////////////////////////\r
+\r
+\r
+ /**\r
+ * Generate code to get the Java value out of a SQL value.\r
+ *\r
+ * Every SQL type has a corresponding Java type. The getObject() method\r
+ * on the SQL type gets the right Java type.\r
+ *\r
+ * The generated code will be:\r
+ *\r
+ * (<Java type name>) ((DataValueDescriptor)\r
+ * <generated value>.getObject())\r
+ *\r
+ * where <Java type name> comes from the getCorrespondingJavaTypeName()\r
+ * method of the value's TypeId.\r
+ *\r
+ * @param acb The ExpressionClassBuilder for the class being built\r
+ * @param mb The method the expression will go into\r
+ *\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+\r
+ public void generateExpression(ExpressionClassBuilder acb,\r
+ MethodBuilder mb)\r
+ throws StandardException\r
+ { \r
+ /* Compile the expression under us */\r
+ generateSQLValue( acb, mb );\r
+\r
+ /* now cast the SQLValue to a Java value */\r
+ generateJavaValue( acb, mb);\r
+ }\r
+\r
+ /**\r
+ * Generate the SQLvalue that this node wraps.\r
+ *\r
+ * @param acb The ExpressionClassBuilder for the class being built\r
+ * @param mb The method the expression will go into\r
+ *\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+\r
+ private void generateSQLValue(ExpressionClassBuilder acb,\r
+ MethodBuilder mb)\r
+ throws StandardException\r
+ {\r
+ value.generateExpression(acb, mb);\r
+ }\r
+\r
+ /**\r
+ * Generate code to cast the SQLValue to a Java value.\r
+ *\r
+ *\r
+ * @param acb The ExpressionClassBuilder for the class being built\r
+ * @param mbex The method the expression will go into\r
+ *\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+\r
+ private void generateJavaValue\r
+ (\r
+ ExpressionClassBuilder acb,\r
+ MethodBuilder mbex\r
+ )\r
+ throws StandardException\r
+ {\r
+ /* If this is a conversion to a primitive type, then call the\r
+ * appropriate method for getting the primitive value and\r
+ * cast it to the primitive type. \r
+ * NOTE: We first call Activation.nullToPrimitiveTest(),\r
+ * which will throw a StandardException if the value is null\r
+ */\r
+ if ( isPrimitiveType() || mustCastToPrimitive() )\r
+ {\r
+ String primitiveTN = value.getTypeCompiler().getCorrespondingPrimitiveTypeName();\r
+\r
+ /* Put the code to check if the object is null and to\r
+ * get the primitive value in a method call. This is\r
+ * necessary because we are generating an expression here and\r
+ * cannot have multiple statements.\r
+ * The method call will take SQLValue as a parameter.\r
+ */\r
+ String[] pd = new String[1];\r
+ pd[0] = getSQLValueInterfaceName(); // parameter "param1"\r
+\r
+ MethodBuilder mb = acb.newGeneratedFun(primitiveTN, Modifier.PRIVATE, pd);\r
+\r
+ mb.getParameter(0);\r
+\r
+ if (returnsNullOnNullState != null)\r
+ {\r
+ generateReturnsNullOnNullCheck(mb);\r
+ }\r
+ else\r
+ {\r
+ mb.dup();\r
+ mb.upCast(ClassName.DataValueDescriptor);\r
+ mb.push(primitiveTN); \r
+ mb.callMethod(VMOpcode.INVOKESTATIC, ClassName.BaseActivation, "nullToPrimitiveTest", "void", 2);\r
+ }\r
+\r
+ // stack is dvd\r
+\r
+ /* Generate the code to get the primitive value */\r
+ mb.callMethod(VMOpcode.INVOKEINTERFACE, ClassName.DataValueDescriptor,\r
+ value.getTypeCompiler().getPrimitiveMethodName(), primitiveTN, 0);\r
+\r
+ mb.methodReturn();\r
+ mb.complete();\r
+\r
+ /* Generate the call to the new method, with the parameter */\r
+\r
+ mbex.pushThis();\r
+ mbex.swap(); // caller pushed out parameter\r
+ mbex.callMethod(VMOpcode.INVOKEVIRTUAL, (String) null, mb.getName(), primitiveTN, 1);\r
+ }\r
+ else\r
+ {\r
+ if (returnsNullOnNullState != null)\r
+ generateReturnsNullOnNullCheck(mbex);\r
+\r
+ /* Call getObject() to get the right type of Java value */\r
+ mbex.callMethod(VMOpcode.INVOKEINTERFACE, ClassName.DataValueDescriptor, "getObject",\r
+ "java.lang.Object", 0);\r
+\r
+ mbex.cast(value.getTypeId().getCorrespondingJavaTypeName());\r
+ }\r
+ }\r
+\r
+ /**\r
+ Generate the code for the returns Null on Null input check..\r
+ Stack must contain the DataDescriptorValue.\r
+ */\r
+\r
+ private void generateReturnsNullOnNullCheck(MethodBuilder mb)\r
+ {\r
+ mb.dup();\r
+ mb.callMethod(VMOpcode.INVOKEINTERFACE, ClassName.Storable,\r
+ "isNull", "boolean", 0);\r
+\r
+ mb.conditionalIf();\r
+ mb.push(true);\r
+ mb.startElseCode();\r
+ mb.getField(returnsNullOnNullState);\r
+ mb.completeConditional();\r
+ \r
+ mb.setField(returnsNullOnNullState);\r
+ }\r
+\r
+\r
+ /**\r
+ * Get the type name of the SQLValue we generate.\r
+ *\r
+ * @return name of interface corresponding to SQLValue\r
+ *\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ private String getSQLValueInterfaceName()\r
+ throws StandardException\r
+ {\r
+ return value.getTypeCompiler().interfaceName();\r
+ }\r
+\r
+ ///////////////////////////////////////////////////////////////////////\r
+ //\r
+ // OTHER VALUE NODE METHODS\r
+ //\r
+ ///////////////////////////////////////////////////////////////////////\r
+\r
+\r
+ /**\r
+ * Get the SQL ValueNode that is being converted to a JavaValueNode\r
+ *\r
+ * @return The underlying SQL ValueNode\r
+ */\r
+ ValueNode getSQLValueNode()\r
+ {\r
+ return value;\r
+ }\r
+\r
+ /** @see ValueNode#getConstantValueAsObject \r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ Object getConstantValueAsObject()\r
+ throws StandardException\r
+ {\r
+ return value.getConstantValueAsObject();\r
+ }\r
+\r
+ /**\r
+ * Accept a visitor, and call v.visit()\r
+ * on child nodes as necessary. \r
+ * \r
+ * @param v the visitor\r
+ *\r
+ * @exception StandardException on error\r
+ */\r
+ public Visitable accept(Visitor v) \r
+ throws StandardException\r
+ {\r
+ Visitable returnNode = v.visit(this);\r
+ \r
+ if (v.skipChildren(this))\r
+ {\r
+ return returnNode;\r
+ }\r
+\r
+ if (value != null && !v.stopTraversal())\r
+ {\r
+ value = (ValueNode)value.accept(v);\r
+ }\r
+\r
+ return returnNode;\r
+ }\r
+}\r