--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.sql.compile.ActivationClassBuilder\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.ClassBuilder;\r
+import org.apache.derby.iapi.services.compiler.MethodBuilder;\r
+import org.apache.derby.iapi.services.compiler.LocalField;\r
+import org.apache.derby.iapi.reference.ClassName;\r
+\r
+\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+\r
+import org.apache.derby.iapi.sql.compile.CompilerContext;\r
+import org.apache.derby.iapi.sql.compile.CodeGeneration;\r
+\r
+import org.apache.derby.iapi.sql.execute.CursorResultSet;\r
+\r
+import org.apache.derby.iapi.sql.ResultSet;\r
+\r
+import org.apache.derby.iapi.sql.execute.ExecRow;\r
+\r
+import org.apache.derby.iapi.types.DataTypeDescriptor;\r
+import org.apache.derby.iapi.types.TypeId;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+\r
+import org.apache.derby.iapi.services.loader.GeneratedMethod;\r
+import org.apache.derby.iapi.services.classfile.VMOpcode;\r
+\r
+import java.lang.reflect.Modifier;\r
+\r
+import java.io.PrintWriter;\r
+\r
+import java.io.File;\r
+import java.io.FileOutputStream;\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.IOException;\r
+import java.util.Hashtable;\r
+\r
+/**\r
+ * ActivationClassBuilder\r
+ * provides an interface to satisfy generation's\r
+ * common tasks in building an activation class,\r
+ * as well as a repository for the JavaFactory used\r
+ * to generate the basic language constructs for the methods in the class.\r
+ * Common tasks include the setting of a static field for each\r
+ * expression function that gets added, the creation\r
+ * of the execute method that gets expanded as the query tree\r
+ * is walked, setting the superclass.\r
+ * <p>\r
+ * An activation class is defined for each statement. It has\r
+ * the following basic layout: TBD\r
+ * See the document\r
+ * \\Jeeves\Unversioned Repository 1\Internal Technical Documents\Other\GenAndExec.doc\r
+ * for details.\r
+ * <p>\r
+ * We could also verify methods as they are\r
+ * added, to have 0 parameters, ...\r
+ *\r
+ */\r
+class ActivationClassBuilder extends ExpressionClassBuilder\r
+{\r
+ ///////////////////////////////////////////////////////////////////////\r
+ //\r
+ // CONSTANTS\r
+ //\r
+ ///////////////////////////////////////////////////////////////////////\r
+\r
+ ///////////////////////////////////////////////////////////////////////\r
+ //\r
+ // STATE\r
+ //\r
+ ///////////////////////////////////////////////////////////////////////\r
+\r
+ private LocalField targetResultSetField;\r
+ private LocalField cursorResultSetField;\r
+\r
+ private MethodBuilder closeActivationMethod;\r
+\r
+\r
+ ///////////////////////////////////////////////////////////////////////\r
+ //\r
+ // CONSTRUCTOR\r
+ //\r
+ ///////////////////////////////////////////////////////////////////////\r
+\r
+ /**\r
+ * By the time this is done, it has constructed the following class:\r
+ * <pre>\r
+ * public class #className extends #superClass {\r
+ * // public void reset() { return; }\r
+ * public ResultSet execute() throws StandardException {\r
+ * throwIfClosed("execute");\r
+ * // statements must be added here\r
+ * }\r
+ * public #className() { super(); }\r
+ * }\r
+ * </pre>\r
+ *\r
+ * @exception StandardException thrown on failure\r
+ */\r
+ ActivationClassBuilder (String superClass, CompilerContext cc) throws StandardException\r
+ {\r
+ super( superClass, (String) null, cc );\r
+ executeMethod = beginExecuteMethod();\r
+ }\r
+\r
+ ///////////////////////////////////////////////////////////////////////\r
+ //\r
+ // ACCESSORS\r
+ //\r
+ ///////////////////////////////////////////////////////////////////////\r
+\r
+ /**\r
+ * Get the package name that this generated class lives in\r
+ *\r
+ * @return package name\r
+ */\r
+ public String getPackageName()\r
+ { return CodeGeneration.GENERATED_PACKAGE_PREFIX; }\r
+\r
+ /**\r
+ The base class for activations is BaseActivation\r
+ */\r
+ String getBaseClassName() {\r
+ return ClassName.BaseActivation;\r
+ }\r
+\r
+\r
+ /**\r
+ * Get the number of ExecRows to allocate\r
+ *\r
+ * @exception StandardException thrown on failure\r
+ * @return package name\r
+ */\r
+ public int getRowCount()\r
+ throws StandardException\r
+ {\r
+ return myCompCtx.getNumResultSets();\r
+ }\r
+\r
+ /**\r
+ * Generate the assignment for numSubqueries = x\r
+ *\r
+ * @exception StandardException thrown on failure\r
+ */\r
+ public void setNumSubqueries()\r
+ {\r
+ int numSubqueries = myCompCtx.getNumSubquerys();\r
+\r
+ // If there are no subqueries then\r
+ // the field is set to the correctly\r
+ // value (0) by java.\r
+ if (numSubqueries == 0)\r
+ return;\r
+\r
+ /* Generated code is:\r
+ * numSubqueries = x;\r
+ */\r
+ constructor.pushThis();\r
+ constructor.push(numSubqueries);\r
+ constructor.putField(ClassName.BaseActivation, "numSubqueries", "int");\r
+ constructor.endStatement();\r
+ }\r
+\r
+\r
+ ///////////////////////////////////////////////////////////////////////\r
+ //\r
+ // EXECUTE METHODS\r
+ //\r
+ ///////////////////////////////////////////////////////////////////////\r
+\r
+ /**\r
+ * By the time this is done, it has generated the following code\r
+ * <pre>\r
+ * public ResultSet execute() throws StandardException {\r
+ * throwIfClosed("execute");\r
+ * // statements must be added here\r
+ * }\r
+ * }\r
+ * </pre>\r
+ *\r
+ * @exception StandardException thrown on failure\r
+ */\r
+ private MethodBuilder beginExecuteMethod()\r
+ throws StandardException\r
+ {\r
+ // create a reset method that does nothing.\r
+ // REVISIT: this might better belong in the Activation\r
+ // superclasses ?? not clear yet what it needs to do.\r
+\r
+ // don't yet need a reset method here. when we do,\r
+ // it will need to call super.reset() as well as\r
+ // whatever it does.\r
+ // mb = cb.newMethodBuilder(\r
+ // Modifier.PUBLIC, "void", "reset");\r
+ // mb.addStatement(javaFac.newStatement(\r
+ // javaFac.newSpecialMethodCall(\r
+ // thisExpression(),\r
+ // BaseActivation.CLASS_NAME,\r
+ // "reset", "void")));\r
+ // mb.addStatement(javaFac.newReturnStatement());\r
+ // mb.complete(); // there is nothing else.\r
+\r
+\r
+ // This method is an implementation of the interface method\r
+ // Activation - ResultSet execute()\r
+\r
+ // create an empty execute method\r
+ MethodBuilder mb = cb.newMethodBuilder(Modifier.PUBLIC,\r
+ ClassName.ResultSet, "execute");\r
+ mb.addThrownException(ClassName.StandardException);\r
+\r
+ // put a 'throwIfClosed("execute");' statement into the execute method.\r
+ mb.pushThis(); // instance\r
+ mb.push("execute");\r
+ mb.callMethod(VMOpcode.INVOKEVIRTUAL, ClassName.BaseActivation, "throwIfClosed", "void", 1);\r
+\r
+ // call this.startExecution(), so the parent class can know an execution\r
+ // has begun.\r
+\r
+ mb.pushThis(); // instance\r
+ mb.callMethod(VMOpcode.INVOKEVIRTUAL, ClassName.BaseActivation, "startExecution", "void", 0);\r
+\r
+ return mb;\r
+ }\r
+\r
+ MethodBuilder startResetMethod() {\r
+ MethodBuilder mb = cb.newMethodBuilder(Modifier.PUBLIC,\r
+ "void", "reset");\r
+\r
+ mb.addThrownException(ClassName.StandardException);\r
+ mb.pushThis();\r
+ mb.callMethod(VMOpcode.INVOKESPECIAL, ClassName.BaseActivation, "reset", "void", 0);\r
+\r
+\r
+ return mb;\r
+ }\r
+\r
+ /**\r
+ * An execute method always ends in a return statement, returning\r
+ * the result set that has been constructed. We want to\r
+ * do some bookkeeping on that statement, so we generate\r
+ * the return given the result set.\r
+\r
+ Upon entry the only word on the stack is the result set expression\r
+ */\r
+ void finishExecuteMethod(boolean genMarkAsTopNode) {\r
+\r
+ /* We only call markAsTopResultSet() for selects.\r
+ * Non-select DML marks the top NoPutResultSet in the constructor.\r
+ * Needed for closing down resultSet on an error.\r
+ */\r
+ if (genMarkAsTopNode)\r
+ {\r
+ // dup the result set to leave one for the return and one for this call\r
+ executeMethod.dup();\r
+ executeMethod.cast(ClassName.NoPutResultSet);\r
+ executeMethod.callMethod(VMOpcode.INVOKEINTERFACE, (String) null, "markAsTopResultSet", "void", 0);\r
+ }\r
+\r
+ /* return resultSet */\r
+ executeMethod.methodReturn();\r
+ executeMethod.complete();\r
+\r
+ getClassBuilder().newFieldWithAccessors("getExecutionCount", "setExecutionCount",\r
+ Modifier.PROTECTED, true, "int");\r
+\r
+ getClassBuilder().newFieldWithAccessors("getRowCountCheckVector", "setRowCountCheckVector",\r
+ Modifier.PROTECTED, true, "java.util.Vector");\r
+\r
+ getClassBuilder().newFieldWithAccessors("getStalePlanCheckInterval", "setStalePlanCheckInterval",\r
+ Modifier.PROTECTED, true, "int");\r
+\r
+ if (closeActivationMethod != null) {\r
+ closeActivationMethod.methodReturn();\r
+ closeActivationMethod.complete();\r
+ }\r
+ }\r
+\r
+ ///////////////////////////////////////////////////////////////////////\r
+ //\r
+ // CURSOR SUPPORT\r
+ //\r
+ ///////////////////////////////////////////////////////////////////////\r
+\r
+ /**\r
+ * Updatable cursors\r
+ * need to add a getter method for use in BaseActivation to access\r
+ * the result set that identifies target rows for a positioned\r
+ * update or delete.\r
+ * <p>\r
+ * The code that is generated is:\r
+ * <pre><verbatim>\r
+ * public CursorResultSet getTargetResultSet() {\r
+ * return targetResultSet;\r
+ * }\r
+ *\r
+ * public CursorResultSet getCursorResultSet() {\r
+ * return cursorResultSet;\r
+ * }\r
+ * </verbatim></pre>\r
+ *\r
+ */\r
+ void addCursorPositionCode() {\r
+\r
+ // the getter\r
+ // This method is an implementation of the interface method\r
+ // CursorActivation - CursorResultSet getTargetResultSet()\r
+ MethodBuilder getter = cb.newMethodBuilder(Modifier.PUBLIC, \r
+ ClassName.CursorResultSet, "getTargetResultSet");\r
+\r
+ getter.getField(targetResultSetField);\r
+ getter.methodReturn();\r
+ getter.complete();\r
+\r
+ // This method is an implementation of the interface method\r
+ // CursorActivation - CursorResultSet getCursorResultSet()\r
+\r
+ getter = cb.newMethodBuilder(Modifier.PUBLIC, \r
+ ClassName.CursorResultSet, "getCursorResultSet");\r
+\r
+ getter.getField(cursorResultSetField);\r
+ getter.methodReturn();\r
+ getter.complete();\r
+ }\r
+\r
+ /**\r
+ * Updatable cursors\r
+ * need to add a field and its initialization\r
+ * for use in BaseActivation to access the result set that\r
+ * identifies target rows for a positioned update or delete.\r
+ * <p>\r
+ * The code that is generated is:\r
+ * <pre><verbatim>\r
+ * private CursorResultSet targetResultSet;\r
+ *\r
+ * </verbatim></pre>\r
+ *\r
+ * The expression that is generated is:\r
+ * <pre><verbatim>\r
+ * (ResultSet) (targetResultSet = (CursorResultSet) #expression#)\r
+ * </verbatim></pre>\r
+ *\r
+ */\r
+ void rememberCursorTarget(MethodBuilder mb) {\r
+\r
+ // the field\r
+ targetResultSetField = cb.addField(ClassName.CursorResultSet,\r
+ "targetResultSet",\r
+ Modifier.PRIVATE);\r
+\r
+ mb.cast(ClassName.CursorResultSet);\r
+ mb.putField(targetResultSetField);\r
+ mb.cast(ClassName.NoPutResultSet);\r
+ }\r
+\r
+ /**\r
+ * Updatable cursors\r
+ * need to add a field and its initialization\r
+ * for use in BaseActivation to access the result set that\r
+ * identifies cursor result rows for a positioned update or delete.\r
+ * <p>\r
+ * The code that is generated is:\r
+ * <pre><verbatim>\r
+ * private CursorResultSet cursorResultSet;\r
+ *\r
+ * </verbatim></pre>\r
+ *\r
+ * The expression that is generated is:\r
+ * <pre><verbatim>\r
+ * (ResultSet) (cursorResultSet = (CursorResultSet) #expression#)\r
+ * </verbatim></pre>\r
+\r
+ The expression must be the top stack word when this method is called.\r
+ *\r
+ */\r
+ void rememberCursor(MethodBuilder mb) {\r
+\r
+ // the field\r
+ cursorResultSetField = cb.addField(ClassName.CursorResultSet,\r
+ "cursorResultSet",\r
+ Modifier.PRIVATE);\r
+\r
+ mb.cast(ClassName.CursorResultSet);\r
+ mb.putField(cursorResultSetField);\r
+ mb.cast(ClassName.ResultSet);\r
+ }\r
+\r
+ ///////////////////////////////////////////////////////////////////////\r
+ //\r
+ // CURRENT DATE/TIME SUPPORT\r
+ //\r
+ ///////////////////////////////////////////////////////////////////////\r
+\r
+ /*\r
+ The first time a current datetime is needed, create the class\r
+ level support for it. The first half of the logic is in our parent\r
+ class.\r
+ */\r
+ protected LocalField getCurrentSetup()\r
+ {\r
+ if (cdtField != null) return cdtField;\r
+\r
+ LocalField lf = super.getCurrentSetup();\r
+\r
+ // 3) the execute method gets a statement (prior to the return)\r
+ // to tell cdt to restart:\r
+ // cdt.forget();\r
+\r
+ executeMethod.getField(lf);\r
+ executeMethod.callMethod(VMOpcode.INVOKEVIRTUAL, (String) null, "forget", "void", 0);\r
+\r
+ return lf;\r
+ }\r
+\r
+ MethodBuilder getCloseActivationMethod() {\r
+\r
+ if (closeActivationMethod == null) {\r
+ closeActivationMethod = cb.newMethodBuilder(Modifier.PUBLIC, "void", "closeActivationAction");\r
+ closeActivationMethod.addThrownException("java.lang.Exception");\r
+ }\r
+ return closeActivationMethod;\r
+ }\r
+}\r
+\r