--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.sql.compile.ParameterNode\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 java.sql.Types;\r
+import java.util.Enumeration;\r
+import java.util.Vector;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+import org.apache.derby.iapi.reference.ClassName;\r
+import org.apache.derby.iapi.reference.SQLState;\r
+import org.apache.derby.iapi.services.classfile.VMOpcode;\r
+import org.apache.derby.iapi.services.compiler.MethodBuilder;\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+import org.apache.derby.iapi.sql.compile.CompilerContext;\r
+import org.apache.derby.iapi.store.access.Qualifier;\r
+import org.apache.derby.iapi.types.DataTypeDescriptor;\r
+import org.apache.derby.iapi.types.DataValueDescriptor;\r
+import org.apache.derby.iapi.types.JSQLType;\r
+import org.apache.derby.iapi.types.TypeId;\r
+\r
+/**\r
+ * This node type represents a ? parameter.\r
+ *\r
+ */\r
+\r
+public class ParameterNode extends ValueNode\r
+{\r
+\r
+ /*\r
+ ** The parameter number for this parameter. The numbers start at 0.\r
+ */\r
+ private int parameterNumber;\r
+\r
+ /**\r
+ ** Pointer to the array in the CompilerContext that holds array\r
+ * of types for all the user-visible paramerers.. When each parameter is\r
+ ** bound, it fills in its type descriptor in this array. Note that\r
+ ** the array is allocated in the parser, but the individual elements\r
+ ** are not filled in until their corresponding parameters are bound.\r
+ *\r
+ * This array is not read in this class, but is read from the\r
+ * CompilerContext on completion of compiling the statement.\r
+ * \r
+ * In some case a parameter node may exist but is not a visble\r
+ * user parameter, in this case typeServices will be null\r
+ * so that setting its type will not modify the user's set.\r
+ */\r
+\r
+ private DataTypeDescriptor[] userParameterTypes;\r
+\r
+ /*\r
+ ** The default value for this parameter. Currently, the only\r
+ ** reason for a parameter having a default value is for a\r
+ ** stored prepared statement, where they are supplied for\r
+ ** optimization.\r
+ */\r
+ private DataValueDescriptor defaultValue;\r
+\r
+ /**\r
+ * This ParameterNode may turn up as an argument to a replicated Work Unit.\r
+ * If so, the remote system will have figured out the type of this node.\r
+ * That's what this variable is for.\r
+ */\r
+ private JSQLType jsqlType;\r
+\r
+ private int orderableVariantType = Qualifier.QUERY_INVARIANT;\r
+\r
+ /**\r
+ * By default, we assume we are just a normal, harmless\r
+ * little ole parameter. But sometimes we may be a return\r
+ * parameter (e.g. ? = CALL myMethod()). \r
+ */\r
+ private ValueNode returnOutputParameter;\r
+\r
+ /**\r
+ * If this parameter node was created as part of a "probe predicate"\r
+ * for an InListOperatorNode then it does not actually correspond to\r
+ * a specific value--we just created it as a start-key place-holder\r
+ * for IN-list values at execution time. In order to serve that\r
+ * purpose we need to generate some value that can be used as the\r
+ * place-holder. Since this parameter node is "fake" and does not\r
+ * correspond to an actual parameter, we can't really generate it;\r
+ * so the following field holds some legitimate ValueNode--either a\r
+ * constant node or a "real" parameter node--that we can generate to\r
+ * serve as the place-holder.\r
+ */\r
+ private ValueNode valToGenerate;\r
+\r
+ /**\r
+ * Constructor for use by the NodeFactory\r
+ */\r
+ public ParameterNode()\r
+ {\r
+ }\r
+\r
+ /**\r
+ * Initializer for a ParameterNode.\r
+ *\r
+ * @param parameterNumber The number of this parameter,\r
+ * (unique per query starting at 0)\r
+ * @param defaultValue The default value for this parameter\r
+ *\r
+ */\r
+\r
+ public void init(Object parameterNumber, Object defaultValue)\r
+ {\r
+ this.defaultValue = (DataValueDescriptor) defaultValue;\r
+ this.parameterNumber = ((Integer) parameterNumber).intValue();\r
+ }\r
+\r
+ /**\r
+ * Get the parameter number\r
+ *\r
+ * @return The parameter number\r
+ */\r
+\r
+ int getParameterNumber()\r
+ {\r
+ return parameterNumber;\r
+ }\r
+\r
+ /**\r
+ * Set the descriptor array\r
+ *\r
+ * @param descriptors The array of DataTypeServices to fill in when the parameters\r
+ * are bound.\r
+ */\r
+\r
+ void setDescriptors(DataTypeDescriptor[] descriptors)\r
+ {\r
+ userParameterTypes = descriptors;\r
+ }\r
+\r
+ /**\r
+ * Set the DataTypeServices for this parameter\r
+ *\r
+ * @param descriptor The DataTypeServices to set for this parameter\r
+ */\r
+\r
+ public void setType(DataTypeDescriptor descriptor) throws StandardException\r
+ {\r
+ /* Make sure the type is nullable. */\r
+\r
+ /*\r
+ ** Generate a new descriptor with all the same properties as\r
+ ** the given one, except that it is nullable.\r
+ */\r
+ descriptor = descriptor.getNullabilityType(true);\r
+ \r
+\r
+ if (userParameterTypes != null)\r
+ userParameterTypes[parameterNumber] = descriptor;\r
+\r
+ //make sure we are calling super's setType. We will get into\r
+ //an infinite loop if this setType ends up calling the local\r
+ //setType method\r
+ super.setType(descriptor);\r
+\r
+ if ( getJSQLType() == null ) { setJSQLType( new JSQLType( descriptor ) ); }\r
+ }\r
+\r
+ /**\r
+ * Mark this as a return output parameter (e.g.\r
+ * ? = CALL myMethod())\r
+ */\r
+ public void setReturnOutputParam(ValueNode valueNode)\r
+ {\r
+ returnOutputParameter = valueNode;\r
+ }\r
+\r
+ /**\r
+ * Is this as a return output parameter (e.g.\r
+ * ? = CALL myMethod())\r
+ *\r
+ * @return true if it is a return param\r
+ */\r
+ public boolean isReturnOutputParam()\r
+ {\r
+ return returnOutputParameter != null;\r
+ }\r
+\r
+ /**\r
+ * Bind this expression. A parameter can't figure out what its type\r
+ * is without knowing where it appears, so this method does nothing.\r
+ * It is up to the node that points to this parameter node to figure\r
+ * out the type of the parameter and set it, using the setType()\r
+ * method above.\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 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
+\r
+ public ValueNode bindExpression(\r
+ FromList fromList, SubqueryList subqueryList,\r
+ Vector aggregateVector) \r
+ throws StandardException\r
+ {\r
+ checkReliability( "?", CompilerContext.UNNAMED_PARAMETER_ILLEGAL );\r
+\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Return whether or not this expression tree represents a constant expression.\r
+ *\r
+ * @return Whether or not this expression tree represents a constant expression.\r
+ */\r
+ public boolean isConstantExpression()\r
+ {\r
+ return true;\r
+ }\r
+\r
+ /** @see ValueNode#constantExpression */\r
+ public boolean constantExpression(PredicateList whereClause)\r
+ {\r
+ return true;\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
+ */\r
+ protected int getOrderableVariantType()\r
+ {\r
+ // Parameters are invariant for the life of the query\r
+ return orderableVariantType;\r
+ }\r
+\r
+ /**\r
+ * In a special circumstance, we want to consider\r
+ * parameters as constants. For that situation, we\r
+ * allow a caller to temporarily set us to CONSTANT\r
+ * and then restore us.\r
+ */\r
+ void setOrderableVariantType(int type)\r
+ {\r
+ orderableVariantType = type;\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////\r
+ //\r
+ // OVERRIDE METHODS IN VALUE NODE THAT ARE USED WHILE BINDING REPLICATED\r
+ // CALL WORK STATEMENTS.\r
+ //\r
+ // In this scenario, a JSQLType was replicated along with this parameter.\r
+ // The JSQLType represents the bind() decision of the remote system, which\r
+ // we want to reproduce locally.\r
+ //\r
+ ////////////////////////////////////////////////////////////////////////\r
+\r
+ /**\r
+ * Set the JSQLType of this parameter. This supports the unnamed parameters\r
+ * that we use for replicated work units.\r
+ *\r
+ * @param type the JSQLType associated with this parameter\r
+ */\r
+ public void setJSQLType\r
+ (\r
+ JSQLType type\r
+ )\r
+ { jsqlType = type; }\r
+\r
+ /**\r
+ * Get the JSQLType associated with this parameter. Again, part of method\r
+ * resolution for replicated work units.\r
+ *\r
+ * @return the JSQLType that the remote system assigned\r
+ */\r
+ public JSQLType getJSQLType()\r
+ {\r
+ return jsqlType;\r
+ }\r
+\r
+\r
+ ////////////////////////////////////////////////////////////////////\r
+ //\r
+ // CODE GENERATOR\r
+ //\r
+ ////////////////////////////////////////////////////////////////////\r
+\r
+ /**\r
+ * For a ParameterNode, we generate for the return value:\r
+ *\r
+ * (<java type name>)\r
+ * ( (BaseActivation) this.getParameter(parameterNumber) )\r
+ *\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
+ public void generateExpression(ExpressionClassBuilder acb,\r
+ MethodBuilder mb)\r
+ throws StandardException\r
+ {\r
+ /* If we were given a specific ValueNode to generate then\r
+ * just use that. See, in particular, the preprocess method\r
+ * of InListOperatorNode.\r
+ */\r
+ if (valToGenerate != null)\r
+ {\r
+ valToGenerate.generateExpression(acb, mb);\r
+ return;\r
+ }\r
+\r
+ DataTypeDescriptor dtd = getTypeServices();\r
+ if ((dtd != null) && dtd.getTypeId().isXMLTypeId()) {\r
+ // We're a parameter that corresponds to an XML column/target,\r
+ // which we don't allow. We throw the error here instead of\r
+ // in "bindExpression" because at the time of bindExpression,\r
+ // we don't know yet what the type is going to be (only when\r
+ // the node that points to this parameter calls\r
+ // "setType" do we figure out the type).\r
+ throw StandardException.newException(\r
+ SQLState.LANG_ATTEMPT_TO_BIND_XML);\r
+ }\r
+\r
+ /* Generate the return value */\r
+\r
+ mb.pushThis();\r
+ mb.push(parameterNumber); // arg\r
+\r
+ mb.callMethod(VMOpcode.INVOKEVIRTUAL, ClassName.BaseActivation, "getParameter",\r
+ ClassName.DataValueDescriptor, 1);\r
+\r
+ // For some types perform host variable checking\r
+ // to match DB2/JCC where if a host variable is too\r
+ // big it is not accepted, regardless of any trailing padding.\r
+\r
+ switch (dtd.getJDBCTypeId()) {\r
+ case Types.BINARY:\r
+ case Types.VARBINARY:\r
+ case Types.LONGVARBINARY:\r
+ case Types.BLOB:\r
+ mb.dup();\r
+ mb.push(dtd.getMaximumWidth());\r
+ mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null, "checkHostVariable",\r
+ "void", 1);\r
+ break;\r
+\r
+ default:\r
+ break;\r
+ }\r
+\r
+ /* Cast the result to its specific interface */\r
+ mb.cast(getTypeCompiler().interfaceName());\r
+ } // End of generateExpression\r
+\r
+ public TypeId getTypeId() throws StandardException\r
+ {\r
+ return (returnOutputParameter != null) ?\r
+ returnOutputParameter.getTypeId() : super.getTypeId();\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////\r
+ //\r
+ // STATIC ROUTINES\r
+ //\r
+ ////////////////////////////////////////////////////////////////////\r
+\r
+ /**\r
+ * Generate the code to create the ParameterValueSet, if necessary,\r
+ * when constructing the activation. Also generate the code to call\r
+ * a method that will throw an exception if we try to execute without\r
+ * all the parameters being set.\r
+ * \r
+ * This generated code goes into the Activation's constructor early on.\r
+ * \r
+ * @param acb The ExpressionClassBuilder for the class we're building\r
+ * @param numberOfParameters number of parameters for this statement\r
+ * @param parameterList The parameter list for the statement.\r
+ *\r
+ * @exception StandardException on error\r
+ */\r
+ static public void generateParameterValueSet(ExpressionClassBuilder acb,\r
+ int numberOfParameters,\r
+ Vector parameterList)\r
+ throws StandardException\r
+ {\r
+ if (numberOfParameters > 0)\r
+ {\r
+ MethodBuilder constructor = acb.getConstructor();\r
+\r
+ /*\r
+ ** Check the first parameter to see if it is a return\r
+ ** parameter.\r
+ */\r
+ boolean hasReturnParam = ((ParameterNode)parameterList.elementAt(0)).isReturnOutputParam();\r
+\r
+ /*\r
+ ** Generate the following:\r
+ **\r
+ ** pvs =\r
+ ** getLanguageConnectionContext()\r
+ ** .getLanguageFactory()\r
+ ** .getParameterValueSet(numberOfParameters);\r
+ **\r
+ ** pvs is a ParameterValueSet that lives in the superclass of\r
+ ** the activation being generated.\r
+ */\r
+\r
+ constructor.pushThis(); // for the put field down below\r
+\r
+ /* Generate the call to getContext */\r
+ //?X constructor.pushThis();\r
+ //?Xconstructor.callMethod(VMOpcode.INVOKEINTERFACE, ClassName.Activation, "getLanguageConnectionContext",\r
+ //?X ClassName.LanguageConnectionContext, 0);\r
+ /*\r
+ ** Call getLanguageFactory()\r
+ */\r
+ //?Xconstructor.callMethod(VMOpcode.INVOKEINTERFACE, (String) null, "getLanguageFactory",\r
+ //?X ClassName.LanguageFactory, 0);\r
+\r
+ /*\r
+ ** Call getParameterValueSet(<number of parameters>, <hasReturnParam>)\r
+ */\r
+\r
+ constructor.push(numberOfParameters); // first arg\r
+ constructor.push(hasReturnParam); // second arg\r
+\r
+ constructor.callMethod(VMOpcode.INVOKEVIRTUAL, ClassName.BaseActivation,\r
+ "setParameterValueSet", "void", 2);\r
+\r
+ //?Xconstructor.callMethod(VMOpcode.INVOKEINTERFACE, (String) null, "getParameterValueSet",\r
+ //?X ClassName.ParameterValueSet, 2);\r
+\r
+ /* Assign the return from getParameterValueSet() to the field */\r
+ //?Xconstructor.putField(ClassName.BaseActivation, "pvs", ClassName.ParameterValueSet);\r
+ //?Xconstructor.endStatement();\r
+\r
+ /*\r
+ ** Add a call to the execute() method to check\r
+ ** for missing parameters\r
+ */\r
+ MethodBuilder executeMethod = acb.getExecuteMethod();\r
+\r
+ executeMethod.pushThis();\r
+ executeMethod.callMethod(VMOpcode.INVOKEVIRTUAL, ClassName.BaseActivation, "throwIfMissingParms", "void", 0);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Get the default value for the parameter. Parameters\r
+ * may get default values for optimization purposes.\r
+ *\r
+ * @return the value, may be null\r
+ */\r
+ DataValueDescriptor getDefaultValue()\r
+ {\r
+ return defaultValue;\r
+ }\r
+\r
+ /**\r
+ * @see ValueNode#requiresTypeFromContext\r
+ */\r
+ public boolean requiresTypeFromContext()\r
+ {\r
+ return true;\r
+ }\r
+ \r
+ /**\r
+ * @see ValueNode#isParameterNode\r
+ */\r
+ public boolean isParameterNode()\r
+ {\r
+ return true;\r
+ }\r
+\r
+ /**\r
+ * @inheritDoc\r
+ */\r
+ protected boolean isEquivalent(ValueNode o)\r
+ {\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * Save the received ValueNode locally so that we can generate it\r
+ * (in place of "this") at generation time. See the preprocess()\r
+ * method of InListOperatorNode for more on how this is used.\r
+ *\r
+ * @param vn The ValueNode to generate in place of this ParameterNode.\r
+ */\r
+ protected void setValueToGenerate(ValueNode vn)\r
+ {\r
+ valToGenerate = vn;\r
+ }\r
+}\r