--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.sql.compile.NonStaticMethodCallNode\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.SQLState;\r
+\r
+import org.apache.derby.iapi.services.context.ContextManager;\r
+\r
+import org.apache.derby.iapi.sql.compile.CompilerContext;\r
+\r
+import org.apache.derby.iapi.sql.dictionary.DataDictionary;\r
+import org.apache.derby.iapi.reference.SQLState;\r
+import org.apache.derby.iapi.error.StandardException;\r
+import org.apache.derby.iapi.store.access.Qualifier;\r
+\r
+\r
+import org.apache.derby.iapi.sql.compile.Visitable;\r
+import org.apache.derby.iapi.sql.compile.Visitor;\r
+import org.apache.derby.iapi.sql.compile.C_NodeTypes;\r
+\r
+import org.apache.derby.iapi.services.loader.ClassInspector;\r
+\r
+import org.apache.derby.iapi.services.compiler.MethodBuilder;\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+\r
+import org.apache.derby.iapi.types.JSQLType;\r
+\r
+import org.apache.derby.impl.sql.compile.ExpressionClassBuilder;\r
+import org.apache.derby.impl.sql.compile.ActivationClassBuilder;\r
+import org.apache.derby.iapi.error.ExceptionSeverity;\r
+import org.apache.derby.iapi.util.JBitSet;\r
+import org.apache.derby.iapi.services.classfile.VMOpcode;\r
+\r
+import org.apache.derby.catalog.AliasInfo;\r
+\r
+import java.lang.reflect.Modifier;\r
+\r
+import java.util.Vector;\r
+\r
+/**\r
+ * A NonStaticMethodCallNode is really a node to represent a (static or non-static)\r
+ * method call from an object (as opposed to a static method call from a class.\r
+ */\r
+public class NonStaticMethodCallNode extends MethodCallNode\r
+{\r
+ /*\r
+ ** The receiver for a non-static method call is an object, represented\r
+ ** by a ValueNode.\r
+ */\r
+ JavaValueNode receiver; \r
+\r
+ /* Is this a static method call? Assume non-static call */\r
+ private boolean isStatic;\r
+\r
+ /**\r
+ * Initializer for a NonStaticMethodCallNode\r
+ *\r
+ * @param methodName The name of the method to call\r
+ * @param receiver A JavaValueNode representing the receiving object\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public void init(\r
+ Object methodName,\r
+ Object receiver)\r
+ throws StandardException\r
+ {\r
+ super.init(methodName);\r
+\r
+ /*\r
+ ** If the receiver is a Java value that has been converted to a\r
+ ** SQL value, get rid of the conversion and just use the Java value\r
+ ** as-is. If the receiver is a "normal" SQL value, then convert\r
+ ** it to a Java value to use as the receiver.\r
+ */\r
+ if (receiver instanceof JavaToSQLValueNode)\r
+ {\r
+ this.receiver = ((JavaToSQLValueNode) receiver).\r
+ getJavaValueNode();\r
+ }\r
+ else\r
+ {\r
+ this.receiver = (JavaValueNode) getNodeFactory().\r
+ getNode(\r
+ C_NodeTypes.SQL_TO_JAVA_VALUE_NODE,\r
+ receiver,\r
+ getContextManager());\r
+// System.out.println("NonStaticMethodCallNode.init() receiver = "+receiver);\r
+// get nulpointer because not .bind?\r
+// System.out.println("\ttypecompiler = "+((ValueNode)receiver).getTypeCompiler());\r
+// System.out.println("\tdtd = "+((ValueNode)receiver).getTypeServices());\r
+// System.out.println("\ttypeid = "+((ValueNode)receiver).getTypeServices().getTypeId());\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 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
+ boolean nullParameter = false;\r
+ String[] parmTypeNames;\r
+\r
+ /* NULL and ? not allowed as receivers */\r
+ if (receiver instanceof SQLToJavaValueNode)\r
+ {\r
+ ValueNode SQLValue =\r
+ ((SQLToJavaValueNode) receiver).getSQLValueNode();\r
+\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(!(SQLValue instanceof UntypedNullConstantNode),\r
+ "UntypedNullConstantNode found as a receiver of a non-static method call");\r
+\r
+ //\r
+ // We don't allow methods off of naked unnamed "?" parameters.\r
+ // This is because we have no way of knowing the data type of\r
+ // a naked "?" parameter.\r
+ //\r
+ // However, if this "?" has actually been associated with a\r
+ // named "?paramName" parameter in a COPY PUBLICATION statement,\r
+ // then we have a type for it. Binding can continue.\r
+ //\r
+\r
+ if (SQLValue.requiresTypeFromContext())\r
+ {\r
+ if ( SQLValue.getTypeServices() == null )\r
+ { throw StandardException.newException(SQLState.LANG_PARAMETER_RECEIVER, methodName); }\r
+ }\r
+ }\r
+\r
+ bindParameters(fromList, subqueryList, aggregateVector);\r
+\r
+ /* Now we don't allow an alias static method call here (that has to\r
+ * use :: sign for any static call). If it gets here, it can't be\r
+ * alias static method call.\r
+ */\r
+ receiver = receiver.bindExpression(fromList, subqueryList, aggregateVector);\r
+\r
+ // Don't allow LOB types to be used as a method receiver\r
+ String type = receiver.getJSQLType().getSQLType().getTypeId().getSQLTypeName();\r
+ if ( type.equals("BLOB") || type.equals("CLOB") || type.equals("NCLOB") ) {\r
+ throw StandardException.newException(SQLState.LOB_AS_METHOD_ARGUMENT_OR_RECEIVER);\r
+ }\r
+\r
+ javaClassName = receiver.getJavaTypeName();\r
+\r
+ /* Not allowed to use a primitive type as a method receiver */\r
+ if (ClassInspector.primitiveType(javaClassName))\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_PRIMITIVE_RECEIVER, methodName, javaClassName);\r
+ }\r
+\r
+ /* Resolve the method call */\r
+ resolveMethodCall(javaClassName, false);\r
+\r
+ /* Remember if method is static */\r
+ isStatic = Modifier.isStatic(method.getModifiers());\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
+ * @exception StandardException Thrown on error\r
+ */\r
+ public boolean categorize(JBitSet referencedTabs, boolean simplePredsOnly)\r
+ throws StandardException\r
+ {\r
+ /* We stop here when only considering simple predicates\r
+ * as we don't consider method calls when looking\r
+ * for null invariant predicates.\r
+ */\r
+ if (simplePredsOnly)\r
+ {\r
+ return false;\r
+ }\r
+\r
+ boolean pushable = true;\r
+\r
+ pushable = pushable && super.categorize(referencedTabs, simplePredsOnly);\r
+\r
+ if (receiver != null)\r
+ {\r
+ pushable = pushable && receiver.categorize(referencedTabs, simplePredsOnly);\r
+ }\r
+\r
+ return pushable;\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
+ * (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() throws StandardException\r
+ {\r
+ int receiverVariant = receiver.getOrderableVariantType();\r
+\r
+ if (receiverVariant > Qualifier.SCAN_INVARIANT) {\r
+ \r
+ // If the method call is related to a trigger then the return\r
+ // values are SCAN_INVARIANT even though their calls look QUERY_INVARIANT\r
+ // because they take no parameters.\r
+ if (receiver.getJavaTypeName().equals("org.apache.derby.iapi.db.TriggerExecutionContext"))\r
+ receiverVariant = Qualifier.SCAN_INVARIANT;\r
+ }\r
+\r
+\r
+ int thisVariant = super.getOrderableVariantType();\r
+ if (receiverVariant < thisVariant) //return the more variant one\r
+ return receiverVariant;\r
+ return thisVariant;\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
+ if (receiver != null)\r
+ {\r
+ receiver.remapColumnReferencesToExpressions();\r
+ }\r
+\r
+ return super.remapColumnReferencesToExpressions();\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 (receiver != null)\r
+ {\r
+ printLabel(depth, "receiver :");\r
+ receiver.treePrint(depth + 1);\r
+ }\r
+ }\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
+ super.preprocess(numTables,\r
+ outerFromList, outerSubqueryList,\r
+ outerPredicateList);\r
+ receiver.preprocess(numTables,\r
+ outerFromList, outerSubqueryList,\r
+ outerPredicateList);\r
+\r
+ }\r
+\r
+ /**\r
+ * Do code generation for this method call\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
+ boolean inConditional = false;\r
+ /*\r
+ ** If this method returns its value to the Java domain,\r
+ ** generate the receiver and put the value in a field (only if\r
+ ** this method does not return a primitive type). If it\r
+ ** returns its value to the SQL domain, it's up to the JavaToSQLNode\r
+ ** to call generateReceiver().\r
+ **\r
+ ** Also, don't do this if the return value from this method\r
+ ** call will be discarded. This is the case for a CALL statement.\r
+ ** One reason we don't want to do this for a CALL statement\r
+ ** is that the ?: operator cannot be made into a statement.\r
+ */\r
+ if ( ( ! valueReturnedToSQLDomain()) && ( ! returnValueDiscarded()))\r
+ {\r
+ if (generateReceiver(acb, mb, receiver))\r
+ {\r
+ /*\r
+ ** If the above did generate the expression, let's test it for\r
+ ** a null value.\r
+ */\r
+ /*\r
+ ** Generate the following to test for null:\r
+ **\r
+ ** (receiverExpression == null) ?\r
+ */\r
+\r
+ inConditional = true;\r
+ mb.conditionalIfNull();\r
+ mb.pushNull(getJavaTypeName());\r
+ mb.startElseCode();\r
+ }\r
+ }\r
+\r
+ /*\r
+ ** Generate the following:\r
+ **\r
+ ** <receiver>.<method name>(<param> (, <param> )* )\r
+ **\r
+ ** for non-static calls.\r
+ **\r
+ ** Refer to the field holding the receiver, if there is any.\r
+ */\r
+\r
+ Class declaringClass = method.getDeclaringClass();\r
+ \r
+ /*\r
+ ** If it's an interface, generate an interface method call, if it's a static,\r
+ ** generate a static method call, otherwise generate a regular method call.\r
+ */\r
+\r
+ short methodType;\r
+\r
+ if (declaringClass.isInterface())\r
+ methodType = VMOpcode.INVOKEINTERFACE;\r
+ else if (isStatic)\r
+ methodType = VMOpcode.INVOKESTATIC;\r
+ else\r
+ methodType = VMOpcode.INVOKEVIRTUAL;\r
+\r
+ getReceiverExpression(acb, mb, receiver);\r
+ if (isStatic)\r
+ mb.endStatement(); // PUSHCOMPILER ???\r
+\r
+ int nargs = generateParameters(acb, mb);\r
+\r
+ mb.callMethod(methodType, declaringClass.getName(), methodName, getJavaTypeName(), nargs);\r
+\r
+ if (inConditional)\r
+ mb.completeConditional();\r
+ }\r
+\r
+ /**\r
+ * Generate the expression that evaluates to the receiver. This is\r
+ * for the case where a java expression is being returned to the SQL\r
+ * domain, and we need to check whether the receiver is null (if so,\r
+ * the SQL value should be set to null, and this Java expression\r
+ * not evaluated). Instance method calls and field references have\r
+ * receivers, while class method calls and calls to constructors do\r
+ * not. If this Java expression does not have a receiver, this method\r
+ * returns null.\r
+ *\r
+ * Only generate the receiver once and cache it in a field. This is\r
+ * because there will be two references to the receiver, and we want\r
+ * to evaluate it only once.\r
+ *\r
+ *\r
+ * @param acb The ExpressionClassBuilder for the class being built\r
+ * @param mb The method the expression will go into\r
+ *\r
+ * @return true if compiled receiver, false otherwise.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ protected boolean generateReceiver(ExpressionClassBuilder acb,\r
+ MethodBuilder mb)\r
+ throws StandardException\r
+ {\r
+ /*\r
+ ** Let's pretend that a call to a static method doesn't have a\r
+ ** receiver, since the method call is actually to the class,\r
+ ** and can be made even if the receiver is null (that is, we\r
+ ** always want to call a static method, even if the receiver is null).\r
+ */\r
+ if (isStatic)\r
+ return false;\r
+ \r
+ return generateReceiver(acb, mb, receiver);\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
+ if (v.skipChildren(this))\r
+ {\r
+ return v.visit(this);\r
+ }\r
+\r
+ Visitable returnNode = super.accept(v);\r
+\r
+ if (receiver != null && !v.stopTraversal())\r
+ {\r
+ receiver = (JavaValueNode)receiver.accept(v);\r
+ }\r
+\r
+ return returnNode;\r
+ }\r
+}\r