Adding JMCR-Stable version
[Benchmarks_CSolver.git] / JMCR-Stable / real-world application / derby-10.3.2.1 / java / engine / org / apache / derby / impl / sql / compile / CursorNode.java
diff --git a/JMCR-Stable/real-world application/derby-10.3.2.1/java/engine/org/apache/derby/impl/sql/compile/CursorNode.java b/JMCR-Stable/real-world application/derby-10.3.2.1/java/engine/org/apache/derby/impl/sql/compile/CursorNode.java
new file mode 100644 (file)
index 0000000..e06ea23
--- /dev/null
@@ -0,0 +1,787 @@
+/*\r
+\r
+   Derby - Class org.apache.derby.impl.sql.compile.CursorNode\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 java.util.ArrayList;\r
+import java.util.Vector;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+import org.apache.derby.iapi.reference.SQLState;\r
+import org.apache.derby.iapi.services.compiler.MethodBuilder;\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+import org.apache.derby.iapi.sql.ResultColumnDescriptor;\r
+import org.apache.derby.iapi.sql.compile.C_NodeTypes;\r
+import org.apache.derby.iapi.sql.dictionary.DataDictionary;\r
+import org.apache.derby.iapi.sql.dictionary.TableDescriptor;\r
+import org.apache.derby.impl.sql.CursorInfo;\r
+import org.apache.derby.impl.sql.CursorTableReference;\r
+\r
+/**\r
+ * A CursorNode represents a result set that can be returned to a client.\r
+ * A cursor can be a named cursor created by the DECLARE CURSOR statement,\r
+ * or it can be an unnamed cursor associated with a SELECT statement (more\r
+ * precisely, a table expression that returns rows to the client).  In the\r
+ * latter case, the cursor does not have a name.\r
+ *\r
+ */\r
+\r
+public class CursorNode extends DMLStatementNode\r
+{\r
+       public final static int UNSPECIFIED = 0;\r
+       public final static int READ_ONLY = 1;\r
+       public final static int UPDATE = 2;\r
+\r
+       private String          name;\r
+       private OrderByList     orderByList;\r
+       private String          statementType;\r
+       private int             updateMode;\r
+       private boolean         needTarget;\r
+\r
+       /**\r
+       ** There can only be a list of updatable columns when FOR UPDATE\r
+       ** is specified as part of the cursor specification.\r
+       */\r
+       private Vector  updatableColumns;\r
+       private FromTable updateTable;\r
+       private ResultColumnDescriptor[]        targetColumnDescriptors;\r
+\r
+       //If cursor references session schema tables, save the list of those table names in savedObjects in compiler context\r
+       //Following is the position of the session table names list in savedObjects in compiler context\r
+       //At generate time, we save this position in activation for easy access to session table names list from compiler context\r
+       private int indexOfSessionTableNamesInSavedObjects = -1;\r
+\r
+       /**\r
+        * Initializer for a CursorNode\r
+        *\r
+        * @param statementType Type of statement (SELECT, UPDATE, INSERT)\r
+        * @param resultSet     A ResultSetNode specifying the result set for\r
+        *                      the cursor\r
+        * @param name          The name of the cursor, null if no name\r
+        * @param orderByList   The order by list for the cursor, null if no\r
+        *                      order by list\r
+        * @param updateMode    The user-specified update mode for the cursor,\r
+        *                      for example, CursorNode.READ_ONLY\r
+        * @param updatableColumns The list of updatable columns specified by\r
+        *                      the user in the FOR UPDATE clause, null if no\r
+        *                      updatable columns specified.  May only be\r
+        *                      provided if the updateMode parameter is\r
+        *                      CursorNode.UPDATE.\r
+        */\r
+\r
+       public  void init(\r
+               Object statementType,\r
+               Object resultSet,\r
+               Object name,\r
+               Object orderByList,\r
+               Object updateMode,\r
+               Object updatableColumns)\r
+       {\r
+               init(resultSet);\r
+               this.name = (String) name;\r
+               this.statementType = (String) statementType;\r
+               this.orderByList = (OrderByList) orderByList;\r
+\r
+               this.updateMode = ((Integer) updateMode).intValue();\r
+               this.updatableColumns = (Vector) updatableColumns;\r
+\r
+               /*\r
+               ** This is a sanity check and not an error since the parser\r
+               ** controls setting updatableColumns and updateMode.\r
+               */\r
+               if (SanityManager.DEBUG)\r
+               SanityManager.ASSERT(this.updatableColumns == null ||\r
+                       this.updatableColumns.size() == 0 || this.updateMode == UPDATE,\r
+                       "Can only have explicit updatable columns if update mode is UPDATE");\r
+       }\r
+\r
+       /**\r
+        * Convert this object to a String.  See comments in QueryTreeNode.java\r
+        * for how this should be done for tree printing.\r
+        *\r
+        * @return      This object as a String\r
+        */\r
+\r
+       public String toString()\r
+       {\r
+               if (SanityManager.DEBUG)\r
+               {\r
+                       return "name: " + name + "\n" +\r
+                               "updateMode: " + updateModeString(updateMode) + "\n" +\r
+                               super.toString();\r
+               }\r
+               else\r
+               {\r
+                       return "";\r
+               }\r
+       }\r
+\r
+       public String statementToString()\r
+       {\r
+               return statementType;\r
+       }\r
+\r
+       /**\r
+        * Support routine for translating an updateMode identifier to a String\r
+        *\r
+        * @param updateMode    An updateMode identifier\r
+        *\r
+        * @return      A String representing the update mode.\r
+        */\r
+\r
+       private static String updateModeString(int updateMode)\r
+       {\r
+               if (SanityManager.DEBUG)\r
+               {\r
+                       switch (updateMode)\r
+                       {\r
+                         case UNSPECIFIED:\r
+                               return "UNSPECIFIED (" + UNSPECIFIED + ")";\r
+\r
+                         case READ_ONLY:\r
+                               return "READ_ONLY (" + READ_ONLY + ")";\r
+\r
+                         case UPDATE:\r
+                               return "UPDATE (" + UPDATE + ")";\r
+\r
+                         default:\r
+                               return "UNKNOWN VALUE (" + updateMode + ")";\r
+                       }\r
+               }\r
+               else\r
+               {\r
+                       return "";\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Prints the sub-nodes of this object.  See QueryTreeNode.java for\r
+        * how tree printing is supposed to work.\r
+        *\r
+        * @param depth         The depth of this node in the tree\r
+        */\r
+\r
+       public void printSubNodes(int depth)\r
+       {\r
+               if (SanityManager.DEBUG)\r
+               {\r
+                       super.printSubNodes(depth);\r
+\r
+                       printLabel(depth, "orderByList: ");\r
+                       if (orderByList != null)\r
+                               orderByList.treePrint(depth + 1);\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Bind this CursorNode.  This means looking up tables and columns and\r
+        * getting their types, and figuring out the result types of all\r
+        * expressions, as well as doing view resolution, permissions checking,\r
+        * etc. It also includes determining whether an UNSPECIFIED cursor\r
+        * is updatable or not, and verifying that an UPDATE cursor actually is.\r
+        *\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+\r
+       public void bindStatement() throws StandardException\r
+       {\r
+               DataDictionary                          dataDictionary;\r
+\r
+               dataDictionary = getDataDictionary();\r
+\r
+               // This is how we handle queries like: SELECT A FROM T ORDER BY B.\r
+               // We pull up the order by columns (if they don't appear in the SELECT\r
+               // LIST) and let the bind() do the job.  Note that the pullup is done\r
+               // before the bind() and we may avoid pulling up ORDERBY columns that\r
+               // would otherwise be avoided, e.g., "SELECT * FROM T ORDER BY B".\r
+               // Pulled-up ORDERBY columns that are duplicates (like the above "SELECT\r
+               // *" query will be removed in bindOrderByColumns().\r
+               // Finally, given that extra columns may be added to the SELECT list, we\r
+               // inject a ProjectRestrictNode so that only the user-specified columns\r
+               // will be returned (see genProjectRestrict() in SelectNode.java).\r
+               if (orderByList != null)\r
+               {\r
+                       orderByList.pullUpOrderByColumns(resultSet);\r
+               }\r
+\r
+               getCompilerContext().pushCurrentPrivType(getPrivType());\r
+               try {\r
+                       FromList        fromList = (FromList) getNodeFactory().getNode(\r
+                                       C_NodeTypes.FROM_LIST,\r
+                                       getNodeFactory().doJoinOrderOptimization(),\r
+                                       getContextManager());\r
+\r
+                       /* Check for ? parameters directly under the ResultColums */\r
+                       resultSet.rejectParameters();\r
+\r
+                       super.bind(dataDictionary);\r
+\r
+                       // bind the query expression\r
+                       resultSet.bindResultColumns(fromList);\r
+\r
+                       // this rejects any untyped nulls in the select list\r
+                       // pass in null to indicate that we don't have any\r
+                       // types for this node\r
+                       resultSet.bindUntypedNullsToResultColumns(null);\r
+\r
+                       // Reject any XML values in the select list; JDBC doesn't\r
+                       // define how we bind these out, so we don't allow it.\r
+                       resultSet.rejectXMLValues();\r
+\r
+                       /* Verify that all underlying ResultSets reclaimed their FromList */\r
+                       if (SanityManager.DEBUG) {\r
+                               SanityManager.ASSERT(fromList.size() == 0,\r
+                                       "fromList.size() is expected to be 0, not "\r
+                                                       + fromList.size()\r
+                                                       + " on return from RS.bindExpressions()");\r
+                       }\r
+               }\r
+               finally\r
+               {\r
+                       getCompilerContext().popCurrentPrivType();\r
+               }\r
+\r
+               // bind the order by\r
+               if (orderByList != null)\r
+               {\r
+                       orderByList.bindOrderByColumns(resultSet);\r
+               }\r
+\r
+               // bind the updatability\r
+\r
+               // if it says it is updatable, verify it.\r
+               if (updateMode == UPDATE)\r
+               {\r
+                       int checkedUpdateMode;\r
+\r
+                       checkedUpdateMode = determineUpdateMode(dataDictionary);\r
+                       if (SanityManager.DEBUG)\r
+                       SanityManager.DEBUG("DumpUpdateCheck","update mode is UPDATE ("+updateMode+") checked mode is "+checkedUpdateMode);\r
+                       if (updateMode != checkedUpdateMode)\r
+                                       throw StandardException.newException(SQLState.LANG_STMT_NOT_UPDATABLE);\r
+               }\r
+\r
+               // if it doesn't know if it is updatable, determine it\r
+               if (updateMode == UNSPECIFIED)\r
+               {\r
+                   // If the statement is opened with CONCUR_READ_ONLY, the upgrade mode is \r
+                   // set to read only.\r
+                   \r
+                   // NOTE: THIS IS NOT COMPATIBLE WITH THE ISO/ANSI SQL STANDARD.\r
+\r
+                   // According to the SQL-standard:\r
+                   // If updatability is not specified, a SELECT * FROM T will be implicitely\r
+                   // read only in the context of a cursor which is insensitive, scrollable or\r
+                   // have an order by clause. Otherwise it is implicitely updatable.\r
+                   \r
+                   // In Derby, we make a SELECT * FROM T updatable if the concurrency mode is\r
+                   // ResultSet.CONCUR_UPDATE. If we do make all SELECT * FROM T  updatable\r
+                   // by default, we cannot use an index on any single-table select, unless it\r
+                   // was declared FOR READ ONLY. This would be pretty terrible, so we are\r
+                   // breaking the ANSI rules.\r
+\r
+                   if (getLanguageConnectionContext().getStatementContext().isForReadOnly()) {\r
+                       updateMode = READ_ONLY;\r
+                   } else {\r
+                       updateMode = determineUpdateMode(dataDictionary);\r
+                   }\r
+                                   \r
+                       //if (SanityManager.DEBUG)\r
+                       //SanityManager.DEBUG("DumpUpdateCheck","update mode is UNSPECIFIED ("+UNSPECIFIED+") checked mode is "+updateMode);\r
+               }\r
+               \r
+               if (updateMode == READ_ONLY) {\r
+                   updatableColumns = null; // don't need them any more\r
+               }\r
+\r
+               // bind the update columns\r
+               if (updateMode == UPDATE)\r
+               {\r
+                       bindUpdateColumns(updateTable);\r
+\r
+                       // If the target table is a FromBaseTable, mark the updatable\r
+                       // columns.  (I can't think of a way that an updatable table\r
+                       // could be anything but a FromBaseTable at this point, but\r
+                       // it's better to be careful.\r
+                       if (updateTable instanceof FromTable)\r
+                       {\r
+                               ((FromTable) updateTable).markUpdatableByCursor(updatableColumns);\r
+                               //make sure that alongwith the FromTable, we keep other ResultSetLists\r
+                               //in correct state too. ResultSetMetaData.isWritable looks at this to\r
+                               //return the correct value.\r
+                               resultSet.getResultColumns().markColumnsInSelectListUpdatableByCursor(\r
+                                       updatableColumns);\r
+                       }\r
+               }\r
+\r
+               resultSet.renameGeneratedResultNames();\r
+\r
+               //need to look for SESSION tables only if global temporary tables declared for the connection\r
+               if (getLanguageConnectionContext().checkIfAnyDeclaredGlobalTempTablesForThisConnection())\r
+               {\r
+                       //If this cursor has references to session schema tables, save the names of those tables into compiler context\r
+                       //so they can be passed to execution phase.\r
+                       ArrayList sessionSchemaTableNames = getSessionSchemaTableNamesForCursor();\r
+                       if (sessionSchemaTableNames != null)\r
+                               indexOfSessionTableNamesInSavedObjects = getCompilerContext().addSavedObject(sessionSchemaTableNames);\r
+               }\r
+\r
+       }\r
+\r
+       /**\r
+        * Return true if the node references SESSION schema tables (temporary or permanent)\r
+        *\r
+        * @return      true if references SESSION schema tables, else false\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+       public boolean referencesSessionSchema()\r
+               throws StandardException\r
+       {\r
+               //If this node references a SESSION schema table, then return true. \r
+               return resultSet.referencesSessionSchema();\r
+       }\r
+\r
+       //Check if this cursor references any session schema tables. If so, pass those names to execution phase through savedObjects\r
+       //This list will be used to check if there are any holdable cursors referencing temporary tables at commit time.\r
+       //If yes, then the data in those temporary tables should be preserved even if they are declared with ON COMMIT DELETE ROWS option\r
+       protected ArrayList getSessionSchemaTableNamesForCursor()\r
+               throws StandardException\r
+       {\r
+               FromList fromList = resultSet.getFromList();\r
+               int fromListSize = fromList.size();\r
+               FromTable fromTable;\r
+               ArrayList sessionSchemaTableNames = null;\r
+\r
+               for( int i = 0; i < fromListSize; i++)\r
+               {\r
+                       fromTable = (FromTable) fromList.elementAt(i);\r
+                       if (fromTable instanceof FromBaseTable && isSessionSchema(fromTable.getTableDescriptor().getSchemaDescriptor()))\r
+                       {\r
+                               if (sessionSchemaTableNames == null)\r
+                                       sessionSchemaTableNames = new ArrayList();\r
+                               sessionSchemaTableNames.add(fromTable.getTableName().getTableName());\r
+                       }\r
+               }\r
+\r
+               return sessionSchemaTableNames;\r
+       }\r
+\r
+       /**\r
+        * Take a cursor and determine if it is UPDATE\r
+        * or READ_ONLY based on the shape of the cursor specification.\r
+        * <p>\r
+        * The following conditions make a cursor read only:\r
+        * <UL>\r
+        * <LI>if it says FOR READ ONLY\r
+        * <LI>if it says ORDER BY\r
+        * <LI>if its query specification is not read only. At present this\r
+        *     is explicitly tested here, with these conditions.  At some future\r
+        *     point in time, this checking ought to be moved into the\r
+        *     ResultSet nodes themselves.  The conditions for a query spec.\r
+     *     not to be read only include:\r
+        *     <UL>\r
+        *     <LI>if it has a set operation such as UNION or INTERSECT, i.e.\r
+        *         does not have a single outermost SELECT\r
+        *     <LI>if it does not have exactly 1 table in its FROM list;\r
+        *         0 tables would occur if we ever support a SELECT without a\r
+        *         FROM e.g., for generating a row without an underlying table\r
+        *         (like what we do for an INSERT of a VALUES list); >1 tables\r
+        *         occurs when joins are in the tree.\r
+        *     <LI>if the table in its FROM list is not a base table (REMIND\r
+        *         when views/from subqueries are added, this should be relaxed to\r
+     *         be that the table is not updatable)\r
+        *     <LI>if it has a GROUP BY or HAVING (NOTE I am assuming that if\r
+        *         and aggregate is detected in a SELECT w/o a GROUP BY, one\r
+        *         has been added to show that the whole table is a group)\r
+        *     <LI> NOTE that cursors are updatable even if none of the columns\r
+        *         in the select are updatable -- what they care about is the\r
+        *         updatability of the columns of the target table.\r
+        *     </UL>\r
+        * </UL>\r
+        *\r
+        * @return the known update mode for the cursor.\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+       private int determineUpdateMode(DataDictionary dataDictionary)\r
+               throws StandardException\r
+       {\r
+               SelectNode selectNode;\r
+               FromList tables;\r
+               FromTable targetTable;\r
+\r
+               if (updateMode == READ_ONLY)\r
+               {\r
+                       return READ_ONLY;\r
+               }\r
+\r
+               if (orderByList != null)\r
+               {\r
+                       if (SanityManager.DEBUG)\r
+                       SanityManager.DEBUG("DumpUpdateCheck","cursor has order by");\r
+                       return READ_ONLY;\r
+               }\r
+\r
+               // get the ResultSet to tell us what it thinks it is\r
+               // and the target table\r
+               if (! resultSet.isUpdatableCursor(dataDictionary))\r
+               {\r
+                       return READ_ONLY;\r
+               }\r
+\r
+               // The FOR UPDATE clause has two uses:\r
+               //\r
+               // for positioned cursor updates\r
+               //\r
+               // to change locking behaviour of the select\r
+               // to reduce deadlocks on subsequent updates\r
+               // in the same transaction.\r
+               //\r
+               // We now support this latter case, without requiring\r
+               // that the source of the rows be able to implement\r
+               // a positioned update.\r
+\r
+               updateTable = resultSet.getCursorTargetTable();\r
+\r
+               /* Tell the table that it is the cursor target */\r
+               if (updateTable.markAsCursorTargetTable()) {\r
+                       /* Cursor is updatable - remember to generate the position code */\r
+                       needTarget = true;\r
+\r
+                       /* We must generate the target column list at bind time\r
+                        * because the optimizer may transform the FromBaseTable from\r
+                        * a table scan into an index scan.\r
+                        */\r
+                       genTargetResultColList();\r
+               }\r
+\r
+\r
+\r
+\r
+               return UPDATE;\r
+       }\r
+\r
+       /**\r
+        * Optimize a DML statement (which is the only type of statement that\r
+        * should need optimizing, I think). This method over-rides the one\r
+        * in QueryTreeNode.\r
+        *\r
+        * This method takes a bound tree, and returns an optimized tree.\r
+        * It annotates the bound tree rather than creating an entirely\r
+        * new tree.\r
+        *\r
+        * Throws an exception if the tree is not bound, or if the binding\r
+        * is out of date.\r
+        *\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+\r
+       public void optimizeStatement() throws StandardException\r
+       {\r
+               // Push the order by list down to the ResultSet\r
+               if (orderByList != null)\r
+               {\r
+                       // If we have more than 1 ORDERBY columns, we may be able to\r
+                       // remove duplicate columns, e.g., "ORDER BY 1, 1, 2".\r
+                       if (orderByList.size() > 1)\r
+                       {\r
+                               orderByList.removeDupColumns();\r
+                       }\r
+\r
+                       resultSet.pushOrderByList(orderByList);\r
+                       orderByList = null;\r
+               }\r
+               super.optimizeStatement();\r
+       }\r
+\r
+       /**\r
+        * Returns the type of activation this class\r
+        * generates.\r
+        * \r
+        * @return either (NEED_CURSOR_ACTIVATION\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+        \r
+       int activationKind()\r
+       {\r
+               return NEED_CURSOR_ACTIVATION;\r
+       }\r
+\r
+       /**\r
+        * Do code generation for this CursorNode\r
+        *\r
+        * @param acb   The ActivationClassBuilder for the class being built\r
+        * @param mb    The method the generated code is to go into\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+\r
+       public void generate(ActivationClassBuilder acb,\r
+                                                               MethodBuilder mb) throws StandardException\r
+       {\r
+               if (indexOfSessionTableNamesInSavedObjects != -1 ) //if this cursor references session schema tables, do following\r
+               {\r
+                       MethodBuilder constructor = acb.getConstructor();\r
+                       constructor.pushThis();\r
+                       constructor.push(indexOfSessionTableNamesInSavedObjects);\r
+                       constructor.putField(org.apache.derby.iapi.reference.ClassName.BaseActivation, "indexOfSessionTableNamesInSavedObjects", "int");\r
+                       constructor.endStatement();\r
+    }\r
+\r
+               // generate the parameters\r
+               generateParameterValueSet(acb);\r
+\r
+               // tell the outermost result set that it is the outer\r
+               // result set of the statement.\r
+               resultSet.markStatementResultSet();\r
+\r
+               generateAuthorizeCheck(acb, mb,\r
+                               org.apache.derby.iapi.sql.conn.Authorizer.SQL_SELECT_OP);\r
+\r
+               // this will generate an expression that will be a ResultSet\r
+           resultSet.generate(acb, mb);\r
+\r
+               /*\r
+               ** Generate the position code if this cursor is updatable.  This\r
+               ** involves generating methods to get the cursor result set, and\r
+               ** the target result set (which is for the base row).  Also,\r
+               ** generate code to store the cursor result set in a generated\r
+               ** field.\r
+               */\r
+               if (needTarget)\r
+               {\r
+                       // PUSHCOMPILE - could be put into a single method\r
+                       acb.rememberCursor(mb);\r
+                       acb.addCursorPositionCode();\r
+               }\r
+       }\r
+\r
+       // class interface\r
+\r
+       public String getUpdateBaseTableName() \r
+       {\r
+               return (updateTable == null) ? null : updateTable.getBaseTableName();\r
+       }\r
+\r
+       public String getUpdateExposedTableName() \r
+               throws StandardException\r
+       {\r
+               return (updateTable == null) ? null : updateTable.getExposedName();\r
+       }\r
+\r
+       public String getUpdateSchemaName() \r
+               throws StandardException\r
+       {\r
+               //we need to use the base table for the schema name\r
+               return (updateTable == null) ? null : ((FromBaseTable)updateTable).getTableNameField().getSchemaName();\r
+       }\r
+\r
+       public int getUpdateMode()\r
+       {\r
+               return updateMode;\r
+       }\r
+\r
+       /**\r
+        * Return String[] of names from the FOR UPDATE OF List\r
+        *\r
+        * @return      String[] of names from the FOR UPDATE OF list.\r
+        */\r
+       private String[] getUpdatableColumns()\r
+       {\r
+               return (updatableColumns == null) ?\r
+                               (String[])null :\r
+                               getUpdateColumnNames();\r
+       }\r
+\r
+       /**\r
+               Positioned update needs to know what the target result set\r
+               looks like. This is generated from the UpdateColumnList\r
+               available for the cursor, to describe the rows coming from\r
+               the target result set under the cursor. This result set contains\r
+               a superset of the updatable columns; the caller must verify that\r
+               only those listed in the FOR UPDATE clause are used.\r
+\r
+               @return a result column list containing a description of\r
+               the target table (this may contain non-updatable columns).\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+       private ResultColumnDescriptor[] genTargetResultColList()\r
+               throws StandardException\r
+       {\r
+               ResultColumnList newList;\r
+\r
+               /*\r
+                  updateTable holds the FromTable that is the target.\r
+                  copy its ResultColumnList, making BaseColumn references\r
+                  for use in the CurrentOfNode, which behaves as if it had\r
+                  base columns for the statement it is in.\r
+\r
+                       updateTable is null if the cursor is not updatable.\r
+                */\r
+               if (updateTable == null) return null;\r
+\r
+               if (targetColumnDescriptors != null) return targetColumnDescriptors;\r
+\r
+               newList = (ResultColumnList) getNodeFactory().getNode(\r
+                                                                               C_NodeTypes.RESULT_COLUMN_LIST,\r
+                                                                               getContextManager());\r
+               ResultColumnList rcl = updateTable.getResultColumns();\r
+               int rclSize = rcl.size();\r
+               for (int index = 0; index < rclSize; index++)\r
+               {\r
+                       ResultColumn origCol, newCol;\r
+                       ValueNode newNode;\r
+\r
+                       origCol = (ResultColumn) rcl.elementAt(index);\r
+\r
+                       // Build a ResultColumn/BaseColumnNode pair for the column\r
+                       newNode = (ValueNode) getNodeFactory().getNode(\r
+                                                       C_NodeTypes.BASE_COLUMN_NODE,\r
+                                                       origCol.getName(),\r
+                                                       makeTableName(origCol.getSchemaName(),\r
+                                                                                 origCol.getTableName()),                                                              \r
+                                                       origCol.getTypeServices(),\r
+                                                       getContextManager());\r
+                       newCol = (ResultColumn) getNodeFactory().getNode(\r
+                                                                       C_NodeTypes.RESULT_COLUMN,\r
+                                                                       origCol.columnDescriptor,\r
+                                                                       newNode,\r
+                                                                       getContextManager());\r
+\r
+                       /* Build the ResultColumnList to return */\r
+                       newList.addResultColumn(newCol);\r
+               }\r
+\r
+               // we save the result so we only do this once\r
+               targetColumnDescriptors = newList.makeResultDescriptors();\r
+               return targetColumnDescriptors;\r
+       }\r
+\r
+       /**\r
+        * Returns whether or not this Statement requires a set/clear savepoint\r
+        * around its execution.  The following statement "types" do not require them:\r
+        *              Cursor  - unnecessary and won't work in a read only environment\r
+        *              Xact    - savepoint will get blown away underneath us during commit/rollback\r
+        *\r
+        * @return boolean      Whether or not this Statement requires a set/clear savepoint\r
+        */\r
+       public boolean needsSavepoint()\r
+       {\r
+               return false;\r
+       }\r
+\r
+       /**\r
+        * Get information about this cursor.  For sps,\r
+        * this is info saved off of the original query\r
+        * tree (the one for the underlying query).\r
+        *\r
+        * @return      the cursor info\r
+        * @exception StandardException thrown if generation fails\r
+        */\r
+       public Object getCursorInfo()\r
+               throws StandardException\r
+       {\r
+               if (!needTarget)\r
+                       return null;\r
+\r
+               return new CursorInfo(updateMode,\r
+                                                               new CursorTableReference(\r
+                                                                               getUpdateExposedTableName(),\r
+                                                                               getUpdateBaseTableName(),\r
+                                                                               getUpdateSchemaName()),\r
+                                                               genTargetResultColList(),\r
+                                                               getUpdatableColumns());\r
+       }\r
+\r
+       /**\r
+               Bind the update columns by their names to the target table\r
+               of the cursor specification.\r
+               Doesn't check for duplicates in the list, although it could...\r
+               REVISIT: If the list is empty, should it expand it out? at present,\r
+               it leaves it empty.\r
+       \r
+               @param targetTable      The underlying target table \r
+       \r
+               @exception StandardException            Thrown on error\r
+        */\r
+       private void bindUpdateColumns(FromTable targetTable)\r
+                                       throws StandardException \r
+       {\r
+               int size = updatableColumns.size();\r
+               TableDescriptor tableDescriptor;\r
+               String columnName;\r
+               ResultColumnList rcls = resultSet.getResultColumns();\r
+\r
+               for (int index = 0; index < size; index++)\r
+               {\r
+                   columnName = (String) updatableColumns.elementAt(index);\r
+                   tableDescriptor = targetTable.getTableDescriptor();\r
+                   if ( tableDescriptor.getColumnDescriptor(columnName) == null)\r
+                   {\r
+                                       throw StandardException.newException(SQLState.LANG_COLUMN_NOT_FOUND, columnName);\r
+                   }\r
+\r
+                   ResultColumn rc;\r
+                   //make sure that we are not using correlation names for updatable columns. \r
+                   //eg select c11 as col1, 2, c13 as col3 from t1 for update of c11, c12\r
+                   //In the eg above, correlation name for c11 will cause exception because Derby does not support correlation name for updatable columns\r
+                   //But correlation name for c13 is ok because it is a read only column\r
+                   for (int rclsIndex = 0; rclsIndex < rcls.size(); rclsIndex++) {//look through each column in the resultset for cursor\r
+                                       rc = ((ResultColumn) rcls.elementAt(rclsIndex));\r
+                                       if (rc.getSourceTableName() == null) //continue to look at the next column because this is derived column in the select list\r
+                                       continue;\r
+                                       if (rc.getExpression() != null && rc.getExpression().getColumnName().equals(columnName) &&  !rc.getName().equals(columnName)) {\r
+                                       throw StandardException.newException(SQLState.LANG_CORRELATION_NAME_FOR_UPDATABLE_COLUMN_DISALLOWED_IN_CURSOR, columnName);\r
+                                       }\r
+                   }\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Get an array of strings for each updatable column\r
+        * in this list.\r
+        *\r
+        * @return an array of strings\r
+        */\r
+       private String[] getUpdateColumnNames()\r
+       {\r
+               int size = updatableColumns.size();\r
+               if (size == 0)\r
+               {\r
+                       return (String[])null;\r
+               }\r
+\r
+               String[] names = new String[size];\r
+\r
+               updatableColumns.copyInto(names);\r
+\r
+               return names;\r
+       }\r
+       \r
+       public String getXML()\r
+       {\r
+               return null;\r
+       }\r
+}\r