--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.sql.compile.NewInvocationNode\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.loader.ClassInspector;\r
+\r
+import org.apache.derby.iapi.services.context.ContextManager;\r
+\r
+import org.apache.derby.iapi.services.compiler.MethodBuilder;\r
+import org.apache.derby.iapi.services.compiler.LocalField;\r
+\r
+\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+import org.apache.derby.iapi.services.i18n.MessageService;\r
+\r
+import org.apache.derby.iapi.sql.compile.CompilerContext;\r
+\r
+import org.apache.derby.iapi.sql.dictionary.TableDescriptor;\r
+import org.apache.derby.iapi.types.DataTypeDescriptor;\r
+import org.apache.derby.iapi.types.StringDataValue;\r
+\r
+import org.apache.derby.iapi.reference.SQLState;\r
+\r
+import org.apache.derby.impl.sql.compile.ExpressionClassBuilder;\r
+\r
+import org.apache.derby.iapi.util.JBitSet;\r
+\r
+import org.apache.derby.catalog.AliasInfo;\r
+import org.apache.derby.catalog.TypeDescriptor;\r
+\r
+import java.lang.reflect.Member;\r
+import java.lang.reflect.Modifier;\r
+\r
+import java.util.Vector;\r
+import java.util.Enumeration;\r
+\r
+/**\r
+ * A NewInvocationNode represents a new object() invocation.\r
+ *\r
+ */\r
+public class NewInvocationNode extends MethodCallNode\r
+{\r
+ // Whether or not to do a single instantiation\r
+ private boolean singleInstantiation = false;\r
+\r
+ private boolean delimitedIdentifier;\r
+\r
+ /**\r
+ * Initializer for a NewInvocationNode. Parameters are:\r
+ *\r
+ * <ul>\r
+ * <li>javaClassName The full package.class name of the class</li>\r
+ * <li>parameterList The parameter list for the constructor</li>\r
+ * </ul>\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public void init(\r
+ Object javaClassName,\r
+ Object params,\r
+ Object delimitedIdentifier)\r
+ throws StandardException\r
+ {\r
+ super.init("<init>");\r
+ addParms((Vector) params);\r
+\r
+ this.javaClassName = (String) javaClassName;\r
+ this.delimitedIdentifier =\r
+ ((Boolean) delimitedIdentifier).booleanValue();\r
+ }\r
+\r
+ /* This version of the "init" method is used for mapping a table name\r
+ * or table function name to a corresponding VTI class name. The VTI\r
+ * is then invoked as a regular NEW invocation node.\r
+ *\r
+ * There are two kinds of VTI mappings that we do: the first is for\r
+ * "table names", the second is for "table function names". Table\r
+ * names can only be mapped to VTIs that do not accept any arguments;\r
+ * any VTI that has at least one constructor which accepts one or more\r
+ * arguments must be mapped from a table *function* name. The way we\r
+ * tell the difference is by looking at the received arguments: if\r
+ * the vtiTableFuncName that we receive is null then we are mapping\r
+ * a "table name" and tableDescriptor must be non-null; if the\r
+ * vtiTableFuncName is non-null then we are mapping a "table\r
+ * function name" and tableDescriptor must be null.\r
+ *\r
+ * Note that we could have just used a single "init()" method and\r
+ * performed the mappings based on what type of Object "javaClassName"\r
+ * was (String, TableDescriptor, or TableName), but making this VTI\r
+ * mapping method separate from the "normal" init() method seems\r
+ * cleaner...\r
+ *\r
+ * @param vtiTableFuncName A TableName object holding a qualified name\r
+ * that maps to a VTI which accepts arguments. If vtiTableFuncName is\r
+ * null then tableDescriptor must NOT be null.\r
+ * @param tableDescriptor A table descriptor that corresponds to a\r
+ * table name (as opposed to a table function name) that will be\r
+ * mapped to a no-argument VTI. If tableDescriptor is null then\r
+ * vtiTableFuncName should not be null.\r
+ * @param params Parameter list for the VTI constructor.\r
+ * @param delimitedIdentifier Whether or not the target class name\r
+ * is a delimited identifier.\r
+ */\r
+ public void init(\r
+ Object vtiTableFuncName,\r
+ Object tableDescriptor,\r
+ Object params,\r
+ Object delimitedIdentifier)\r
+ throws StandardException\r
+ {\r
+ super.init("<init>");\r
+ addParms((Vector) params);\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ // Exactly one of vtiTableFuncName or tableDescriptor should\r
+ // be null.\r
+ SanityManager.ASSERT(\r
+ ((vtiTableFuncName == null) && (tableDescriptor != null)) ||\r
+ ((vtiTableFuncName != null) && (tableDescriptor == null)),\r
+ "Exactly one of vtiTableFuncName or tableDescriptor should " +\r
+ "be null, but neither or both of them were null.");\r
+ }\r
+\r
+ TableName vtiName = (TableName)vtiTableFuncName;\r
+ TableDescriptor td = (TableDescriptor)tableDescriptor;\r
+ boolean isTableFunctionVTI = (vtiTableFuncName != null);\r
+ if (isTableFunctionVTI)\r
+ {\r
+ // We have to create a generic TableDescriptor to\r
+ // pass to the data dictionary.\r
+ td = new TableDescriptor(getDataDictionary(),\r
+ vtiName.getTableName(),\r
+ getSchemaDescriptor(vtiName.getSchemaName()),\r
+ TableDescriptor.VTI_TYPE,\r
+ TableDescriptor.DEFAULT_LOCK_GRANULARITY);\r
+ }\r
+\r
+ /* Use the table descriptor to figure out what the corresponding\r
+ * VTI class name is; we let the data dictionary do the mapping\r
+ * for us.\r
+ */\r
+ this.javaClassName = getDataDictionary().getVTIClass(\r
+ td, isTableFunctionVTI);\r
+\r
+ /* If javaClassName is still null at this point then we\r
+ * could not find the target class for the received table\r
+ * (or table function) name. So throw the appropriate\r
+ * error.\r
+ */\r
+ if (this.javaClassName == null)\r
+ {\r
+ if (!isTableFunctionVTI)\r
+ {\r
+ /* Create a TableName object from the table descriptor\r
+ * that we received. This gives us the name to use\r
+ * in the error message.\r
+ */\r
+ vtiName = makeTableName(td.getSchemaName(),\r
+ td.getDescriptorName());\r
+ }\r
+\r
+ throw StandardException.newException(\r
+ isTableFunctionVTI\r
+ ? SQLState.LANG_NO_SUCH_METHOD_ALIAS\r
+ : SQLState.LANG_TABLE_NOT_FOUND,\r
+ vtiName.getFullTableName());\r
+ }\r
+\r
+ this.delimitedIdentifier =\r
+ ((Boolean) delimitedIdentifier).booleanValue();\r
+ }\r
+\r
+ /**\r
+ * Mark this node as only needing to\r
+ * to a single instantiation. (We can\r
+ * reuse the object after newing it.)\r
+ */\r
+ void setSingleInstantiation()\r
+ {\r
+ singleInstantiation = true;\r
+ }\r
+\r
+ /**\r
+ * Get the resolved Classes of our parameters\r
+ *\r
+ * @return the Classes of our parameters\r
+ */\r
+ public Class[] getMethodParameterClasses() \r
+ { \r
+ ClassInspector ci = getClassFactory().getClassInspector();\r
+\r
+ Class[] parmTypeClasses = new Class[methodParms.length];\r
+ for (int i = 0; i < methodParms.length; i++)\r
+ {\r
+ String className = methodParameterTypes[i];\r
+ try\r
+ {\r
+ parmTypeClasses[i] = ci.getClass(className);\r
+ }\r
+ catch (ClassNotFoundException cnfe)\r
+ {\r
+ /* We should never get this exception since we verified \r
+ * that the classes existed at bind time. Just return null.\r
+ */\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.THROWASSERT("Unexpected exception", cnfe);\r
+ }\r
+ return null;\r
+ }\r
+ }\r
+\r
+ return parmTypeClasses;\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 Nothing\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
+ bindParameters(fromList, subqueryList, aggregateVector);\r
+\r
+ verifyClassExist(javaClassName);\r
+ /*\r
+ ** Get the parameter type names out of the parameters and put them\r
+ ** in an array.\r
+ */\r
+ String[] parmTypeNames = getObjectSignature();\r
+ boolean[] isParam = getIsParam();\r
+ ClassInspector classInspector = getClassFactory().getClassInspector();\r
+\r
+ /*\r
+ ** Find the matching constructor.\r
+ */\r
+ try\r
+ {\r
+ /* First try with built-in types and mappings */\r
+ method = classInspector.findPublicConstructor(javaClassName,\r
+ parmTypeNames, null, isParam);\r
+\r
+ /* If no match, then retry to match any possible combinations of\r
+ * object and primitive types.\r
+ */\r
+ if (method == null)\r
+ {\r
+ String[] primParmTypeNames = getPrimitiveSignature(false);\r
+ method = classInspector.findPublicConstructor(javaClassName,\r
+ parmTypeNames, primParmTypeNames, isParam);\r
+ }\r
+ }\r
+ catch (ClassNotFoundException e)\r
+ {\r
+ /*\r
+ ** If one of the classes couldn't be found, just act like the\r
+ ** method couldn't be found. The error lists all the class names,\r
+ ** which should give the user enough info to diagnose the problem.\r
+ */\r
+ method = null;\r
+ }\r
+\r
+ if (method == null)\r
+ {\r
+ /* Put the parameter type names into a single string */\r
+ String parmTypes = "";\r
+ for (int i = 0; i < parmTypeNames.length; i++)\r
+ {\r
+ if (i != 0)\r
+ parmTypes += ", ";\r
+ parmTypes += (parmTypeNames[i].length() != 0 ?\r
+ parmTypeNames[i] :\r
+ MessageService.getTextMessage(\r
+ SQLState.LANG_UNTYPED)\r
+ );\r
+ }\r
+\r
+ throw StandardException.newException(SQLState.LANG_NO_CONSTRUCTOR_FOUND, \r
+ javaClassName,\r
+ parmTypes);\r
+ }\r
+\r
+ methodParameterTypes = classInspector.getParameterTypes(method);\r
+\r
+ for (int i = 0; i < methodParameterTypes.length; i++)\r
+ {\r
+ if (classInspector.primitiveType(methodParameterTypes[i]))\r
+ methodParms[i].castToPrimitive(true);\r
+ }\r
+\r
+ /* Set type info for any null parameters */\r
+ if ( someParametersAreNull() )\r
+ {\r
+ setNullParameterInfo(methodParameterTypes);\r
+ }\r
+\r
+ /* Constructor always returns an object of type javaClassName */\r
+ if (SanityManager.DEBUG) {\r
+ SanityManager.ASSERT(javaClassName.equals(classInspector.getType(method)),\r
+ "Constructor is wrong type, expected " + javaClassName + \r
+ " actual is " + classInspector.getType(method));\r
+ }\r
+ setJavaTypeName( javaClassName );\r
+ if (routineInfo != null)\r
+ {\r
+ TypeDescriptor returnType = routineInfo.getReturnType();\r
+ if (returnType != null)\r
+ {\r
+ setCollationType(returnType.getCollationType());\r
+ }\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 new opeators 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
+ return pushable;\r
+ }\r
+\r
+ /**\r
+ * Build a JBitSet of all of the tables that we are\r
+ * correlated with.\r
+ *\r
+ * @param correlationMap The JBitSet of the tables that we are correlated with.\r
+ */\r
+ void getCorrelationTables(JBitSet correlationMap)\r
+ throws StandardException\r
+ {\r
+ CollectNodesVisitor getCRs = new CollectNodesVisitor(ColumnReference.class);\r
+ super.accept(getCRs);\r
+ Vector colRefs = getCRs.getList();\r
+ for (Enumeration e = colRefs.elements(); e.hasMoreElements(); )\r
+ {\r
+ ColumnReference ref = (ColumnReference)e.nextElement();\r
+ if (ref.getCorrelated())\r
+ {\r
+ correlationMap.set(ref.getTableNumber());\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Is this class assignable to the specified class?\r
+ * This is useful for the VTI interface where we want to see\r
+ * if the class implements java.sql.ResultSet.\r
+ *\r
+ * @param toClassName The java class name we want to assign to\r
+ *\r
+ * @return boolean Whether or not this class is assignable to\r
+ * the specified class\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ protected boolean assignableTo(String toClassName) throws StandardException\r
+ {\r
+ ClassInspector classInspector = getClassFactory().getClassInspector();\r
+ return classInspector.assignableTo(javaClassName, toClassName);\r
+ }\r
+\r
+\r
+ /**\r
+ * Is this class have a public method with the specified signiture\r
+ * This is useful for the VTI interface where we want to see\r
+ * if the class has the option static method for returning the\r
+ * ResultSetMetaData.\r
+ *\r
+ * @param methodName The method name we are looking for\r
+ * @param staticMethod Whether or not the method we are looking for is static\r
+ *\r
+ * @return Member The Member representing the method (or null\r
+ * if the method doesn't exist).\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ protected Member findPublicMethod(String methodName, boolean staticMethod)\r
+ throws StandardException\r
+ {\r
+ Member publicMethod;\r
+\r
+ /*\r
+ ** Get the parameter type names out of the parameters and put them\r
+ ** in an array.\r
+ */\r
+ String[] parmTypeNames = getObjectSignature();\r
+ boolean[] isParam = getIsParam();\r
+\r
+ ClassInspector classInspector = getClassFactory().getClassInspector();\r
+\r
+ try\r
+ {\r
+ publicMethod = classInspector.findPublicMethod(javaClassName, methodName,\r
+ parmTypeNames, null, isParam, staticMethod, false);\r
+\r
+ /* If no match, then retry to match any possible combinations of\r
+ * object and primitive types.\r
+ */\r
+ if (publicMethod == null)\r
+ {\r
+ String[] primParmTypeNames = getPrimitiveSignature(false);\r
+ publicMethod = classInspector.findPublicMethod(javaClassName, \r
+ methodName, parmTypeNames,\r
+ primParmTypeNames, isParam, staticMethod, false);\r
+ }\r
+ }\r
+ catch (ClassNotFoundException e)\r
+ {\r
+ /* We should always be able to find the class at this point\r
+ * since the protocol is to check to see if it exists\r
+ * before checking for a method off of it. Anyway, just return\r
+ * null if the class doesn't exist, since the method doesn't\r
+ * exist in that case.\r
+ */\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.THROWASSERT("Unexpected exception", e);\r
+ }\r
+ return null;\r
+ }\r
+\r
+ return publicMethod;\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
+ /* If this node is for an ungrouped aggregator, \r
+ * then we generate a conditional\r
+ * wrapper so that we only new the aggregator once.\r
+ * (fx == null) ? fx = new ... : fx\r
+ */\r
+ LocalField objectFieldLF = null;\r
+ if (singleInstantiation)\r
+ {\r
+ /* Declare the field */\r
+ objectFieldLF = acb.newFieldDeclaration(Modifier.PRIVATE, javaClassName);\r
+\r
+ // now we fill in the body of the conditional\r
+\r
+ mb.getField(objectFieldLF);\r
+ mb.conditionalIfNull();\r
+ }\r
+\r
+ mb.pushNewStart(javaClassName);\r
+ int nargs = generateParameters(acb, mb);\r
+ mb.pushNewComplete(nargs);\r
+\r
+ if (singleInstantiation) {\r
+\r
+ mb.putField(objectFieldLF);\r
+ mb.startElseCode();\r
+ mb.getField(objectFieldLF);\r
+ mb.completeConditional();\r
+ }\r
+ }\r
+}\r