Adding JMCR-Stable version
[Benchmarks_CSolver.git] / JMCR-Stable / real-world application / derby-10.3.2.1 / java / engine / org / apache / derby / impl / sql / execute / UpdateResultSet.java
diff --git a/JMCR-Stable/real-world application/derby-10.3.2.1/java/engine/org/apache/derby/impl/sql/execute/UpdateResultSet.java b/JMCR-Stable/real-world application/derby-10.3.2.1/java/engine/org/apache/derby/impl/sql/execute/UpdateResultSet.java
new file mode 100644 (file)
index 0000000..00862b3
--- /dev/null
@@ -0,0 +1,1107 @@
+/*\r
+\r
+   Derby - Class org.apache.derby.impl.sql.execute.UpdateResultSet\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.util.Hashtable;\r
+import java.util.Properties;\r
+\r
+import org.apache.derby.iapi.db.TriggerExecutionContext;\r
+import org.apache.derby.iapi.error.StandardException;\r
+import org.apache.derby.iapi.reference.SQLState;\r
+import org.apache.derby.iapi.services.io.FormatableBitSet;\r
+import org.apache.derby.iapi.services.io.StreamStorable;\r
+import org.apache.derby.iapi.services.loader.GeneratedMethod;\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+import org.apache.derby.iapi.sql.Activation;\r
+import org.apache.derby.iapi.sql.ResultDescription;\r
+import org.apache.derby.iapi.sql.ResultSet;\r
+import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;\r
+import org.apache.derby.iapi.sql.execute.ConstantAction;\r
+import org.apache.derby.iapi.sql.execute.CursorResultSet;\r
+import org.apache.derby.iapi.sql.execute.ExecRow;\r
+import org.apache.derby.iapi.sql.execute.NoPutResultSet;\r
+import org.apache.derby.iapi.sql.execute.RowChanger;\r
+import org.apache.derby.iapi.store.access.ConglomerateController;\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.BooleanDataValue;\r
+import org.apache.derby.iapi.types.DataValueDescriptor;\r
+import org.apache.derby.iapi.types.RowLocation;\r
+\r
+/**\r
+ * Update the rows from the specified\r
+ * base table. This will cause constraints to be checked\r
+ * and triggers to be executed based on the c's and t's\r
+ * compiled into the update plan.\r
+ *\r
+ */\r
+class UpdateResultSet extends DMLWriteResultSet\r
+{\r
+       private TransactionController   tc;\r
+       private ExecRow                                 newBaseRow;\r
+       private ExecRow                                         row;\r
+       private ExecRow                                         deferredSparseRow;\r
+       UpdateConstantAction            constants;\r
+       \r
+    private ResultDescription          resultDescription;\r
+       private NoPutResultSet                  source;\r
+       NoPutResultSet                  savedSource;\r
+       private RowChanger                              rowChanger;\r
+\r
+       protected ConglomerateController        deferredBaseCC;\r
+\r
+       protected long[]                                deferredUniqueCIDs;\r
+       protected boolean[]                             deferredUniqueCreated;\r
+       protected ConglomerateController        deferredUniqueCC[];\r
+       protected ScanController[]              deferredUniqueScans;\r
+\r
+       private TemporaryRowHolderImpl  deletedRowHolder;\r
+       private TemporaryRowHolderImpl  insertedRowHolder;\r
+\r
+       // cached \r
+       private RISetChecker                    riChecker;\r
+       private TriggerInfo                             triggerInfo;\r
+       private TriggerEventActivator   triggerActivator;\r
+       private boolean                                 updatingReferencedKey;\r
+       private boolean                                 updatingForeignKey;\r
+       private int                                             numOpens;\r
+       private long                                    heapConglom; \r
+       private FKInfo[]                                fkInfoArray;\r
+       private FormatableBitSet                                baseRowReadList;\r
+       private GeneratedMethod                 checkGM;\r
+       private int                                             resultWidth;\r
+       private int                                             numberOfBaseColumns;\r
+       private ExecRow                                 deferredTempRow;\r
+       private ExecRow                                 deferredBaseRow;\r
+       private ExecRow                                 oldDeletedRow;\r
+       private ResultDescription               triggerResultDescription;\r
+\r
+       int lockMode;\r
+       boolean deferred;\r
+       boolean beforeUpdateCopyRequired = false;\r
+\r
+       /**\r
+     * Returns the description of the updated rows.\r
+     * REVISIT: Do we want this to return NULL instead?\r
+        */\r
+       public ResultDescription getResultDescription()\r
+       {\r
+           return resultDescription;\r
+       }\r
+\r
+    /*\r
+     * class interface\r
+     *\r
+     */\r
+    /**\r
+        * @param source update rows come from source\r
+        * @param checkGM       Generated method for enforcing check constraints\r
+        * @exception StandardException thrown on error\r
+     */\r
+    UpdateResultSet(NoPutResultSet source,\r
+                                                  GeneratedMethod checkGM,\r
+                                                  Activation activation)\r
+      throws StandardException\r
+    {\r
+               this(source, checkGM , activation, activation.getConstantAction(),null);\r
+       }\r
+\r
+    /*\r
+     * class interface\r
+     *\r
+     */\r
+    /**\r
+        * @param source update rows come from source\r
+        * @param checkGM       Generated method for enforcing check constraints\r
+        * @param activation Activation\r
+        * @param constantActionItem  id of the update constant action saved objec\r
+        * @param rsdItem  id of the Result Description saved object\r
+        * @exception StandardException thrown on error\r
+     */\r
+    UpdateResultSet(NoPutResultSet source,\r
+                                                  GeneratedMethod checkGM,\r
+                                                  Activation activation, \r
+                                                  int constantActionItem,\r
+                                                  int rsdItem)\r
+      throws StandardException\r
+    {\r
+               this(source, checkGM , activation,\r
+                         ((ConstantAction)activation.getPreparedStatement().getSavedObject(constantActionItem)),\r
+                        (ResultDescription) activation.getPreparedStatement().getSavedObject(rsdItem));\r
+       \r
+               // In case of referential action update, we do a deferred updates\r
+               deferred = true;\r
+       }\r
+\r
+\r
+    /*\r
+     * class interface\r
+     *\r
+     */\r
+    /**\r
+        * @param source update rows come from source\r
+        * @param checkGM       Generated method for enforcing check constraints\r
+        * @exception StandardException thrown on error\r
+     */\r
+    UpdateResultSet(NoPutResultSet source,\r
+                                                  GeneratedMethod checkGM,\r
+                                                  Activation activation,\r
+                                                  ConstantAction passedInConstantAction,\r
+                                                  ResultDescription passedInRsd)\r
+      throws StandardException\r
+    {\r
+               super(activation, passedInConstantAction);\r
+\r
+               // Get the current transaction controller\r
+        tc = activation.getTransactionController();\r
+               this.source = source;\r
+               this.checkGM = checkGM;\r
+\r
+               constants = (UpdateConstantAction) constantAction;\r
+               fkInfoArray = constants.getFKInfo();\r
+               triggerInfo = constants.getTriggerInfo();\r
+\r
+               heapConglom = constants.conglomId;\r
+\r
+               baseRowReadList = constants.getBaseRowReadList();\r
+               if(passedInRsd ==null)\r
+                       resultDescription = source.getResultDescription();\r
+               else\r
+                       resultDescription = passedInRsd;\r
+               /*\r
+               ** We NEED a result description when we are going to\r
+               ** to have to kick off a trigger.  In a replicated environment\r
+               ** we don't get a result description when we are replaying\r
+               ** source xacts on the target, which should never be the\r
+               ** case for an UpdateResultSet.\r
+               */\r
+               if (SanityManager.DEBUG)\r
+               {\r
+                       if (resultDescription == null)\r
+                       {\r
+                               SanityManager.ASSERT(triggerInfo == null, "triggers need a result description to pass to result sets given to users");\r
+                       }\r
+               }\r
+\r
+               if (fkInfoArray != null)\r
+               {\r
+                       for (int i = 0; i < fkInfoArray.length; i++)\r
+                       {\r
+                               if (fkInfoArray[i].type == FKInfo.REFERENCED_KEY)\r
+                               {\r
+                                       updatingReferencedKey = true;\r
+                                       if (SanityManager.DEBUG)\r
+                                       {\r
+                                               SanityManager.ASSERT(constants.deferred, "updating referenced key but update not deferred, wuzzup?");\r
+                                       }\r
+                               }\r
+                               else\r
+                               {       \r
+                                       updatingForeignKey = true;\r
+                               }\r
+                       }\r
+               }\r
+\r
+               /* Get the # of columns in the ResultSet */\r
+               resultWidth = resultDescription.getColumnCount();\r
+               \r
+               /*\r
+               ** Calculate the # of columns in the base table.  The result set\r
+               ** contains the before columns, the after columns, and the RowLocation,\r
+               ** so the number of base columns is half of the number of result set\r
+               ** columns, after subtracting one for the row location column.\r
+               */\r
+               numberOfBaseColumns = (resultWidth - 1) / 2;\r
+               \r
+               /* Get the new base row */\r
+               newBaseRow = RowUtil.getEmptyValueRow(numberOfBaseColumns, lcc);\r
+\r
+               deferred = constants.deferred;\r
+               \r
+               //update can be marked for deferred mode because the scan is being done\r
+               //using index. But it is not necesary  to keep the before copy\r
+               //of the row in the temporary row holder (deletedRowHolder) unless\r
+               //there are RI constraint or Triggers.(beetle:5301)\r
+               if(triggerInfo != null || fkInfoArray !=null){\r
+                       beforeUpdateCopyRequired = true;\r
+               }\r
+               \r
+       }\r
+       /**\r
+               @exception StandardException Standard Derby error policy\r
+       */\r
+       public void open() throws StandardException\r
+       {\r
+\r
+               setup();\r
+               collectAffectedRows();\r
+\r
+               /*\r
+               ** If this is a deferred update, read the new rows and RowLocations\r
+               ** from the temporary conglomerate and update the base table using\r
+               ** the RowChanger.\r
+               */\r
+               if (deferred)\r
+               {\r
+\r
+                       runChecker(true); //check for only RESTRICT referential action rule violations\r
+                       fireBeforeTriggers();\r
+                       updateDeferredRows();\r
+                       /* Apply deferred inserts to unique indexes */\r
+                       rowChanger.finish();\r
+                       runChecker(false); //check for all  violations\r
+                       fireAfterTriggers();\r
+\r
+               }\r
+               else{\r
+               /* Apply deferred inserts to unique indexes */\r
+               rowChanger.finish();\r
+               }\r
+\r
+               cleanUp();\r
+    }\r
+\r
+\r
+       /**\r
+               @exception StandardException Standard Derby error policy\r
+       */\r
+       void setup() throws StandardException\r
+       {\r
+               super.setup();\r
+\r
+               /* decode lock mode */\r
+               lockMode = decodeLockMode(constants.lockMode);\r
+\r
+               boolean firstOpen = (rowChanger == null);\r
+\r
+               rowCount = 0;\r
+               \r
+               /* Cache query plan text for source, before it gets blown away */\r
+               if (lcc.getRunTimeStatisticsMode())\r
+               {\r
+                       /* savedSource nulled after run time statistics generation */\r
+                       savedSource = source;\r
+               }\r
+\r
+               /* Get or re-use the row changer.\r
+                * NOTE: We need to set ourself as the top result set\r
+                * if this is not the 1st execution.  (Done in constructor\r
+                * for 1st execution.)\r
+                */\r
+               if (firstOpen)\r
+               {\r
+                       rowChanger = lcc.getLanguageConnectionFactory().getExecutionFactory()\r
+                                            .getRowChanger( heapConglom, \r
+                                                                                constants.heapSCOCI, \r
+                                                                                heapDCOCI,\r
+                                                                                constants.irgs,\r
+                                                                                constants.indexCIDS,\r
+                                                                                constants.indexSCOCIs,\r
+                                                                                indexDCOCIs,\r
+                                                                                constants.numColumns,\r
+                                                                                tc,\r
+                                                                                constants.changedColumnIds,\r
+                                                                                constants.getBaseRowReadList(),\r
+                                                                                constants.getBaseRowReadMap(),\r
+                                                                                constants.getStreamStorableHeapColIds(),\r
+                                                                                activation);\r
+                       rowChanger.setIndexNames(constants.indexNames);\r
+               }\r
+               else\r
+               {\r
+                       lcc.getStatementContext().setTopResultSet(this, subqueryTrackingArray);\r
+               }\r
+\r
+\r
+               /* Open the RowChanger before the source ResultSet so that\r
+                * the store will see the RowChanger's lock as a covering lock\r
+                * if it is a table lock.\r
+                */\r
+               rowChanger.open(lockMode);\r
+\r
+               if (numOpens++ == 0)\r
+               {\r
+                       source.openCore();\r
+               }\r
+               else\r
+               {\r
+                       source.reopenCore();\r
+               }\r
+\r
+               /* The source does not know whether or not we are doing a\r
+                * deferred mode update.  If we are, then we must clear the\r
+                * index scan info from the activation so that the row changer\r
+                * does not re-use that information (which won't be valid for\r
+                * a deferred mode update).\r
+                */\r
+               if (deferred)\r
+               {\r
+                       activation.clearIndexScanInfo();\r
+               }\r
+\r
+               if (fkInfoArray != null)\r
+               {\r
+                       if (riChecker == null)\r
+                       {\r
+                               riChecker = new RISetChecker(tc, fkInfoArray);\r
+                       }\r
+                       else\r
+                       {\r
+                               riChecker.reopen();\r
+                       }\r
+               }\r
+\r
+               if (deferred)\r
+               {\r
+                       /* Allocate the temporary rows and get result description\r
+                        * if this is the 1st time that we are executing.\r
+                        */\r
+                       if (firstOpen)\r
+                       {\r
+                               deferredTempRow = RowUtil.getEmptyValueRow(numberOfBaseColumns+1, lcc);\r
+                               oldDeletedRow = RowUtil.getEmptyValueRow(numberOfBaseColumns, lcc);\r
+                               triggerResultDescription = (resultDescription != null) ?\r
+                                                                       resultDescription.truncateColumns(numberOfBaseColumns+1) :\r
+                                                                       null;\r
+                       }\r
+\r
+                       Properties properties = new Properties();\r
+\r
+                       // Get the properties on the heap\r
+                       rowChanger.getHeapConglomerateController().getInternalTablePropertySet(properties);\r
+                       if(beforeUpdateCopyRequired){\r
+                               deletedRowHolder =\r
+                                       new TemporaryRowHolderImpl(activation, properties,\r
+                                                                                          triggerResultDescription);\r
+                       }\r
+                       insertedRowHolder =\r
+                               new TemporaryRowHolderImpl(activation, properties,\r
+                                                                                  triggerResultDescription);\r
+\r
+                       rowChanger.setRowHolder(insertedRowHolder);\r
+               }\r
+\r
+       }       \r
+\r
+       /* Following 2 methods are for checking and make sure we don't have one un-objectified stream\r
+        * to be inserted into 2 temp table rows for deferred update.  Otherwise it would cause problem\r
+        * when writing to disk using the stream a second time.  In other cases we don't want to\r
+        * unnecessarily objectify the stream. beetle 4896.\r
+        */\r
+       private FormatableBitSet checkStreamCols()\r
+       {\r
+               DataValueDescriptor[] cols = row.getRowArray();\r
+               FormatableBitSet streamCols = null;\r
+               for (int i = 0; i < numberOfBaseColumns; i++)\r
+               {\r
+                       if (cols[i+numberOfBaseColumns] instanceof StreamStorable)  //check new values\r
+                       {\r
+                               if (streamCols == null) streamCols = new FormatableBitSet(numberOfBaseColumns);\r
+                               streamCols.set(i);\r
+                       }\r
+               }\r
+               return streamCols;\r
+       }\r
+\r
+       private void objectifyStream(ExecRow tempRow, FormatableBitSet streamCols) throws StandardException\r
+       {\r
+               DataValueDescriptor[] cols = tempRow.getRowArray();\r
+               for (int i = 0; i < numberOfBaseColumns; i++)\r
+               {\r
+                       if (cols[i] != null && streamCols.get(i))\r
+                               ((StreamStorable)cols[i]).loadStream();\r
+               }\r
+       }\r
+\r
+       public boolean collectAffectedRows() throws StandardException\r
+       {\r
+\r
+               boolean rowsFound = false;\r
+               row = getNextRowCore(source);\r
+               if (row!=null)\r
+                       rowsFound = true;\r
+               else\r
+               {\r
+                       activation.addWarning(\r
+                                               StandardException.newWarning(\r
+                                                       SQLState.LANG_NO_ROW_FOUND));\r
+               }\r
+\r
+               //beetle 3865, update cursor use index.\r
+               TableScanResultSet tableScan = (TableScanResultSet) activation.getForUpdateIndexScan();\r
+               boolean notifyCursor = ((tableScan != null) && ! tableScan.sourceDrained);\r
+               boolean checkStream = (deferred && rowsFound && ! constants.singleRowSource);\r
+               FormatableBitSet streamCols = (checkStream ? checkStreamCols() : null);\r
+               checkStream = (streamCols != null);\r
+\r
+        while ( row != null )\r
+        {\r
+\r
+                       /* By convention, the last column in the result set for an\r
+                        * update contains a SQLRef containing the RowLocation of\r
+                        * the row to be updated.\r
+                        */\r
+\r
+                       /*\r
+                       ** If we're doing deferred update, write the new row and row\r
+                       ** location to the temporary conglomerate.  If we're not doing\r
+                       ** deferred update, update the permanent conglomerates now\r
+                       ** using the RowChanger.\r
+                       */\r
+                       if (deferred)\r
+                       {\r
+                               /*\r
+                               ** If we have a before trigger, we must evaluate the \r
+                               ** check constraint after we have executed the trigger.\r
+                               ** Note that we have compiled checkGM accordingly (to\r
+                               ** handle the different row shape if we are evaluating\r
+                               ** against the input result set or a temporary row holder\r
+                               ** result set).\r
+                               */\r
+                               if (triggerInfo == null)\r
+                               {\r
+                                       evaluateCheckConstraints( checkGM, activation );\r
+                               }\r
+\r
+                               /*\r
+                               ** We are going to only save off the updated\r
+                               ** columns and the RID.  For a trigger, all columns\r
+                               ** were marked as needed so we'll copy them all.\r
+                               */\r
+                               RowUtil.copyRefColumns(deferredTempRow,\r
+                                                                                       row,\r
+                                                                                       numberOfBaseColumns,\r
+                                                                                       numberOfBaseColumns + 1);\r
+                               if (checkStream)\r
+                                       objectifyStream(deferredTempRow, streamCols);\r
+\r
+                               insertedRowHolder.insert(deferredTempRow); \r
+\r
+                               /*\r
+                               ** Grab a copy of the row to delete.  We are\r
+                               ** going to use this for deferred RI checks.\r
+                               */\r
+                               if(beforeUpdateCopyRequired)\r
+                               {\r
+                                       RowUtil.copyRefColumns(oldDeletedRow,\r
+                                                                                  row,\r
+                                                                                  numberOfBaseColumns);\r
+\r
+                                       deletedRowHolder.insert(oldDeletedRow);\r
+                               }\r
+\r
+                               /*\r
+                               ** If we haven't already, lets get a template to\r
+                               ** use as a template for our rescan of the base table.\r
+                               ** Do this now while we have a real row to use\r
+                               ** as a copy.\r
+                               **\r
+                               ** There is one less column in the base row than\r
+                               ** there is in source row, because the base row\r
+                               ** doesn't contain the row location.\r
+                               */\r
+                               if (deferredBaseRow == null)\r
+                               {\r
+                                       deferredBaseRow = RowUtil.getEmptyValueRow(numberOfBaseColumns, lcc);\r
+                       \r
+                                       RowUtil.copyCloneColumns(deferredBaseRow, row, \r
+                                                                                       numberOfBaseColumns);\r
+\r
+                                       /*\r
+                                       ** While we're here, let's also create a sparse row for\r
+                                       ** fetching from the store.\r
+                                       */\r
+                                       deferredSparseRow = makeDeferredSparseRow(deferredBaseRow,\r
+                                                                                                                               baseRowReadList,\r
+                                                                                                                               lcc);\r
+                               }\r
+                       }\r
+                       else\r
+                       {\r
+                               evaluateCheckConstraints( checkGM, activation );\r
+\r
+                               /* Get the RowLocation to update \r
+                               * NOTE - Column #s in the Row are 1 based.\r
+                               */\r
+                               RowLocation baseRowLocation = (RowLocation)\r
+                                       (row.getColumn(resultWidth)).getObject();\r
+\r
+                               RowUtil.copyRefColumns(newBaseRow,\r
+                                                                               row,\r
+                                                                               numberOfBaseColumns,\r
+                                                                               numberOfBaseColumns);\r
+\r
+                               if (riChecker != null)\r
+                               {\r
+                                       /*\r
+                                       ** Make sure all foreign keys in the new row\r
+                                       ** are maintained.  Note that we don't bother \r
+                                       ** checking primary/unique keys that are referenced\r
+                                       ** here.  The reason is that if we are updating\r
+                                       ** a referenced key, we'll be updating in deferred\r
+                                       ** mode, so we wont get here.\r
+                                       */\r
+                                       riChecker.doFKCheck(newBaseRow);\r
+                               }\r
+\r
+                               source.updateRow(newBaseRow);\r
+                               rowChanger.updateRow(row,newBaseRow,baseRowLocation);\r
+\r
+                               //beetle 3865, update cursor use index.\r
+                               if (notifyCursor)\r
+                                       notifyForUpdateCursor(row.getRowArray(),newBaseRow.getRowArray(),baseRowLocation,\r
+                                                                                       tableScan);\r
+                       }\r
+\r
+                       rowCount++;\r
+\r
+                       // No need to do a next on a single row source\r
+                       if (constants.singleRowSource)\r
+                       {\r
+                               row = null;\r
+                       }\r
+                       else\r
+                       {\r
+                               row = getNextRowCore(source);\r
+                       }\r
+               }\r
+\r
+               return rowsFound;\r
+       }\r
+\r
+       /* beetle 3865, updateable cursor use index. If the row we are updating has new value that\r
+        * falls into the direction of the index scan of the cursor, we save this rid into a hash table\r
+        * (for fast search), so that when the cursor hits it again, it knows to skip it.  When we get\r
+        * to a point that the hash table is full, we scan forward the cursor until one of two things\r
+        * happen: (1) we hit a record whose rid is in the hash table (we went through it already, so\r
+        * skip it), we remove it from hash table, so that we can continue to use hash table. OR, (2) the scan\r
+        * forward hit the end.  If (2) happens, we can de-reference the hash table to make it available\r
+        * for garbage collection.  We save the future row id's in a virtual mem heap.  In any case,\r
+        * next read will use a row id that we saved.\r
+        */\r
+       private void notifyForUpdateCursor(DataValueDescriptor[] row, DataValueDescriptor[] newBaseRow,\r
+                                                                               RowLocation rl, TableScanResultSet tableScan)\r
+               throws StandardException\r
+       {\r
+               int[] indexCols = tableScan.indexCols;\r
+               int[] changedCols = constants.changedColumnIds;\r
+               boolean placedForward = false, ascending, decided = false, overlap = false;\r
+               int basePos, k;\r
+               /* first of all, we see if there's overlap between changed column ids and index key\r
+                * columns.  If so, we see if the new update value falls into the future range of the\r
+                * index scan, if so, we need to save it in hash table.\r
+                */\r
+               for (int i = 0; i < indexCols.length; i++)\r
+               {\r
+                       basePos = indexCols[i];\r
+                       if (basePos > 0)\r
+                               ascending = true;\r
+                       else\r
+                       {\r
+                               ascending = false;\r
+                               basePos = -basePos;\r
+                       }\r
+                       for (int j = 0; j < changedCols.length; j++)\r
+                       {\r
+                               if (basePos == changedCols[j])\r
+                               {\r
+                                       decided = true;         //we pretty much decided if new row falls in front\r
+                                                                               //of the cursor or behind\r
+                                       /* the row and newBaseRow we get are compact base row that only have\r
+                                        * referenced columns.  Our "basePos" is index in sparse heap row, so\r
+                                        * we need the BaseRowReadMap to map into the compact row.\r
+                                        */\r
+                                       int[] map = constants.getBaseRowReadMap();\r
+                                       if (map == null)\r
+                                               k = basePos - 1;\r
+                                       else\r
+                                               k =  map[basePos - 1];\r
+\r
+                                       DataValueDescriptor key;\r
+                                       /* We need to compare with saved most-forward cursor scan key if we\r
+                                        * are reading records from the saved RowLocation temp table (instead\r
+                                        * of the old column value) because we only care if new update value\r
+                                        * jumps forward the most-forward scan key.\r
+                                        */\r
+                                       if (tableScan.compareToLastKey)\r
+                                               key = tableScan.lastCursorKey.getColumn(i + 1);\r
+                                       else\r
+                                               key = row[k];\r
+\r
+                                       /* Starting from the first index key column forward, we see if the direction\r
+                                        * of the update change is consistent with the direction of index scan.\r
+                                        * If so, we save it in hash table.\r
+                                        */\r
+                                       if ((ascending && key.greaterThan(newBaseRow[k], key).equals(true)) ||\r
+                                               (!ascending && key.lessThan(newBaseRow[k], key).equals(true)))\r
+                                               placedForward = true;\r
+                                       else if (key.equals(newBaseRow[k], key).equals(true))\r
+                                       {\r
+                                               decided = false;\r
+                                               overlap = true;\r
+                                       }\r
+                                       break;\r
+                               }\r
+                       }\r
+                       if (decided)  // already decided if new row falls in front or behind\r
+                               break;\r
+               }\r
+               /* If index row gets updated but key value didn't actually change, we still\r
+                * put it in hash table because it can either fall in front or behind.  This\r
+                * can happen if the update explicitly sets a value, but same as old.\r
+                */\r
+               if (overlap && !decided)\r
+                       placedForward = true;\r
+\r
+               if (placedForward)              // add it to hash table\r
+               {\r
+                       /* determining initial capacity of hash table from a few factors:\r
+                        * (1) user specified MAX_MEMORY_PER_TABLE property, (2) min value 100\r
+                        * (3) optimizer estimated row count.  We want to avoid re-hashing if\r
+                        * possible, for performance reason, yet don't waste space.  If initial\r
+                        * capacity is greater than max size divided by load factor, no rehash\r
+                        * is ever needed.\r
+                        */\r
+                       int maxCapacity = lcc.getOptimizerFactory().getMaxMemoryPerTable() / 16;\r
+                       if (maxCapacity < 100)\r
+                               maxCapacity = 100;\r
+\r
+                       if (tableScan.past2FutureTbl == null)\r
+                       {\r
+                               double rowCount = tableScan.getEstimatedRowCount();\r
+                               int initCapacity = 32 * 1024;\r
+                               if (rowCount > 0.0)\r
+                               {\r
+                                       rowCount = rowCount / 0.75 + 1.0;       // load factor\r
+                                       if (rowCount < initCapacity)\r
+                                               initCapacity = (int) rowCount;\r
+                               }\r
+                               if (maxCapacity < initCapacity)\r
+                                       initCapacity = maxCapacity;\r
+\r
+                               tableScan.past2FutureTbl = new Hashtable(initCapacity);\r
+                       }\r
+\r
+                       Hashtable past2FutureTbl = tableScan.past2FutureTbl;\r
+                       /* If hash table is not full, we add it in.  The key of the hash entry\r
+                        * is the string value of the RowLocation.  If the hash table is full,\r
+                        * as the comments above this function say, we scan forward.\r
+                        *\r
+                        * Need to save a clone because when we get cached currentRow, "rl" shares the\r
+                        * same reference, so is changed at the same time.\r
+                        */\r
+                       RowLocation updatedRL = (RowLocation) rl.getClone();\r
+\r
+                       if (past2FutureTbl.size() < maxCapacity)\r
+                               past2FutureTbl.put(updatedRL, updatedRL);\r
+                       else\r
+                       {\r
+                               tableScan.skipFutureRowHolder = true;\r
+                               ExecRow rlRow = new ValueRow(1);\r
+\r
+                               for (;;)\r
+                               {\r
+                                       ExecRow aRow = tableScan.getNextRowCore();\r
+                                       if (aRow == null)\r
+                                       {\r
+                                               tableScan.sourceDrained = true;\r
+                                               tableScan.past2FutureTbl = null;        // de-reference for garbage coll.\r
+                                               break;\r
+                                       }\r
+                                       RowLocation rowLoc = (RowLocation) aRow.getColumn(aRow.nColumns());\r
+\r
+                                       if (updatedRL.equals(rowLoc))  //this row we are updating jumped forward\r
+                                       {\r
+                                               saveLastCusorKey(tableScan, aRow);\r
+                                               break;  // don't need to worry about adding this row to hash any more\r
+                                       }\r
+\r
+                                       if (tableScan.futureForUpdateRows == null)\r
+                                       {\r
+                                               // virtual memory heap. In-memory part size 100. With the co-operation\r
+                                               // of hash table and in-memory part of heap (hash table shrinks while\r
+                                               // in-memory heap grows), hopefully we never spill temp table to disk.\r
+\r
+                                               tableScan.futureForUpdateRows = new TemporaryRowHolderImpl\r
+                                                       (activation, null, null, 100, false, true);\r
+                                       }\r
+\r
+                                       rlRow.setColumn(1, rowLoc);\r
+                                       tableScan.futureForUpdateRows.insert(rlRow);\r
+                                       if (past2FutureTbl.size() < maxCapacity) //we got space in the hash table now, stop!\r
+                                       {\r
+                                               past2FutureTbl.put(updatedRL, updatedRL);\r
+                                               saveLastCusorKey(tableScan, aRow);\r
+                                               break;\r
+                                       }\r
+                               }\r
+                               tableScan.skipFutureRowHolder = false;\r
+                       }\r
+               }\r
+       }\r
+\r
+       private void saveLastCusorKey(TableScanResultSet tableScan, ExecRow aRow) throws StandardException\r
+       {\r
+               /* We save the most-forward cursor scan key where we are stopping, so\r
+                * that next time when we decide if we need to put an updated row id into\r
+                * hash table, we can compare with this key.  This is an optimization on\r
+                * memory usage of the hash table, otherwise it may be "leaking".\r
+                */\r
+               if (tableScan.lastCursorKey == null)\r
+                       tableScan.lastCursorKey = new ValueRow(aRow.nColumns() - 1);\r
+               for (int i = 1; i <= tableScan.lastCursorKey.nColumns(); i++)\r
+               {\r
+                       DataValueDescriptor aCol = aRow.getColumn(i);\r
+                       if (aCol != null)\r
+                               tableScan.lastCursorKey.setColumn(i, aCol.getClone());\r
+               }\r
+       }\r
+\r
+       void fireBeforeTriggers() throws StandardException\r
+       {\r
+               if (deferred)\r
+               {\r
+                       if (triggerInfo != null)\r
+                       {\r
+                               if (triggerActivator == null)\r
+                               {\r
+                               triggerActivator = new TriggerEventActivator(lcc, \r
+                                                                                       tc, \r
+                                                                                       constants.targetUUID,\r
+                                                                                       triggerInfo,\r
+                                                                                       TriggerExecutionContext.UPDATE_EVENT,\r
+                                                                                       activation, null);\r
+                               }\r
+                               else\r
+                               {\r
+                                       triggerActivator.reopen();\r
+                               }\r
+\r
+                               // fire BEFORE trigger, do this before checking constraints\r
+                               triggerActivator.notifyEvent(TriggerEvents.BEFORE_UPDATE, \r
+                                                                                               deletedRowHolder.getResultSet(),\r
+                                                                                               insertedRowHolder.getResultSet());\r
+\r
+                       }\r
+               }\r
+       }\r
+\r
+    void fireAfterTriggers() throws StandardException\r
+       {\r
+               if (deferred)\r
+               {\r
+                       if (triggerActivator != null)\r
+                       {\r
+                               triggerActivator.notifyEvent(TriggerEvents.AFTER_UPDATE, \r
+                                                                               deletedRowHolder.getResultSet(),\r
+                                                                               insertedRowHolder.getResultSet());\r
+                       }\r
+               }\r
+       }\r
+\r
+\r
+\r
+       void updateDeferredRows() throws StandardException\r
+       {\r
+               if (deferred)\r
+               {\r
+                       // we already have everything locked \r
+                       deferredBaseCC = \r
+                tc.openCompiledConglomerate(\r
+                    false,\r
+                    tc.OPENMODE_FORUPDATE|tc.OPENMODE_SECONDARY_LOCKED,\r
+                    lockMode,\r
+                    TransactionController.ISOLATION_SERIALIZABLE,\r
+                    constants.heapSCOCI,\r
+                    heapDCOCI);\r
+                       \r
+                       CursorResultSet rs = insertedRowHolder.getResultSet();\r
+                       try\r
+                       {\r
+                               /*\r
+                               ** We need to do a fetch doing a partial row\r
+                               ** read.  We need to shift our 1-based bit\r
+                               ** set to a zero based bit set like the store\r
+                               ** expects.\r
+                               */\r
+                               FormatableBitSet readBitSet = RowUtil.shift(baseRowReadList, 1);\r
+                               ExecRow deferredTempRow2;\r
+\r
+                               rs.open();\r
+                               while ((deferredTempRow2 = rs.getNextRow()) != null)\r
+                               {\r
+                                       /*\r
+                                       ** Check the constraint now if we have triggers.\r
+                                       ** Otherwise we evaluated them as we read the\r
+                                       ** rows in from the source.\r
+                                       */\r
+                                       if (triggerInfo != null)\r
+                                       {\r
+                                               source.setCurrentRow(deferredTempRow);\r
+                                               evaluateCheckConstraints(checkGM, activation);\r
+                                       }\r
+\r
+                                       /* \r
+                                       ** The last column is a Ref, which contains a \r
+                                       ** RowLocation.\r
+                                       */\r
+                                       DataValueDescriptor rlColumn = deferredTempRow2.getColumn(numberOfBaseColumns + 1);\r
+                                       RowLocation baseRowLocation = \r
+                                                       (RowLocation) (rlColumn).getObject();\r
+       \r
+                                       /* Get the base row at the given RowLocation */\r
+                                       boolean row_exists = \r
+                                               deferredBaseCC.fetch(\r
+                                                       baseRowLocation, deferredSparseRow.getRowArray(), \r
+                                                       readBitSet);\r
+\r
+                                       if (SanityManager.DEBUG)\r
+                                       {\r
+                                               SanityManager.ASSERT(row_exists, "did not find base row in deferred update");\r
+                                       }\r
+       \r
+                                       /*\r
+                                       ** Copy the columns from the temp row to the base row.\r
+                                       ** The base row has fewer columns than the temp row,\r
+                                       ** because it doesn't contain the row location.\r
+                                       */\r
+                                       RowUtil.copyRefColumns(newBaseRow,\r
+                                                                                       deferredTempRow2,\r
+                                                                                       numberOfBaseColumns);\r
+\r
+                                       rowChanger.updateRow(deferredBaseRow,\r
+                                                                               newBaseRow,\r
+                                                                               baseRowLocation);\r
+                                       source.updateRow(newBaseRow);\r
+                               }\r
+                       } finally\r
+                       {\r
+                               source.clearCurrentRow();\r
+                               rs.close();\r
+                       }\r
+               }\r
+       }\r
+\r
+\r
+       \r
+       void runChecker(boolean restrictCheckOnly) throws StandardException\r
+       {\r
+\r
+               /*\r
+               ** For a deferred update, make sure that there\r
+               ** aren't any primary keys that were removed which\r
+               ** are referenced.  \r
+               */\r
+               if (deferred && updatingReferencedKey)\r
+               {\r
+                       ExecRow deletedRow;\r
+                       CursorResultSet deletedRows; \r
+\r
+                       /*\r
+                       ** For each referenced key that was modified\r
+                       */\r
+                       for (int i = 0; i < fkInfoArray.length; i++)\r
+                       {\r
+                               if (fkInfoArray[i].type == FKInfo.FOREIGN_KEY)\r
+                               {\r
+                                       continue;\r
+                               }\r
+\r
+                               deletedRows = deletedRowHolder.getResultSet();\r
+                               try\r
+                               {\r
+                                       /*\r
+                                       ** For each delete row\r
+                                       */      \r
+                                       deletedRows.open();\r
+                                       while ((deletedRow = deletedRows.getNextRow()) != null)\r
+                                       {\r
+                                               if (!foundRow(deletedRow, \r
+                                                                               fkInfoArray[i].colArray, \r
+                                                                               insertedRowHolder))\r
+                                               {\r
+                                                       riChecker.doRICheck(i, deletedRow, restrictCheckOnly);\r
+                                               }\r
+                                       }       \r
+                               }\r
+                               finally\r
+                               {\r
+                                       deletedRows.close();\r
+                               }\r
+                       }\r
+               }\r
+\r
+               /*\r
+               ** For a deferred update, make sure that there\r
+               ** aren't any foreign keys that were added that\r
+               ** aren't referenced.  \r
+               */\r
+               if (deferred && updatingForeignKey)\r
+               {\r
+                       ExecRow insertedRow;\r
+                       CursorResultSet insertedRows; \r
+\r
+                       /*\r
+                       ** For each foreign key that was modified\r
+                       */\r
+                       for (int i = 0; i < fkInfoArray.length; i++)\r
+                       {\r
+                               if (fkInfoArray[i].type == FKInfo.REFERENCED_KEY)\r
+                               {\r
+                                       continue;\r
+                               }\r
+\r
+                               insertedRows = insertedRowHolder.getResultSet();\r
+                               try\r
+                               {\r
+                                       /*\r
+                                       ** For each inserted row\r
+                                       */      \r
+                                       insertedRows.open();\r
+                                       while ((insertedRow = insertedRows.getNextRow()) != null)\r
+                                       {\r
+                                               if (!foundRow(insertedRow, \r
+                                                                               fkInfoArray[i].colArray, \r
+                                                                               deletedRowHolder))\r
+                                               {\r
+                                                       riChecker.doRICheck(i, insertedRow, restrictCheckOnly);\r
+                                               }\r
+                                       }       \r
+                               }\r
+                               finally\r
+                               {\r
+                                       insertedRows.close();\r
+                               }\r
+                       }\r
+               }\r
+\r
+       }\r
+\r
+       public static boolean foundRow\r
+       (\r
+               ExecRow                                 checkRow, \r
+               int[]                                   colsToCheck,\r
+               TemporaryRowHolderImpl  rowHolder\r
+       )\r
+               throws StandardException\r
+       {\r
+               ExecRow                         scanRow;\r
+               boolean                         foundMatch = false;\r
+               Object[]                        checkRowArray = checkRow.getRowArray();\r
+               DataValueDescriptor     checkCol;\r
+               DataValueDescriptor     scanCol;\r
+\r
+               CursorResultSet rs = rowHolder.getResultSet();\r
+               try\r
+               {       \r
+                       /*\r
+                       ** For each inserted row\r
+                       */      \r
+                       rs.open();\r
+                       while ((scanRow = rs.getNextRow()) != null)\r
+                       {\r
+                               Object[] scanRowArray = scanRow.getRowArray();\r
+                               int i;\r
+                               for (i = 0; i < colsToCheck.length; i++)\r
+                               {\r
+                                       checkCol = (DataValueDescriptor)checkRowArray[colsToCheck[i]-1];\r
+                                       scanCol = (DataValueDescriptor)scanRowArray[colsToCheck[i]-1];\r
+\r
+                                       BooleanDataValue result = checkCol.equals(\r
+                                                                                       scanCol,\r
+                                                                                       checkCol); // result\r
+                                       if (!result.getBoolean())\r
+                                       {\r
+                                               break;\r
+                                       }\r
+                               }\r
+                               if (i == colsToCheck.length)\r
+                               {\r
+                                       foundMatch = true;\r
+                                       break;\r
+                               }       \r
+                       }\r
+               }\r
+               finally\r
+               {\r
+                       rs.close();\r
+               }\r
+               return foundMatch;\r
+       }\r
+\r
+\r
+       /**\r
+        * @see ResultSet#cleanUp\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+       public void     cleanUp() throws StandardException\r
+       { \r
+               numOpens = 0;\r
+\r
+               /* Close down the source ResultSet tree */\r
+               if (source != null)\r
+               {\r
+                       source.close();\r
+                       // cache source across open()s\r
+               }\r
+\r
+               if (triggerActivator != null)\r
+               {\r
+                       triggerActivator.cleanup();\r
+                       // cache triggerActivator across open()s\r
+               }\r
+\r
+               if (rowChanger != null)\r
+                       rowChanger.close();\r
+\r
+               if (deferredBaseCC != null)\r
+                       deferredBaseCC.close();\r
+               deferredBaseCC = null;\r
+\r
+               if (insertedRowHolder != null)\r
+               {\r
+                       insertedRowHolder.close();\r
+               }\r
+       \r
+               if (deletedRowHolder != null)\r
+               {\r
+                       deletedRowHolder.close();\r
+               }\r
+\r
+               if (riChecker != null)\r
+               {\r
+                       riChecker.close();\r
+                       // cache riChecker across open()s\r
+               }\r
+\r
+               super.close();\r
+\r
+               endTime = getCurrentTimeMillis();\r
+       }\r
+\r
+       void rowChangerFinish() throws StandardException\r
+       {\r
+               rowChanger.finish();\r
+       }\r
+\r
+}\r