--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.sql.compile.UnaryOperatorNode\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.store.access.Qualifier;\r
+\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.sql.dictionary.DataDictionary;\r
+\r
+import org.apache.derby.iapi.reference.SQLState;\r
+import org.apache.derby.iapi.reference.ClassName;\r
+import org.apache.derby.iapi.error.StandardException;\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+import org.apache.derby.iapi.services.compiler.MethodBuilder;\r
+import org.apache.derby.iapi.services.compiler.LocalField;\r
+import org.apache.derby.iapi.services.io.StoredFormatIds;\r
+\r
+import org.apache.derby.iapi.types.StringDataValue;\r
+import org.apache.derby.iapi.types.TypeId;\r
+import org.apache.derby.iapi.types.DataTypeDescriptor;\r
+import org.apache.derby.iapi.types.SqlXmlUtil;\r
+\r
+import java.lang.reflect.Modifier;\r
+import org.apache.derby.impl.sql.compile.ExpressionClassBuilder;\r
+\r
+import org.apache.derby.iapi.util.JBitSet;\r
+import org.apache.derby.iapi.services.classfile.VMOpcode;\r
+\r
+import java.sql.Types;\r
+import java.util.Vector;\r
+\r
+/**\r
+ * A UnaryOperatorNode represents a built-in unary operator as defined by\r
+ * the ANSI/ISO SQL standard. This covers operators like +, -, NOT, and IS NULL.\r
+ * Java operators are not represented here: the JSQL language allows Java\r
+ * methods to be called from expressions, but not Java operators.\r
+ *\r
+ */\r
+\r
+public class UnaryOperatorNode extends ValueNode\r
+{\r
+ String operator;\r
+ String methodName;\r
+ \r
+ /**\r
+ * Operator type, only valid for XMLPARSE and XMLSERIALIZE.\r
+ */\r
+ private int operatorType;\r
+\r
+ String resultInterfaceType;\r
+ String receiverInterfaceType;\r
+\r
+ /**\r
+ * WARNING: operand may be NULL for COUNT(*). \r
+ */\r
+ ValueNode operand;\r
+\r
+ // At the time of adding XML support, it was decided that\r
+ // we should avoid creating new OperatorNodes where possible.\r
+ // So for the XML-related unary operators we just add the\r
+ // necessary code to _this_ class, similar to what is done in\r
+ // TernarnyOperatorNode. Subsequent unary operators (whether\r
+ // XML-related or not) should follow this example when\r
+ // possible.\r
+ //\r
+ // This has lead to this class having somewhat of\r
+ // a confused personality. In one mode it is really\r
+ // a parent (abstract) class for various unary operator\r
+ // node implementations, in its other mode it is a concrete\r
+ // class for XMLPARSE and XMLSERIALIZE.\r
+\r
+ public final static int XMLPARSE_OP = 0;\r
+ public final static int XMLSERIALIZE_OP = 1;\r
+\r
+ // NOTE: in the following 4 arrays, order\r
+ // IS important.\r
+\r
+ static final String[] UnaryOperators = {\r
+ "xmlparse",\r
+ "xmlserialize"\r
+ };\r
+\r
+ static final String[] UnaryMethodNames = {\r
+ "XMLParse",\r
+ "XMLSerialize"\r
+ };\r
+\r
+ static final String[] UnaryResultTypes = {\r
+ ClassName.XMLDataValue, // XMLParse\r
+ ClassName.StringDataValue // XMLSerialize\r
+ };\r
+\r
+ static final String[] UnaryArgTypes = {\r
+ ClassName.StringDataValue, // XMLParse\r
+ ClassName.XMLDataValue // XMLSerialize\r
+ };\r
+\r
+ // Array to hold Objects that contain primitive\r
+ // args required by the operator method call.\r
+ private Object [] additionalArgs;\r
+\r
+ // Class used to hold XML-specific objects required for\r
+ // parsing/serializing XML data.\r
+ private SqlXmlUtil sqlxUtil;\r
+\r
+ /**\r
+ * Initializer for a UnaryOperatorNode.\r
+ *\r
+ * <ul>\r
+ * @param operand The operand of the node\r
+ * @param operatorOrOpType Either 1) the name of the operator,\r
+ * OR 2) an Integer holding the operatorType for this operator.\r
+ * @param methodNameOrAddedArgs Either 1) name of the method\r
+ * to call for this operator, or 2) an array of Objects\r
+ * from which primitive method parameters can be\r
+ * retrieved.\r
+ */\r
+\r
+ public void init(\r
+ Object operand,\r
+ Object operatorOrOpType,\r
+ Object methodNameOrAddedArgs)\r
+ {\r
+ this.operand = (ValueNode) operand;\r
+ if (operatorOrOpType instanceof String) {\r
+ // then 2nd and 3rd params are operator and methodName,\r
+ // respectively.\r
+ this.operator = (String) operatorOrOpType;\r
+ this.methodName = (String) methodNameOrAddedArgs;\r
+ this.operatorType = -1;\r
+ }\r
+ else {\r
+ // 2nd and 3rd params are operatorType and additional args,\r
+ // respectively.\r
+ if (SanityManager.DEBUG) {\r
+ SanityManager.ASSERT(\r
+ ((operatorOrOpType instanceof Integer) &&\r
+ ((methodNameOrAddedArgs == null) ||\r
+ (methodNameOrAddedArgs instanceof Object[]))),\r
+ "Init params in UnaryOperator node have the " +\r
+ "wrong type.");\r
+ }\r
+ this.operatorType = ((Integer) operatorOrOpType).intValue();\r
+ this.operator = UnaryOperators[this.operatorType];\r
+ this.methodName = UnaryMethodNames[this.operatorType];\r
+ this.resultInterfaceType = UnaryResultTypes[this.operatorType];\r
+ this.receiverInterfaceType = UnaryArgTypes[this.operatorType];\r
+ this.additionalArgs = (Object[])methodNameOrAddedArgs;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Initializer for a UnaryOperatorNode\r
+ *\r
+ * @param operand The operand of the node\r
+ */\r
+ public void init(Object operand)\r
+ {\r
+ this.operand = (ValueNode) operand;\r
+ this.operatorType = -1;\r
+ }\r
+\r
+ /**\r
+ * Set the operator.\r
+ *\r
+ * @param operator The operator.\r
+ */\r
+ void setOperator(String operator)\r
+ {\r
+ this.operator = operator;\r
+ this.operatorType = -1;\r
+ }\r
+\r
+ /**\r
+ * Get the operator of this unary operator.\r
+ *\r
+ * @return The operator of this unary operator.\r
+ */\r
+ String getOperatorString()\r
+ {\r
+ return operator;\r
+ }\r
+\r
+ /**\r
+ * Set the methodName.\r
+ *\r
+ * @param methodName The methodName.\r
+ */\r
+ void setMethodName(String methodName)\r
+ {\r
+ this.methodName = methodName;\r
+ this.operatorType = -1;\r
+ }\r
+\r
+ /**\r
+ * Convert this object to a String. See comments in QueryTreeNode.java\r
+ * for how this should be done for tree printing.\r
+ *\r
+ * @return This object as a String\r
+ */\r
+\r
+ public String toString()\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ return "operator: " + operator + "\n" +\r
+ "methodName: " + methodName + "\n" +\r
+ super.toString();\r
+ }\r
+ else\r
+ {\r
+ return "";\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
+\r
+ if (operand != null)\r
+ {\r
+ printLabel(depth, "operand: ");\r
+ operand.treePrint(depth + 1);\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Get the operand of this unary operator.\r
+ *\r
+ * @return The operand of this unary operator.\r
+ */\r
+ public ValueNode getOperand()\r
+ {\r
+ return operand;\r
+ }\r
+\r
+ /**\r
+ * Get the parameter operand of this unary operator.\r
+ * For the example below, for abs unary operator node, we want to get ?\r
+ * select * from t1 where -? = max_cni(abs(-?), sqrt(+?))\r
+ * \r
+ * This gets called when ParameterNode is needed to get parameter\r
+ * specific information like getDefaultValue(), getParameterNumber() etc \r
+ * \r
+ * @return The parameter operand of this unary operator else null.\r
+ */\r
+ public ParameterNode getParameterOperand() throws StandardException\r
+ {\r
+ if (requiresTypeFromContext() == false)\r
+ return null;\r
+ else {\r
+ UnaryOperatorNode tempUON = this;\r
+ while (!(tempUON.getOperand() instanceof ParameterNode)) \r
+ tempUON = (UnaryOperatorNode)tempUON.getOperand();\r
+ return (ParameterNode)(tempUON.getOperand());\r
+ }\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
+ * This method is the implementation for XMLPARSE and XMLSERIALIZE.\r
+ * Sub-classes need to implement their own bindExpression() method\r
+ * for their own specific rules.\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
+ bindOperand(fromList, subqueryList, aggregateVector);\r
+ if (operatorType == XMLPARSE_OP)\r
+ bindXMLParse();\r
+ else if (operatorType == XMLSERIALIZE_OP)\r
+ bindXMLSerialize();\r
+ return this;\r
+ }\r
+\r
+ /**\r
+ * Bind the operand for this unary operator.\r
+ * Binding the operator may change the operand node.\r
+ * Sub-classes bindExpression() methods need to call this\r
+ * method to bind the operand.\r
+ */\r
+ protected void bindOperand(\r
+ FromList fromList, SubqueryList subqueryList,\r
+ Vector aggregateVector)\r
+ throws StandardException\r
+ {\r
+ operand = operand.bindExpression(fromList, subqueryList,\r
+ aggregateVector);\r
+\r
+ if (operand.requiresTypeFromContext()) {\r
+ bindParameter();\r
+ // If not bound yet then just return.\r
+ // The node type will be set by either\r
+ // this class' bindExpression() or a by\r
+ // a node that contains this expression.\r
+ if (operand.getTypeServices() == null)\r
+ return;\r
+ }\r
+\r
+ /* If the operand is not a built-in type, then generate a bound conversion\r
+ * tree to a built-in type.\r
+ */\r
+ if (! (operand instanceof UntypedNullConstantNode) &&\r
+ operand.getTypeId().userType() &&\r
+ ! (this instanceof IsNullNode))\r
+ {\r
+ operand = operand.genSQLJavaSQLTree();\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Bind an XMLPARSE operator. Makes sure the operand type\r
+ * is correct, and sets the result type.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ private void bindXMLParse() throws StandardException\r
+ {\r
+ // Check the type of the operand - this function is allowed only on\r
+ // string value (char) types.\r
+ TypeId operandType = operand.getTypeId();\r
+ if (operandType != null) {\r
+ switch (operandType.getJDBCTypeId())\r
+ {\r
+ case Types.CHAR:\r
+ case Types.VARCHAR:\r
+ case Types.LONGVARCHAR:\r
+ case Types.CLOB:\r
+ break;\r
+ default:\r
+ {\r
+ throw StandardException.newException(\r
+ SQLState.LANG_UNARY_FUNCTION_BAD_TYPE, \r
+ methodName,\r
+ operandType.getSQLTypeName());\r
+ }\r
+ }\r
+ }\r
+\r
+ // Create a new XML compiler object; the constructor\r
+ // here automatically creates the XML-specific objects \r
+ // required for parsing/serializing XML, so all we\r
+ // have to do is create an instance.\r
+ sqlxUtil = new SqlXmlUtil();\r
+\r
+ // The result type of XMLParse() is always an XML type.\r
+ setType(DataTypeDescriptor.getBuiltInDataTypeDescriptor(\r
+ StoredFormatIds.XML_TYPE_ID));\r
+ }\r
+\r
+ /**\r
+ * Bind an XMLSERIALIZE operator. Makes sure the operand type\r
+ * and target type are both correct, and sets the result type.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ private void bindXMLSerialize() throws StandardException\r
+ {\r
+ TypeId operandType;\r
+\r
+ // Check the type of the operand - this function is allowed only on\r
+ // the XML type.\r
+ operandType = operand.getTypeId();\r
+ if ((operandType != null) && !operandType.isXMLTypeId())\r
+ {\r
+ throw StandardException.newException(\r
+ SQLState.LANG_UNARY_FUNCTION_BAD_TYPE, \r
+ methodName,\r
+ operandType.getSQLTypeName());\r
+ }\r
+\r
+ // Check the target type. We only allow string types to be used as\r
+ // the target type. The targetType is stored as the first Object\r
+ // in our list of additional parameters, so we have to retrieve\r
+ // it from there.\r
+ if (SanityManager.DEBUG) {\r
+ SanityManager.ASSERT(\r
+ ((additionalArgs != null) && (additionalArgs.length > 0)),\r
+ "Failed to locate target type for XMLSERIALIZE operator");\r
+ }\r
+\r
+ DataTypeDescriptor targetType =\r
+ (DataTypeDescriptor)additionalArgs[0];\r
+\r
+ TypeId targetTypeId = targetType.getTypeId();\r
+ switch (targetTypeId.getJDBCTypeId())\r
+ {\r
+ case Types.CHAR:\r
+ case Types.VARCHAR:\r
+ case Types.LONGVARCHAR:\r
+ case Types.CLOB:\r
+ break;\r
+ default:\r
+ {\r
+ throw StandardException.newException(\r
+ SQLState.LANG_INVALID_XMLSERIALIZE_TYPE,\r
+ targetTypeId.getSQLTypeName());\r
+ }\r
+ }\r
+\r
+ // The result type of XMLSerialize() is always a string; which\r
+ // kind of string is determined by the targetType field.\r
+ setType(targetType);\r
+ //Set the collation type to be same as the current schema's \r
+ //collation type. \r
+ setCollationUsingCompilationSchema(\r
+ StringDataValue.COLLATION_DERIVATION_IMPLICIT);\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
+ * @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
+ if (operand != null)\r
+ {\r
+ operand = operand.preprocess(numTables,\r
+ outerFromList, outerSubqueryList,\r
+ outerPredicateList);\r
+ }\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 (operand == null) ? \r
+ false : \r
+ operand.categorize(referencedTabs, simplePredsOnly);\r
+ }\r
+\r
+ /**\r
+ * Remap all ColumnReferences in this tree to be clones of the\r
+ * underlying expression.\r
+ *\r
+ * @return ValueNode The remapped expression tree.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public ValueNode remapColumnReferencesToExpressions()\r
+ throws StandardException\r
+ {\r
+ if (operand != null)\r
+ {\r
+ operand = operand.remapColumnReferencesToExpressions();\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 (operand == null) ? true: operand.isConstantExpression();\r
+ }\r
+\r
+ /** @see ValueNode#constantExpression */\r
+ public boolean constantExpression(PredicateList whereClause)\r
+ {\r
+ return (operand == null) ?\r
+ true :\r
+ operand.constantExpression(whereClause);\r
+ }\r
+\r
+ /**\r
+ * By default unary operators don't accept ? parameters as operands.\r
+ * This can be over-ridden for particular unary operators.\r
+ *\r
+ * We throw an exception if the parameter doesn't have a datatype\r
+ * assigned to it yet.\r
+ *\r
+ * @exception StandardException Thrown if ? parameter doesn't\r
+ * have a type bound to it yet.\r
+ * ? parameter where it isn't allowed.\r
+ */\r
+\r
+ void bindParameter() throws StandardException\r
+ {\r
+ if (operatorType == XMLPARSE_OP)\r
+ {\r
+ /* SQL/XML[2006] allows both binary and character strings for\r
+ * the XMLParse parameter (section 10.16:Function). The spec\r
+ * also goes on to say, in section 6.15:Conformance Rules:4,\r
+ * that:\r
+ *\r
+ * "Without Feature X066, XMLParse: BLOB input and DOCUMENT\r
+ * option, in conforming SQL language, the declared type of\r
+ * the <string value expression> immediately contained in\r
+ * <XML parse> shall not be a binary string type."\r
+ *\r
+ * Thus since Derby doesn't currently support BLOB input,\r
+ * we have to ensure that the "declared type" of the parameter\r
+ * is not a binary string type; i.e. it must be a character\r
+ * string type. Since there's no way to determine what the\r
+ * declared type is from the XMLPARSE syntax, the user must\r
+ * explicitly declare the type of the parameter, and it must\r
+ * be a character string. They way s/he does that is by\r
+ * specifying an explicit CAST on the parameter, such as:\r
+ *\r
+ * insert into myXmlTable (xcol) values\r
+ * XMLPARSE(DOCUMENT cast (? as CLOB) PRESERVE WHITESPACE);\r
+ *\r
+ * If that was done then we wouldn't be here; we only get\r
+ * here if the parameter was specified without a cast. That\r
+ * means we don't know what the "declared type" is and so\r
+ * we throw an error.\r
+ */\r
+ throw StandardException.newException(\r
+ SQLState.LANG_XMLPARSE_UNKNOWN_PARAM_TYPE);\r
+ }\r
+ else if (operatorType == XMLSERIALIZE_OP) {\r
+ // For now, since JDBC has no type defined for XML, we\r
+ // don't allow binding to an XML parameter.\r
+ throw StandardException.newException(\r
+ SQLState.LANG_ATTEMPT_TO_BIND_XML);\r
+ }\r
+ else if (operand.getTypeServices() == null)\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_UNARY_OPERAND_PARM, operator);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Do code generation for this unary operator.\r
+ *\r
+ * @param acb The ExpressionClassBuilder for the class we're generating\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
+ // For XML operator we do some extra work.\r
+ boolean xmlGen = (operatorType == XMLPARSE_OP) ||\r
+ (operatorType == XMLSERIALIZE_OP);\r
+\r
+ if (xmlGen) {\r
+ // We create an execution-time object from which we call\r
+ // the necessary methods. We do this for two reasons: 1) this\r
+ // level of indirection allows us to separate the XML data type\r
+ // from the required XML implementation classes (esp. JAXP and\r
+ // Xalan classes)--for more on how this works, see the comments\r
+ // in SqlXmlUtil.java; and 2) this allows us to create the\r
+ // required XML objects a single time (which we did at bind time\r
+ // when we created a new SqlXmlUtil) and then reuse those objects\r
+ // for each row in the target result set, instead of creating\r
+ // new objects every time; see SqlXmlUtil.java for more.\r
+ mb.pushNewStart(\r
+ "org.apache.derby.impl.sql.execute.SqlXmlExecutor");\r
+ mb.pushNewComplete(addXmlOpMethodParams(acb, mb));\r
+ }\r
+\r
+ String resultTypeName = \r
+ (operatorType == -1)\r
+ ? getTypeCompiler().interfaceName()\r
+ : resultInterfaceType;\r
+ \r
+ // System.out.println("resultTypeName " + resultTypeName + " method " + methodName);\r
+ // System.out.println("isBooleanTypeId() " + getTypeId().isBooleanTypeId());\r
+\r
+ boolean needField = !getTypeId().isBooleanTypeId();\r
+\r
+ String receiverType = getReceiverInterfaceName();\r
+ operand.generateExpression(acb, mb);\r
+ mb.cast(receiverType);\r
+\r
+ if (needField) {\r
+\r
+ /* Allocate an object for re-use to hold the result of the operator */\r
+ LocalField field = acb.newFieldDeclaration(Modifier.PRIVATE, resultTypeName);\r
+ mb.getField(field);\r
+\r
+ /* If we're calling a method on a class (SqlXmlExecutor) instead\r
+ * of calling a method on the operand interface, then we invoke\r
+ * VIRTUAL; we then have 2 args (the operand and the local field)\r
+ * instead of one, i.e:\r
+ *\r
+ * SqlXmlExecutor.method(operand, field)\r
+ *\r
+ * instead of\r
+ *\r
+ * <operand>.method(field).\r
+ */\r
+ if (xmlGen) {\r
+ mb.callMethod(VMOpcode.INVOKEVIRTUAL, null,\r
+ methodName, resultTypeName, 2);\r
+ }\r
+ else {\r
+ mb.callMethod(VMOpcode.INVOKEINTERFACE,\r
+ (String) null, methodName, resultTypeName, 1);\r
+ }\r
+\r
+ /*\r
+ ** Store the result of the method call in the field, so we can re-use\r
+ ** the object.\r
+ */\r
+ mb.putField(field);\r
+ } else {\r
+ mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null,\r
+ methodName, resultTypeName, 0);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Determine the type the binary method is called on.\r
+ * By default, based on the receiver.\r
+ *\r
+ * Override in nodes that use methods on super-interfaces of\r
+ * the receiver's interface, such as comparisons.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public String getReceiverInterfaceName() throws StandardException {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(operand!=null,\r
+ "cannot get interface without operand");\r
+ }\r
+\r
+ if (operatorType != -1)\r
+ return receiverInterfaceType;\r
+ \r
+ return operand.getTypeCompiler().interfaceName();\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
+ * CONSTANT - immutable\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
+ /*\r
+ ** If we have nothing in the operator, then\r
+ ** it must be constant.\r
+ */\r
+ return (operand != null) ?\r
+ operand.getOrderableVariantType() :\r
+ Qualifier.CONSTANT;\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 (operand != null && !v.stopTraversal())\r
+ {\r
+ operand = (ValueNode)operand.accept(v);\r
+ }\r
+\r
+ return returnNode;\r
+ }\r
+\r
+ /**\r
+ * Add some additional arguments to our method call for\r
+ * XML related operations like XMLPARSE and XMLSERIALIZE.\r
+ * @param mb The MethodBuilder that will make the call.\r
+ * @return Number of parameters added.\r
+ */\r
+ protected int addXmlOpMethodParams(ExpressionClassBuilder acb,\r
+ MethodBuilder mb) throws StandardException\r
+ {\r
+ if ((operatorType != XMLPARSE_OP) && (operatorType != XMLSERIALIZE_OP))\r
+ // nothing to do.\r
+ return 0;\r
+\r
+ if (operatorType == XMLSERIALIZE_OP) {\r
+ // We push the target type's JDBC type id as well as\r
+ // the maximum width, since both are required when\r
+ // we actually perform the operation, and both are\r
+ // primitive types. Note: we don't have to save\r
+ // any objects for XMLSERIALIZE because it doesn't\r
+ // require any XML-specific objects: it just returns\r
+ // the serialized version of the XML value, which we\r
+ // already found when the XML value was created (ex.\r
+ // as part of the XMLPARSE work).\r
+ // We also need to pass the collation type of the current\r
+ // compilation schema. If the JDBC type id is of type\r
+ // StringDataValue, then we should use the collation to\r
+ // decide whether we need to generate collation sensitive\r
+ // StringDataValue.\r
+ DataTypeDescriptor targetType =\r
+ (DataTypeDescriptor)additionalArgs[0];\r
+ mb.push(targetType.getJDBCTypeId());\r
+ mb.push(targetType.getMaximumWidth());\r
+ mb.push(getSchemaDescriptor(null, false).getCollationType());\r
+ return 3;\r
+ }\r
+\r
+ /* Else we're here for XMLPARSE. */\r
+\r
+ // Push activation, which we use at execution time to\r
+ // get our saved object (which will hold objects used\r
+ // for parsing/serializing) back.\r
+ acb.pushThisAsActivation(mb);\r
+\r
+ // Push our XML object (used for parsing/serializing) as\r
+ // a saved object, so that we can retrieve it at execution\r
+ // time. This allows us to avoid having to re-create the\r
+ // objects for every row in a given result set.\r
+ mb.push(getCompilerContext().addSavedObject(sqlxUtil));\r
+\r
+ // Push whether or not we want to preserve whitespace.\r
+ mb.push(((Boolean)additionalArgs[0]).booleanValue());\r
+ return 3;\r
+ }\r
+ \r
+ /**\r
+ * @throws StandardException \r
+ * {@inheritDoc}\r
+ */\r
+ protected boolean isEquivalent(ValueNode o) throws StandardException\r
+ {\r
+ if (isSameNodeType(o)) \r
+ {\r
+ // the first condition in the || covers the case when \r
+ // both operands are null.\r
+ UnaryOperatorNode other = (UnaryOperatorNode)o;\r
+ return (operator.equals(other.operator) && \r
+ ((operand == other.operand)|| \r
+ ((operand != null) && operand.isEquivalent(other.operand))));\r
+ }\r
+ return false;\r
+ }\r
+}\r