--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.sql.execute.BaseActivation\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.execute;\r
+\r
+import java.sql.Connection;\r
+import java.sql.SQLException;\r
+import java.sql.SQLWarning;\r
+import java.util.ArrayList;\r
+import java.util.HashSet;\r
+import java.util.Hashtable;\r
+import java.util.Vector;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+import org.apache.derby.iapi.jdbc.ConnectionContext;\r
+import org.apache.derby.iapi.reference.Property;\r
+import org.apache.derby.iapi.reference.SQLState;\r
+import org.apache.derby.iapi.services.context.Context;\r
+import org.apache.derby.iapi.services.context.ContextManager;\r
+import org.apache.derby.iapi.services.io.FormatableBitSet;\r
+import org.apache.derby.iapi.services.loader.GeneratedByteCode;\r
+import org.apache.derby.iapi.services.loader.GeneratedClass;\r
+import org.apache.derby.iapi.services.loader.GeneratedMethod;\r
+import org.apache.derby.iapi.services.property.PropertyUtil;\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+import org.apache.derby.iapi.sql.Activation;\r
+import org.apache.derby.iapi.sql.ParameterValueSet;\r
+import org.apache.derby.iapi.sql.ResultDescription;\r
+import org.apache.derby.iapi.sql.ResultSet;\r
+import org.apache.derby.iapi.sql.compile.Optimizer;\r
+import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;\r
+import org.apache.derby.iapi.sql.depend.DependencyManager;\r
+import org.apache.derby.iapi.sql.dictionary.TableDescriptor;\r
+import org.apache.derby.iapi.sql.execute.ConstantAction;\r
+import org.apache.derby.iapi.sql.execute.CursorActivation;\r
+import org.apache.derby.iapi.sql.execute.CursorResultSet;\r
+import org.apache.derby.iapi.sql.execute.ExecPreparedStatement;\r
+import org.apache.derby.iapi.sql.execute.ExecRow;\r
+import org.apache.derby.iapi.sql.execute.ExecutionFactory;\r
+import org.apache.derby.iapi.sql.execute.NoPutResultSet;\r
+import org.apache.derby.iapi.sql.execute.ResultSetFactory;\r
+import org.apache.derby.iapi.sql.execute.TemporaryRowHolder;\r
+import org.apache.derby.iapi.store.access.ConglomerateController;\r
+import org.apache.derby.iapi.store.access.Qualifier;\r
+import org.apache.derby.iapi.store.access.ScanController;\r
+import org.apache.derby.iapi.store.access.TransactionController;\r
+import org.apache.derby.iapi.types.DataTypeDescriptor;\r
+import org.apache.derby.iapi.types.DataValueDescriptor;\r
+import org.apache.derby.iapi.types.DataValueFactory;\r
+import org.apache.derby.iapi.types.NumberDataValue;\r
+import org.apache.derby.iapi.types.RowLocation;\r
+import org.apache.derby.iapi.util.ReuseFactory;\r
+\r
+/**\r
+ * BaseActivation\r
+ * provides the fundamental support we expect all activations to have.\r
+ * Doesn't actually implement any of the activation interface,\r
+ * expects the subclasses to do that.\r
+ */\r
+public abstract class BaseActivation implements CursorActivation, GeneratedByteCode\r
+\r
+{\r
+ private LanguageConnectionContext lcc;\r
+ protected ContextManager cm;\r
+\r
+ protected ExecPreparedStatement preStmt;\r
+ protected ResultSet resultSet;\r
+ protected ResultDescription resultDescription;\r
+ protected boolean closed;\r
+ private String cursorName;\r
+ \r
+ protected int numSubqueries;\r
+\r
+ private boolean singleExecution;\r
+\r
+ // This flag is declared volatile to ensure it is \r
+ // visible when it has been modified by the finalizer thread.\r
+ private volatile boolean inUse;\r
+\r
+ private java.sql.ResultSet targetVTI;\r
+ private SQLWarning warnings;\r
+\r
+ private GeneratedClass gc; // my Generated class object.\r
+\r
+ private boolean checkRowCounts;\r
+ private HashSet rowCountsCheckedThisExecution = new HashSet(4, 0.9f);\r
+\r
+ private static final long MAX_SQRT = (long) Math.sqrt(Long.MAX_VALUE);\r
+\r
+ // When the row count exceeds this number, we should recompile if\r
+ // the difference in row counts is greater than 10%. If it's less\r
+ // than this number, we use an entirely different technique to check\r
+ // for recompilation. See comments below, in informOfRowCount()\r
+ private static final int TEN_PERCENT_THRESHOLD = 400;\r
+\r
+ /* Performance optimization for update/delete - only\r
+ * open heap ConglomerateController once when doing\r
+ * index row to base row on search\r
+ */\r
+ private ConglomerateController updateHeapCC;\r
+ private ScanController indexSC;\r
+ private long indexConglomerateNumber = -1;\r
+\r
+ private TableDescriptor ddlTableDescriptor;\r
+\r
+ private int maxRows = -1;\r
+ private boolean forCreateTable;\r
+\r
+ private boolean scrollable;\r
+\r
+ private boolean resultSetHoldability;\r
+\r
+ //beetle 3865: updateable cursor using index. A way of communication\r
+ //between cursor activation and update activation.\r
+ private CursorResultSet forUpdateIndexScan;\r
+\r
+ //Following three are used for JDBC3.0 auto-generated keys feature.\r
+ //autoGeneratedKeysResultSetMode will be set true if at the time of statement execution,\r
+ //either Statement.RETURN_GENERATED_KEYS was passed or an array of (column positions or\r
+ //column names) was passed\r
+ private boolean autoGeneratedKeysResultSetMode;\r
+ private int[] autoGeneratedKeysColumnIndexes ;\r
+ private String[] autoGeneratedKeysColumnNames ;\r
+\r
+ //Following is the position of the session table names list in savedObjects in compiler context\r
+ //This is updated to be the correct value at cursor generate time if the cursor references any session table names.\r
+ //If the cursor does not reference any session table names, this will stay negative\r
+ protected int indexOfSessionTableNamesInSavedObjects = -1;\r
+\r
+ // WARNING: these fields are accessed by code generated in the \r
+ // ExpressionClassBuilder: don't change them unless you \r
+ // make the appropriate changes there.\r
+ protected ExecRow[] row;\r
+ protected ParameterValueSet pvs;\r
+\r
+ //\r
+ // constructors\r
+ //\r
+\r
+ protected BaseActivation()\r
+ {\r
+ super();\r
+ }\r
+\r
+ public final void initFromContext(Context context) \r
+ throws StandardException {\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(context!=null, "NULL context passed to BaseActivation.initFromContext");\r
+ }\r
+ this.cm = context.getContextManager();\r
+\r
+ lcc = (LanguageConnectionContext) cm.getContext(LanguageConnectionContext.CONTEXT_ID);\r
+\r
+ if (SanityManager.DEBUG) {\r
+ if (lcc == null)\r
+ SanityManager.THROWASSERT("lcc is null in activation type " + getClass());\r
+ }\r
+\r
+ // mark in use\r
+ inUse = true;\r
+ \r
+ // add this activation to the pool for the connection.\r
+ lcc.addActivation(this);\r
+ }\r
+\r
+\r
+ //\r
+ // Activation interface\r
+ //\r
+\r
+ public final ExecPreparedStatement getPreparedStatement() {\r
+ return preStmt;\r
+ }\r
+\r
+ public ConstantAction getConstantAction() {\r
+ return preStmt.getConstantAction();\r
+ }\r
+\r
+\r
+ public final void checkStatementValidity() throws StandardException {\r
+\r
+ if (preStmt == null)\r
+ return;\r
+\r
+ synchronized (preStmt) {\r
+\r
+ if ((gc == preStmt.getActivationClass()) && preStmt.upToDate())\r
+ return;\r
+ }\r
+\r
+ StandardException se = StandardException.newException(SQLState.LANG_STATEMENT_NEEDS_RECOMPILE);\r
+ se.setReport(StandardException.REPORT_NEVER);\r
+ throw se;\r
+ }\r
+\r
+ /**\r
+ Link this activation with its PreparedStatement.\r
+ It can be called with null to break the link with the\r
+ PreparedStatement.\r
+\r
+ */\r
+ public final void setupActivation(ExecPreparedStatement ps, boolean scrollable) \r
+ throws StandardException {\r
+ preStmt = ps;\r
+ \r
+ if (ps != null) {\r
+ // get the result set description\r
+ resultDescription = ps.getResultDescription();\r
+ this.scrollable = scrollable;\r
+ \r
+ // Initialize the parameter set to have allocated\r
+ // DataValueDescriptor objects for each parameter.\r
+ if (pvs != null && pvs.getParameterCount() != 0)\r
+ pvs.initialize(ps.getParameterTypes());\r
+\r
+ } else {\r
+ resultDescription = null;\r
+ this.scrollable = false;\r
+ }\r
+ }\r
+\r
+ public ResultSet getResultSet() {\r
+ return resultSet;\r
+ }\r
+\r
+ /**\r
+ Get the saved RowLocation.\r
+\r
+ @param itemNumber The saved item number.\r
+\r
+ @return A RowLocation template for the conglomerate\r
+ */\r
+ public RowLocation getRowLocationTemplate(int itemNumber)\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(itemNumber >= 0,\r
+ "itemNumber expected to be >= 0");\r
+ if (! (getPreparedStatement().getSavedObject(itemNumber) instanceof RowLocation))\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ "getPreparedStatement().getSavedObject(itemNumber) expected to be " +\r
+ "instance of RowLocation, not " +\r
+ getPreparedStatement().getSavedObject(itemNumber).getClass().getName() +\r
+ ", query is " + getPreparedStatement().getSource());\r
+ }\r
+ RowLocation rl = (RowLocation) getPreparedStatement().getSavedObject(itemNumber);\r
+ if (! (rl.cloneObject() instanceof RowLocation))\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ "rl.cloneObject() expected to be " +\r
+ "instance of RowLocation, not " +\r
+ rl.getClass().getName() +\r
+ ", query is " + getPreparedStatement().getSource());\r
+ }\r
+ }\r
+ /* We have to return a clone of the saved RowLocation due\r
+ * to the shared cache of SPSs.\r
+ */\r
+ return (RowLocation)\r
+ ((RowLocation)(getPreparedStatement().getSavedObject(itemNumber))).cloneObject();\r
+ }\r
+\r
+ /*\r
+ */\r
+ public ResultDescription getResultDescription() {\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(resultDescription != null, "Must have a result description");\r
+ return resultDescription;\r
+ }\r
+\r
+ /**\r
+ This is a partial implementation of reset.\r
+ Subclasses will want to reset information\r
+ they are aware of, such as parameters.\r
+ <p>\r
+ All subclasses must call super.reset() and\r
+ then do their cleanup.\r
+ <p>\r
+ The execute call must set the resultSet field\r
+ to be the resultSet that it has returned.\r
+\r
+ @exception StandardException on error\r
+ */\r
+ public void reset() throws StandardException\r
+ {\r
+ if (resultSet != null) \r
+ resultSet.close();\r
+ \r
+ updateHeapCC = null;\r
+ // REMIND: do we need to get them to stop input as well?\r
+\r
+ if (!isSingleExecution())\r
+ clearWarnings();\r
+ }\r
+\r
+ /**\r
+ Closing an activation marks it as unusable. Any other\r
+ requests made on it will fail. An activation should be\r
+ marked closed when it is expected to not be used any longer,\r
+ i.e. when the connection for it is closed, or it has suffered some\r
+ sort of severe error.\r
+\r
+ This should also remove it from the language connection context.\r
+\r
+ @exception StandardException on error\r
+ */\r
+ public final void close() throws StandardException \r
+ {\r
+ if (! closed) { \r
+ \r
+ // markUnused();\r
+\r
+ // we call reset so that if the actual type of "this"\r
+ // is a subclass of BaseActivation, its cleanup will\r
+ // also happen -- reset in the actual type is called,\r
+ // not reset in BaseActivation. Subclass reset's\r
+ // are supposed to call super.reset() as well.\r
+ reset(); // get everything related to executing released\r
+\r
+ if (resultSet != null)\r
+ {\r
+ // Finish the resultSet, it will never be used again.\r
+ resultSet.finish();\r
+ resultSet = null;\r
+ }\r
+\r
+ closed = true;\r
+\r
+ LanguageConnectionContext lcc = getLanguageConnectionContext();\r
+\r
+ lcc.removeActivation(this);\r
+ if (preStmt != null) {\r
+ preStmt.finish(lcc);\r
+ preStmt = null;\r
+ }\r
+\r
+ try {\r
+ closeActivationAction();\r
+ } catch (Throwable e) {\r
+ throw StandardException.plainWrapException(e);\r
+ }\r
+\r
+ }\r
+ \r
+ }\r
+\r
+ /**\r
+ A generated class can create its own closeActivationAction\r
+ method to invoke special logic when the activation is closed.\r
+ */\r
+ protected void closeActivationAction() throws Exception {\r
+ // no code to be added here as generated code\r
+ // will not call super.closeActivationAction()\r
+ }\r
+\r
+ /**\r
+ Find out if the activation closed or not.\r
+ @return true if the prepared statement has been closed.\r
+ */\r
+ public boolean isClosed() {\r
+ return closed;\r
+ }\r
+\r
+ /**\r
+ Set this Activation for a single execution.\r
+\r
+ @see Activation#setSingleExecution\r
+ */\r
+ public void setSingleExecution() {\r
+ singleExecution = true;\r
+ }\r
+\r
+ /**\r
+ Returns true if this Activation is only going to be used for\r
+ one execution.\r
+\r
+ @see Activation#isSingleExecution\r
+ */\r
+ public boolean isSingleExecution() {\r
+ return singleExecution;\r
+ }\r
+\r
+ /**\r
+ Get the number of subqueries in the entire query.\r
+ @return int The number of subqueries in the entire query.\r
+ */\r
+ public int getNumSubqueries() {\r
+ return numSubqueries;\r
+ }\r
+\r
+ /**\r
+ * @see Activation#isCursorActivation\r
+ */\r
+ public boolean isCursorActivation()\r
+ {\r
+ return false;\r
+ }\r
+\r
+ //\r
+ // GeneratedByteCode interface\r
+ //\r
+\r
+ public final void setGC(GeneratedClass gc) {\r
+ this.gc = gc;\r
+ }\r
+\r
+ public final GeneratedClass getGC() {\r
+\r
+ if (SanityManager.DEBUG) {\r
+ if (gc == null)\r
+ SanityManager.THROWASSERT("move code requiring GC to postConstructor() method!!");\r
+ }\r
+ return gc;\r
+ }\r
+\r
+ public final GeneratedMethod getMethod(String methodName) throws StandardException {\r
+\r
+ return getGC().getMethod(methodName);\r
+ }\r
+ public Object e0() throws StandardException { return null; } \r
+ public Object e1() throws StandardException { return null; }\r
+ public Object e2() throws StandardException { return null; }\r
+ public Object e3() throws StandardException { return null; }\r
+ public Object e4() throws StandardException { return null; } \r
+ public Object e5() throws StandardException { return null; }\r
+ public Object e6() throws StandardException { return null; }\r
+ public Object e7() throws StandardException { return null; }\r
+ public Object e8() throws StandardException { return null; } \r
+ public Object e9() throws StandardException { return null; }\r
+\r
+ //\r
+ // class interface\r
+ //\r
+\r
+ /**\r
+ * Temporary tables can be declared with ON COMMIT DELETE ROWS. But if the table has a held curosr open at\r
+ * commit time, data should not be deleted from the table. This method, (gets called at commit time) checks if this\r
+ * activation held cursor and if so, does that cursor reference the passed temp table name.\r
+ *\r
+ * @return true if this activation has held cursor and if it references the passed temp table name\r
+ */\r
+ public boolean checkIfThisActivationHasHoldCursor(String tableName)\r
+ {\r
+ if (!inUse)\r
+ return false;\r
+\r
+ if (resultSetHoldability == false) //if this activation is not held over commit, do not need to worry about it\r
+ return false;\r
+\r
+ if (indexOfSessionTableNamesInSavedObjects == -1) //if this activation does not refer to session schema tables, do not need to worry about it\r
+ return false;\r
+\r
+ /* is there an open result set? */\r
+ if ((resultSet != null) && !resultSet.isClosed() && resultSet.returnsRows())\r
+ {\r
+ //If we came here, it means this activation is held over commit and it reference session table names\r
+ //Now let's check if it referneces the passed temporary table name which has ON COMMIT DELETE ROWS defined on it.\r
+ return ((ArrayList)getPreparedStatement().getSavedObject(indexOfSessionTableNamesInSavedObjects)).contains(tableName);\r
+ }\r
+\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ remember the cursor name\r
+ */\r
+\r
+ public void setCursorName(String cursorName)\r
+ {\r
+ if (isCursorActivation())\r
+ this.cursorName = cursorName;\r
+ }\r
+\r
+\r
+ /**\r
+ get the cursor name. For something that isn't\r
+ a cursor, this is used as a string name of the\r
+ result set for messages from things like the\r
+ dependency manager.\r
+ <p>\r
+ Activations that do support cursors will override\r
+ this. \r
+ */\r
+ public String getCursorName() {\r
+\r
+ return isCursorActivation() ? cursorName : null;\r
+ }\r
+\r
+ public void setResultSetHoldability(boolean resultSetHoldability)\r
+ {\r
+ this.resultSetHoldability = resultSetHoldability;\r
+ }\r
+\r
+ public boolean getResultSetHoldability()\r
+ {\r
+ return resultSetHoldability;\r
+ }\r
+\r
+ /** @see Activation#setAutoGeneratedKeysResultsetInfo */\r
+ public void setAutoGeneratedKeysResultsetInfo(int[] columnIndexes, String[] columnNames)\r
+ {\r
+ autoGeneratedKeysResultSetMode = true;\r
+ autoGeneratedKeysColumnIndexes = columnIndexes;\r
+ autoGeneratedKeysColumnNames = columnNames;\r
+ }\r
+\r
+ /** @see Activation#getAutoGeneratedKeysResultsetMode */\r
+ public boolean getAutoGeneratedKeysResultsetMode()\r
+ {\r
+ return autoGeneratedKeysResultSetMode;\r
+ }\r
+\r
+ /** @see Activation#getAutoGeneratedKeysColumnIndexes */\r
+ public int[] getAutoGeneratedKeysColumnIndexes()\r
+ {\r
+ return autoGeneratedKeysColumnIndexes;\r
+ }\r
+\r
+ /** @see Activation#getAutoGeneratedKeysColumnNames */\r
+ public String[] getAutoGeneratedKeysColumnNames()\r
+ {\r
+ return autoGeneratedKeysColumnNames;\r
+ }\r
+\r
+ //\r
+ // class implementation\r
+ //\r
+\r
+\r
+ /**\r
+ Used in the execute method of activations for\r
+ generating the result sets that they concatenate together.\r
+ */\r
+ public final ResultSetFactory getResultSetFactory() {\r
+ return getExecutionFactory().getResultSetFactory();\r
+ }\r
+\r
+ /**\r
+ Used in activations for generating rows.\r
+ */\r
+ public final ExecutionFactory getExecutionFactory() {\r
+ return getLanguageConnectionContext().\r
+ getLanguageConnectionFactory().getExecutionFactory();\r
+ }\r
+\r
+\r
+ /**\r
+ Used in CurrentOfResultSet to get to the target result set\r
+ for a cursor. Overridden by activations generated for\r
+ updatable cursors. Those activations capture the target\r
+ result set in a field in their execute() method, and then\r
+ return the value of that field in their version of this method.\r
+\r
+ @return null.\r
+ */\r
+ public CursorResultSet getTargetResultSet() {\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.THROWASSERT("Must be overridden to be used.");\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * Called by generated code to compute the next autoincrement value.\r
+ * \r
+ * @return The next autoincrement value which should be inserted.\r
+ * returns the correct number datatype.\r
+ */\r
+ protected DataValueDescriptor \r
+ getSetAutoincrementValue(int columnPosition, long increment)\r
+ throws StandardException\r
+ {\r
+ DataValueDescriptor l =\r
+ ((InsertResultSet)resultSet).getSetAutoincrementValue(columnPosition, increment);\r
+ return l;\r
+\r
+ }\r
+\r
+ /**\r
+ Used in CurrentOfResultSet to get to the cursor result set\r
+ for a cursor. Overridden by activations generated for\r
+ updatable cursors. Those activations capture the cursor\r
+ result set in a field in their execute() method, and then\r
+ return the value of that field in their version of this method.\r
+\r
+ @return null\r
+ */\r
+ public CursorResultSet getCursorResultSet() {\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.THROWASSERT("Must be overridden to be used.");\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ Various activation methods need to disallow their\r
+ invocation if the activation is closed. This lets them\r
+ check and throw without generating alot of code.\r
+ <p>\r
+ The code to write to generate the call to this is approximately:\r
+ <verbatim>\r
+ // jf is a JavaFactory\r
+ CallableExpression ce = jf.newMethodCall(\r
+ jf.thisExpression(),\r
+ BaseActivation.CLASS_NAME,\r
+ "throwIfClosed",\r
+ "void",\r
+ acb.exprArray(jf.newStringLiteral(...some literal here...)));\r
+\r
+ //mb is a MethodBuilder\r
+ mb.addStatement(jf.newStatement(ce));\r
+ </verbatim>\r
+ The java code to write to call this is:\r
+ <verbatim>\r
+ this.throwIfClosed(...some literal here...);\r
+ </verbatim>\r
+ In both cases, "...some literal here..." gets replaced with\r
+ an expression of type String that evaluates to the name\r
+ of the operation that is being checked, like "execute" or\r
+ "reset".\r
+\r
+ @exception StandardException thrown if closed\r
+ */\r
+ public void throwIfClosed(String op) throws StandardException {\r
+ if (closed)\r
+ throw StandardException.newException(SQLState.LANG_ACTIVATION_CLOSED, op);\r
+ }\r
+\r
+ /**\r
+ * Set a column position in an array of column positions.\r
+ *\r
+ * @param columnPositions The array of column positions\r
+ * @param positionToSet The place to put the column position\r
+ * @param column The column position\r
+ */\r
+ public static void setColumnPosition(\r
+ int[] columnPositions,\r
+ int positionToSet,\r
+ int column)\r
+ {\r
+ columnPositions[positionToSet] = column;\r
+ }\r
+\r
+ /**\r
+ * Allocate an array of qualifiers and initialize in Qualifier[][]\r
+ *\r
+ * @param qualifiers The array of Qualifier arrays.\r
+ * @param position The position in the array to set\r
+ * @param length The array length of the qualifier array to allocate.\r
+ */\r
+ public static void allocateQualArray(\r
+ Qualifier[][] qualifiers,\r
+ int position,\r
+ int length)\r
+ {\r
+ qualifiers[position] = new Qualifier[length];\r
+ }\r
+\r
+\r
+ /**\r
+ * Set a Qualifier in a 2 dimensional array of Qualifiers.\r
+ *\r
+ * Set a single Qualifier into one slot of a 2 dimensional array of \r
+ * Qualifiers. @see Qualifier for detailed description of layout of\r
+ * the 2-d array.\r
+ *\r
+ * @param qualifiers The array of Qualifiers\r
+ * @param qualifier The Qualifier\r
+ * @param position_1 The Nth array index into qualifiers[N][M]\r
+ * @param position_2 The Nth array index into qualifiers[N][M]\r
+ */\r
+ public static void setQualifier(\r
+ Qualifier[][] qualifiers,\r
+ Qualifier qualifier,\r
+ int position_1,\r
+ int position_2)\r
+ {\r
+ qualifiers[position_1][position_2] = qualifier;\r
+ }\r
+\r
+ /**\r
+ * Reinitialize all Qualifiers in an array of Qualifiers.\r
+ *\r
+ * @param qualifiers The array of Qualifiers\r
+ */\r
+ public static void reinitializeQualifiers(Qualifier[][] qualifiers)\r
+ {\r
+ if (qualifiers != null)\r
+ {\r
+ for (int term = 0; term < qualifiers.length; term++)\r
+ {\r
+ for (int i = 0; i < qualifiers[term].length; i++)\r
+ {\r
+ qualifiers[term][i].reinitialize();\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Mark the activation as unused. \r
+ */\r
+ public final void markUnused()\r
+ {\r
+ if(isInUse()) {\r
+ inUse = false;\r
+ lcc.notifyUnusedActivation();\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Is the activation in use?\r
+ *\r
+ * @return true/false\r
+ */\r
+ public final boolean isInUse()\r
+ {\r
+ return inUse;\r
+ }\r
+\r
+ /**\r
+ @see org.apache.derby.iapi.sql.Activation#addWarning\r
+ */\r
+ public void addWarning(SQLWarning w)\r
+ {\r
+ if (warnings == null)\r
+ warnings = w;\r
+ else\r
+ warnings.setNextWarning(w);\r
+ }\r
+\r
+ /**\r
+ @see org.apache.derby.iapi.sql.Activation#getWarnings\r
+ */\r
+ public SQLWarning getWarnings()\r
+ {\r
+ return warnings;\r
+ }\r
+\r
+ /**\r
+ @see org.apache.derby.iapi.sql.Activation#clearWarnings\r
+ */\r
+ public void clearWarnings()\r
+ {\r
+ warnings = null;\r
+ }\r
+\r
+ /**\r
+ * @exception StandardException on error\r
+ */\r
+ protected static void nullToPrimitiveTest(DataValueDescriptor dvd, String primitiveType)\r
+ throws StandardException\r
+ {\r
+ if (dvd.isNull())\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_NULL_TO_PRIMITIVE_PARAMETER, primitiveType);\r
+ }\r
+ }\r
+\r
+ /**\r
+ @see Activation#informOfRowCount\r
+ @exception StandardException Thrown on error\r
+ */\r
+ public void informOfRowCount(NoPutResultSet resultSet, long currentRowCount)\r
+ throws StandardException\r
+ {\r
+\r
+ /* Do we want to check the row counts during this execution? */\r
+ if (checkRowCounts)\r
+ {\r
+ boolean significantChange = false;\r
+\r
+ int resultSetNumber = resultSet.resultSetNumber();\r
+ Integer rsn = ReuseFactory.getInteger(resultSetNumber);\r
+\r
+ /* Check each result set only once per execution */\r
+ if (rowCountsCheckedThisExecution.add(rsn))\r
+ {\r
+ synchronized (getPreparedStatement())\r
+ {\r
+ Vector rowCountCheckVector = getRowCountCheckVector();\r
+\r
+ if (rowCountCheckVector == null) {\r
+ rowCountCheckVector = new Vector();\r
+ setRowCountCheckVector(rowCountCheckVector);\r
+ }\r
+\r
+ Long firstRowCount = null;\r
+\r
+ /*\r
+ ** Check whether this resultSet has been seen yet.\r
+ */\r
+ if (resultSetNumber < rowCountCheckVector.size())\r
+ {\r
+ firstRowCount =\r
+ (Long) rowCountCheckVector.elementAt(resultSetNumber);\r
+ }\r
+ else\r
+ {\r
+ rowCountCheckVector.setSize(resultSetNumber + 1);\r
+ }\r
+\r
+ if (firstRowCount != null)\r
+ {\r
+ /*\r
+ ** This ResultSet has been seen - has the row count\r
+ ** changed significantly?\r
+ */\r
+ long n1 = firstRowCount.longValue();\r
+\r
+ if (currentRowCount != n1)\r
+ {\r
+ if (n1 >= TEN_PERCENT_THRESHOLD)\r
+ {\r
+ /*\r
+ ** For tables with more than\r
+ ** TEN_PERCENT_THRESHOLD rows, the\r
+ ** threshold is 10% of the size of the table.\r
+ */\r
+ long changeFactor = n1 / (currentRowCount - n1);\r
+ if (Math.abs(changeFactor) <= 10)\r
+ significantChange = true;\r
+ }\r
+ else\r
+ {\r
+ /*\r
+ ** For tables with less than\r
+ ** TEN_PERCENT_THRESHOLD rows, the threshold\r
+ ** is non-linear. This is because we want\r
+ ** recompilation to happen sooner for small\r
+ ** tables that change size. This formula\r
+ ** is for a second-order equation (a parabola).\r
+ ** The derivation is:\r
+ **\r
+ ** c * n1 = (difference in row counts) ** 2\r
+ ** - or - \r
+ ** c * n1 = (currentRowCount - n1) ** 2\r
+ **\r
+ ** Solving this for currentRowCount, we get:\r
+ **\r
+ ** currentRowCount = n1 + sqrt(c * n1)\r
+ **\r
+ ** - or -\r
+ **\r
+ ** difference in row counts = sqrt(c * n1)\r
+ **\r
+ ** - or -\r
+ **\r
+ ** (difference in row counts) ** 2 =\r
+ ** c * n1\r
+ **\r
+ ** Which means that we should recompile when\r
+ ** the current row count exceeds n1 (the first\r
+ ** row count) by sqrt(c * n1), or when the\r
+ ** square of the difference exceeds c * n1.\r
+ ** A good value for c seems to be 4.\r
+ **\r
+ ** We don't use this formula when c is greater\r
+ ** than TEN_PERCENT_THRESHOLD because we never\r
+ ** want to recompile unless the number of rows\r
+ ** changes by more than 10%, and this formula\r
+ ** is more sensitive than that for values of\r
+ ** n1 greater than TEN_PERCENT_THRESHOLD.\r
+ */\r
+ long changediff = currentRowCount - n1;\r
+\r
+ /*\r
+ ** Square changediff rather than take the square\r
+ ** root of (4 * n1), because multiplying is\r
+ ** faster than taking a square root. Also,\r
+ ** check to be sure that squaring changediff\r
+ ** will not cause an overflow by comparing it\r
+ ** with the square root of the maximum value\r
+ ** for a long (this square root is taken only\r
+ ** once, when the class is loaded, or during\r
+ ** compilation if the compiler is smart enough).\r
+ */\r
+ if (Math.abs(changediff) <= MAX_SQRT)\r
+ {\r
+ if ((changediff * changediff) >\r
+ Math.abs(4 * n1))\r
+ {\r
+ significantChange = true;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ else\r
+ {\r
+ firstRowCount = new Long(currentRowCount);\r
+ rowCountCheckVector.setElementAt(\r
+ firstRowCount,\r
+ resultSetNumber\r
+ );\r
+\r
+ }\r
+ }\r
+ }\r
+\r
+ /* Invalidate outside of the critical section */\r
+ if (significantChange)\r
+ {\r
+ preStmt.makeInvalid(DependencyManager.INTERNAL_RECOMPILE_REQUEST, lcc);\r
+ }\r
+ }\r
+\r
+ }\r
+\r
+ /**\r
+ * The subclass calls this method when it begins an execution.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public void startExecution() throws StandardException\r
+ {\r
+ // determine if we should check row counts during this execution\r
+ shouldWeCheckRowCounts();\r
+\r
+ // If we are to check row counts, clear the hash table of row counts\r
+ // we have checked.\r
+ if (checkRowCounts)\r
+ rowCountsCheckedThisExecution.clear();\r
+ }\r
+\r
+ /**\r
+ * @see Activation#getHeapConglomerateController\r
+ */\r
+ public ConglomerateController getHeapConglomerateController()\r
+ {\r
+ return updateHeapCC;\r
+ }\r
+\r
+\r
+ /**\r
+ * @see Activation#setHeapConglomerateController\r
+ */\r
+ public void setHeapConglomerateController(ConglomerateController updateHeapCC)\r
+ {\r
+ this.updateHeapCC = updateHeapCC;\r
+ }\r
+\r
+ /**\r
+ * @see Activation#clearHeapConglomerateController\r
+ */\r
+ public void clearHeapConglomerateController()\r
+ {\r
+ updateHeapCC = null;\r
+ }\r
+\r
+ /**\r
+ * @see Activation#getIndexScanController\r
+ */\r
+ public ScanController getIndexScanController()\r
+ {\r
+ return indexSC;\r
+ }\r
+\r
+ /**\r
+ * @see Activation#setIndexScanController\r
+ */\r
+ public void setIndexScanController(ScanController indexSC)\r
+ {\r
+ this.indexSC = indexSC;\r
+ }\r
+\r
+ /**\r
+ * @see Activation#getIndexConglomerateNumber\r
+ */\r
+ public long getIndexConglomerateNumber()\r
+ {\r
+ return indexConglomerateNumber;\r
+ }\r
+\r
+ /**\r
+ * @see Activation#setIndexConglomerateNumber\r
+ */\r
+ public void setIndexConglomerateNumber(long indexConglomerateNumber)\r
+ {\r
+ this.indexConglomerateNumber = indexConglomerateNumber;\r
+ }\r
+\r
+ /**\r
+ * @see Activation#clearIndexScanInfo\r
+ */\r
+ public void clearIndexScanInfo()\r
+ {\r
+ indexSC = null;\r
+ indexConglomerateNumber = -1;\r
+ }\r
+\r
+ /**\r
+ * @see Activation#setForCreateTable()\r
+ */\r
+ public void setForCreateTable()\r
+ {\r
+ forCreateTable = true;\r
+ }\r
+\r
+ /**\r
+ * @see Activation#getForCreateTable()\r
+ */\r
+ public boolean getForCreateTable()\r
+ {\r
+ return forCreateTable;\r
+ }\r
+\r
+ /**\r
+ * @see Activation#setDDLTableDescriptor\r
+ */\r
+ public void setDDLTableDescriptor(TableDescriptor td)\r
+ {\r
+ ddlTableDescriptor = td;\r
+ }\r
+\r
+ /**\r
+ * @see Activation#getDDLTableDescriptor\r
+ */\r
+ public TableDescriptor getDDLTableDescriptor()\r
+ {\r
+ return ddlTableDescriptor;\r
+ }\r
+\r
+ /**\r
+ * @see Activation#setMaxRows\r
+ */\r
+ public void setMaxRows(int maxRows)\r
+ {\r
+ this.maxRows = maxRows;\r
+ }\r
+\r
+ /**\r
+ * @see Activation#getMaxRows\r
+ */\r
+ public int getMaxRows()\r
+ {\r
+ return maxRows;\r
+ }\r
+\r
+ public void setTargetVTI(java.sql.ResultSet targetVTI)\r
+ {\r
+ this.targetVTI = targetVTI;\r
+ }\r
+\r
+ public java.sql.ResultSet getTargetVTI()\r
+ {\r
+ return targetVTI;\r
+ }\r
+\r
+ private void shouldWeCheckRowCounts() throws StandardException\r
+ {\r
+ /*\r
+ ** Check the row count only every N executions. OK to check this\r
+ ** without synchronization, since the value of this number is not\r
+ ** critical. The value of N is determined by the property\r
+ ** derby.language.stalePlanCheckInterval.\r
+ */\r
+ int executionCount = getExecutionCount() + 1;\r
+\r
+ /*\r
+ ** Always check row counts the first time, to establish the\r
+ ** row counts for each result set. After that, don't check\r
+ ** if the execution count is below the minimum row count check\r
+ ** interval. This saves us from checking a database property\r
+ ** when we don't have to (checking involves querying the store,\r
+ ** which can be expensive).\r
+ */\r
+\r
+ if (executionCount == 1)\r
+ {\r
+ checkRowCounts = true;\r
+ }\r
+ else if (executionCount <\r
+ Property.MIN_LANGUAGE_STALE_PLAN_CHECK_INTERVAL)\r
+ {\r
+ checkRowCounts = false;\r
+ }\r
+ else\r
+ {\r
+ int stalePlanCheckInterval = getStalePlanCheckInterval();\r
+\r
+ /*\r
+ ** Only query the database property once. We can tell because\r
+ ** the minimum value of the property is greater than zero.\r
+ */\r
+ if (stalePlanCheckInterval == 0)\r
+ {\r
+ TransactionController tc = getTransactionController();\r
+\r
+ stalePlanCheckInterval =\r
+ PropertyUtil.getServiceInt(\r
+ tc,\r
+ Property.LANGUAGE_STALE_PLAN_CHECK_INTERVAL,\r
+ Property.MIN_LANGUAGE_STALE_PLAN_CHECK_INTERVAL,\r
+ Integer.MAX_VALUE,\r
+ Property.DEFAULT_LANGUAGE_STALE_PLAN_CHECK_INTERVAL\r
+ );\r
+ setStalePlanCheckInterval(stalePlanCheckInterval);\r
+ }\r
+\r
+ checkRowCounts = (executionCount % stalePlanCheckInterval) == 1;\r
+\r
+\r
+ }\r
+\r
+ setExecutionCount(executionCount);\r
+ }\r
+\r
+ /*\r
+ ** These accessor methods are provided by the sub-class to help figure\r
+ ** out whether to check row counts during this execution.\r
+ */\r
+ abstract protected int getExecutionCount();\r
+\r
+ abstract protected void setExecutionCount(int newValue); \r
+\r
+ /*\r
+ ** These accessor methods are provided by the sub-class to help figure\r
+ ** out whether the row count for a particular result set has changed\r
+ ** enough to force recompilation.\r
+ */\r
+ abstract protected Vector getRowCountCheckVector();\r
+\r
+ abstract protected void setRowCountCheckVector(Vector newValue);\r
+\r
+ /*\r
+ ** These accessor methods are provided by the sub-class to remember the\r
+ ** value of the stale plan check interval property, so that we only\r
+ ** have to query the database properties once (there is heavyweight\r
+ ** synchronization around the database properties).\r
+ */\r
+ abstract protected int getStalePlanCheckInterval();\r
+\r
+ abstract protected void setStalePlanCheckInterval(int newValue);\r
+\r
+ public final boolean getScrollable() {\r
+ return scrollable;\r
+ }\r
+\r
+ protected final void setParameterValueSet(int paramCount, boolean hasReturnParam) {\r
+\r
+ pvs = lcc.getLanguageFactory().newParameterValueSet(\r
+ lcc.getLanguageConnectionFactory().getClassFactory().getClassInspector(),\r
+ paramCount, hasReturnParam);\r
+ }\r
+ \r
+ /**\r
+ * This method can help reduce the amount of generated code by changing\r
+ * instances of this.pvs.getParameter(position) to this.getParameter(position) \r
+ * @param position\r
+ * @throws StandardException\r
+ */\r
+ protected final DataValueDescriptor getParameter(int position) throws StandardException { \r
+ return pvs.getParameter(position); \r
+ } \r
+ \r
+ /**\r
+ return the parameters.\r
+ */\r
+ public ParameterValueSet getParameterValueSet() \r
+ { \r
+ if (pvs == null)\r
+ setParameterValueSet(0, false); \r
+ return pvs; \r
+ }\r
+\r
+ // how do we do/do we want any sanity checking for\r
+ // the number of parameters expected?\r
+ public void setParameters(ParameterValueSet parameterValues, DataTypeDescriptor[] parameterTypes) throws StandardException\r
+ {\r
+ if (!isClosed())\r
+ {\r
+\r
+ if (this.pvs == null || parameterTypes == null) {\r
+ pvs = parameterValues;\r
+ return;\r
+\r
+ }\r
+\r
+ DataTypeDescriptor[] newParamTypes = preStmt.getParameterTypes();\r
+\r
+ /*\r
+ ** If there are old parameters but not new ones,\r
+ ** they aren't compatible.\r
+ */\r
+ boolean match = false;\r
+ if (newParamTypes != null) {\r
+\r
+ if (newParamTypes.length == parameterTypes.length) {\r
+\r
+ /* Check each parameter */\r
+ match = true;\r
+ for (int i = 0; i < parameterTypes.length; i++)\r
+ {\r
+ DataTypeDescriptor oldType = parameterTypes[i];\r
+ DataTypeDescriptor newType = newParamTypes[i];\r
+\r
+ if (!oldType.isExactTypeAndLengthMatch(newType)) {\r
+ match = false;\r
+ break;\r
+ }\r
+ /*\r
+ ** We could probably get away without checking nullability,\r
+ ** since parameters are always nullable.\r
+ */\r
+ if (oldType.isNullable() != newType.isNullable()) {\r
+ match = false;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ }\r
+\r
+ if (!match)\r
+ throw StandardException.newException(SQLState.LANG_OBSOLETE_PARAMETERS);\r
+\r
+\r
+ parameterValues.transferDataValues(pvs);\r
+\r
+ }\r
+ else if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.THROWASSERT("isClosed() is expected to return false");\r
+ }\r
+ }\r
+\r
+ /**\r
+ Throw an exception if any parameters are uninitialized.\r
+\r
+ @exception StandardException Thrown if any parameters\r
+ are unitialized\r
+ */\r
+\r
+ public void throwIfMissingParms() throws StandardException\r
+ {\r
+ if (pvs != null && !pvs.allAreSet())\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_MISSING_PARMS);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Remember the row for the specified ResultSet.\r
+ */\r
+ public void setCurrentRow(ExecRow currentRow, int resultSetNumber)\r
+ { \r
+ if (SanityManager.DEBUG) \r
+ {\r
+ SanityManager.ASSERT(!isClosed(), "closed");\r
+ if (row != null)\r
+ {\r
+ if (!(resultSetNumber >=0 && resultSetNumber < row.length))\r
+ {\r
+ SanityManager.THROWASSERT("resultSetNumber = " + resultSetNumber +\r
+ ", expected to be between 0 and " + row.length);\r
+ }\r
+ }\r
+ }\r
+ if (row != null)\r
+ {\r
+ row[resultSetNumber] = currentRow;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Clear the current row for the specified ResultSet.\r
+ */\r
+ public void clearCurrentRow(int resultSetNumber)\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (row != null)\r
+ {\r
+ if (!(resultSetNumber >=0 && resultSetNumber < row.length))\r
+ {\r
+ SanityManager.THROWASSERT("resultSetNumber = " + resultSetNumber +\r
+ ", expected to be between 0 and " + row.length);\r
+ }\r
+ }\r
+ }\r
+ if (row != null)\r
+ {\r
+ row[resultSetNumber] = null;\r
+ }\r
+ }\r
+\r
+ protected final DataValueDescriptor getColumnFromRow(int rsNumber, int colId)\r
+ throws StandardException {\r
+\r
+ if( row[rsNumber] == null)\r
+ {\r
+ /* This actually happens. NoPutResultSetImpl.clearOrderableCache attempts to prefetch invariant values\r
+ * into a cache. This fails in some deeply nested joins. See Beetle 4736 and 4880.\r
+ */\r
+ return null;\r
+ }\r
+ return row[rsNumber].getColumn(colId);\r
+ }\r
+\r
+ /**\r
+ * Check that a positioned statement is executing against a cursor\r
+ * from the same PreparedStatement (plan) that the positioned\r
+ * statement was original compiled against.\r
+ * \r
+ * Only called from generated code for positioned UPDATE and DELETE\r
+ * statements. See CurrentOfNode.\r
+ * \r
+ * @param cursorName Name of the cursor\r
+ * @param psName Object name of the PreparedStatement.\r
+ * @throws StandardException\r
+ */\r
+ protected void checkPositionedStatement(String cursorName, String psName)\r
+ throws StandardException {\r
+\r
+ ExecPreparedStatement ps = getPreparedStatement();\r
+ if (ps == null)\r
+ return;\r
+ \r
+ LanguageConnectionContext lcc = getLanguageConnectionContext();\r
+\r
+ CursorActivation cursorActivation = lcc.lookupCursorActivation(cursorName);\r
+\r
+ if (cursorActivation != null)\r
+ {\r
+ // check we are compiled against the correct cursor\r
+ if (!psName.equals(cursorActivation.getPreparedStatement().getObjectName())) {\r
+\r
+ // our prepared statement is now invalid since there\r
+ // exists another cursor with the same name but a different\r
+ // statement.\r
+ ps.makeInvalid(DependencyManager.CHANGED_CURSOR, lcc);\r
+ }\r
+ }\r
+ }\r
+\r
+ /* This method is used to materialize a resultset if can actually fit in the memory\r
+ * specified by "maxMemoryPerTable" system property. It converts the result set into\r
+ * union(union(union...(union(row, row), row), ...row), row). It returns this\r
+ * in-memory converted resultset, or the original result set if not converted.\r
+ * See beetle 4373 for details.\r
+ *\r
+ * Optimization implemented as part of Beetle: 4373 can cause severe stack overflow\r
+ * problems. See JIRA entry DERBY-634. With default MAX_MEMORY_PER_TABLE of 1MG, it is\r
+ * possible that this optimization could attempt to cache upto 250K rows as nested\r
+ * union results. At runtime, this would cause stack overflow.\r
+ *\r
+ * As Jeff mentioned in DERBY-634, right way to optimize original problem would have been\r
+ * to address subquery materialization during optimization phase, through hash joins.\r
+ * Recent Army's optimizer work through DEBRY-781 and related work introduced a way to\r
+ * materialize subquery results correctly and needs to be extended to cover this case.\r
+ * While his optimization needs to be made more generic and stable, I propose to avoid\r
+ * this regression by limiting size of the materialized resultset created here to be\r
+ * less than MAX_MEMORY_PER_TABLE and MAX_DYNAMIC_MATERIALIZED_ROWS.\r
+ *\r
+ * @param rs input result set\r
+ * @return materialized resultset, or original rs if it can't be materialized\r
+ */\r
+ public NoPutResultSet materializeResultSetIfPossible(NoPutResultSet rs)\r
+ throws StandardException\r
+ {\r
+ rs.openCore();\r
+ Vector rowCache = new Vector();\r
+ ExecRow aRow;\r
+ int cacheSize = 0;\r
+ FormatableBitSet toClone = null;\r
+\r
+ int maxMemoryPerTable = getLanguageConnectionContext().getOptimizerFactory().getMaxMemoryPerTable();\r
+\r
+ aRow = rs.getNextRowCore();\r
+ if (aRow != null)\r
+ {\r
+ toClone = new FormatableBitSet(aRow.nColumns() + 1);\r
+ toClone.set(1);\r
+ }\r
+ while (aRow != null)\r
+ {\r
+ cacheSize += aRow.getColumn(1).getLength();\r
+ if (cacheSize > maxMemoryPerTable ||\r
+ rowCache.size() > Optimizer.MAX_DYNAMIC_MATERIALIZED_ROWS)\r
+ break;\r
+ rowCache.addElement(aRow.getClone(toClone));\r
+ aRow = rs.getNextRowCore();\r
+ }\r
+ rs.close();\r
+\r
+ if (aRow == null)\r
+ {\r
+ int rsNum = rs.resultSetNumber();\r
+\r
+ int numRows = rowCache.size();\r
+ if (numRows == 0)\r
+ {\r
+ return new RowResultSet(\r
+ this,\r
+ (ExecRow) null,\r
+ true,\r
+ rsNum,\r
+ 0,\r
+ 0);\r
+ }\r
+ RowResultSet[] rrs = new RowResultSet[numRows];\r
+ UnionResultSet[] urs = new UnionResultSet[numRows - 1];\r
+\r
+ for (int i = 0; i < numRows; i++)\r
+ {\r
+ rrs[i] = new RowResultSet(\r
+ this,\r
+ (ExecRow) rowCache.elementAt(i),\r
+ true,\r
+ rsNum,\r
+ 1,\r
+ 0);\r
+ if (i > 0)\r
+ {\r
+ urs[i - 1] = new UnionResultSet (\r
+ (i > 1) ? (NoPutResultSet)urs[i - 2] : (NoPutResultSet)rrs[0],\r
+ rrs[i],\r
+ this,\r
+ rsNum,\r
+ i + 1,\r
+ 0);\r
+ }\r
+ }\r
+\r
+ rs.finish();\r
+\r
+ if (numRows == 1)\r
+ return rrs[0];\r
+ else\r
+ return urs[urs.length - 1];\r
+ }\r
+ return rs;\r
+ }\r
+\r
+\r
+\r
+ //WARNING : this field name is referred in the DeleteNode generate routines.\r
+ protected CursorResultSet[] raParentResultSets;\r
+\r
+\r
+ // maintain hash table of parent result set vector\r
+ // a table can have more than one parent source.\r
+ protected Hashtable parentResultSets;\r
+ public void setParentResultSet(TemporaryRowHolder rs, String resultSetId)\r
+ {\r
+ Vector rsVector;\r
+ if(parentResultSets == null)\r
+ parentResultSets = new Hashtable();\r
+ rsVector = (Vector) parentResultSets.get(resultSetId);\r
+ if(rsVector == null)\r
+ {\r
+ rsVector = new Vector();\r
+ rsVector.addElement(rs);\r
+ }else\r
+ {\r
+ rsVector.addElement(rs);\r
+ }\r
+ parentResultSets.put(resultSetId , rsVector);\r
+ }\r
+\r
+ /**\r
+ * get the reference to parent table ResultSets, that will be needed by the \r
+ * referential action dependent table scans.\r
+ */\r
+ public Vector getParentResultSet(String resultSetId)\r
+ {\r
+ return (Vector) parentResultSets.get(resultSetId);\r
+ }\r
+\r
+ public Hashtable getParentResultSets()\r
+ {\r
+ return parentResultSets;\r
+ }\r
+\r
+ /**\r
+ ** prepared statement use the same activation for\r
+ ** multiple execution. For each excution we create new\r
+ ** set of temporary resultsets, we should clear this hash table.\r
+ ** otherwise we will refer to the released resources.\r
+ */\r
+ public void clearParentResultSets()\r
+ {\r
+ if(parentResultSets != null)\r
+ parentResultSets.clear();\r
+ }\r
+\r
+ /**\r
+ * beetle 3865: updateable cursor using index. A way of communication\r
+ * between cursor activation and update activation.\r
+ */\r
+ public void setForUpdateIndexScan(CursorResultSet forUpdateIndexScan)\r
+ {\r
+ this.forUpdateIndexScan = forUpdateIndexScan;\r
+ }\r
+\r
+ public CursorResultSet getForUpdateIndexScan()\r
+ {\r
+ return forUpdateIndexScan;\r
+ }\r
+\r
+ private java.util.Calendar cal;\r
+ /**\r
+ Return a calendar for use by this activation.\r
+ Calendar objects are not thread safe, the one returned\r
+ is purely for use by this activation and it is assumed\r
+ that is it single threded through the single active\r
+ thread in a connection model.\r
+ */\r
+ protected java.util.Calendar getCalendar() {\r
+ if (cal == null)\r
+ cal = new java.util.GregorianCalendar();\r
+ return cal;\r
+\r
+ }\r
+\r
+\r
+ /*\r
+ ** Code originally in the parent class BaseExpressionActivation\r
+ */\r
+ /**\r
+ Get the language connection factory associated with this connection\r
+ */\r
+ public final LanguageConnectionContext getLanguageConnectionContext()\r
+ {\r
+ return lcc;\r
+ }\r
+\r
+ public final TransactionController getTransactionController()\r
+ {\r
+ return lcc.getTransactionExecute();\r
+ }\r
+ \r
+ /**\r
+ * Get the Current ContextManager.\r
+ *\r
+ * @return Current ContextManager\r
+ */\r
+ public ContextManager getContextManager()\r
+ {\r
+ return cm;\r
+ }\r
+\r
+ /**\r
+ Used by activations to generate data values. Most DML statements\r
+ will use this method. Possibly some DDL statements will, as well.\r
+ */\r
+ public DataValueFactory getDataValueFactory()\r
+ {\r
+ return getLanguageConnectionContext().getDataValueFactory();\r
+ }\r
+\r
+ /**\r
+ * Used to get a proxy for the current connection.\r
+ *\r
+ * @exception SQLException Thrown on failure to get connection\r
+ */\r
+ public Connection getCurrentConnection() throws SQLException {\r
+\r
+ ConnectionContext cc = \r
+ (ConnectionContext) getContextManager().getContext(ConnectionContext.CONTEXT_ID);\r
+\r
+ return cc.getNestedConnection(true);\r
+ } \r
+\r
+ /**\r
+ Real implementations of this method are provided by a generated class.\r
+ */\r
+ public java.sql.ResultSet[][] getDynamicResults() {\r
+ return null;\r
+ }\r
+ /**\r
+ Real implementations of this method are provided by a generated class.\r
+ */\r
+ public int getMaxDynamicResults() {\r
+ return 0;\r
+ }\r
+\r
+ /**\r
+ * Compute the DB2 compatible length of a value.\r
+ *\r
+ * @param value\r
+ * @param constantLength The length, if it is a constant modulo null/not null. -1 if the length is not constant\r
+ * @param reUse If non-null then re-use this as a container for the length\r
+ *\r
+ * @return the DB2 compatible length, set to null if value is null.\r
+ */\r
+ public NumberDataValue getDB2Length( DataValueDescriptor value,\r
+ int constantLength,\r
+ NumberDataValue reUse)\r
+ throws StandardException\r
+ {\r
+ if( reUse == null)\r
+ reUse = getDataValueFactory().getNullInteger( null);\r
+ if( value.isNull())\r
+ reUse.setToNull();\r
+ else\r
+ {\r
+ if( constantLength >= 0)\r
+ reUse.setValue( constantLength);\r
+ else\r
+ {\r
+ reUse.setValue(value.getLength());\r
+ }\r
+ }\r
+ return reUse;\r
+ } // end of getDB2Length\r
+}\r