Adding JMCR-Stable version
[Benchmarks_CSolver.git] / JMCR-Stable / real-world application / MyDerby-10.3 / java / engine / org / apache / derby / impl / sql / execute / IndexChanger.java
diff --git a/JMCR-Stable/real-world application/MyDerby-10.3/java/engine/org/apache/derby/impl/sql/execute/IndexChanger.java b/JMCR-Stable/real-world application/MyDerby-10.3/java/engine/org/apache/derby/impl/sql/execute/IndexChanger.java
new file mode 100644 (file)
index 0000000..1f8869b
--- /dev/null
@@ -0,0 +1,702 @@
+/*\r
+\r
+   Derby - Class org.apache.derby.impl.sql.execute.IndexChanger\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.Properties;\r
+\r
+import org.apache.derby.catalog.UUID;\r
+import org.apache.derby.iapi.error.StandardException;\r
+import org.apache.derby.iapi.reference.SQLState;\r
+import org.apache.derby.iapi.services.i18n.MessageService;\r
+import org.apache.derby.iapi.services.io.FormatableBitSet;\r
+import org.apache.derby.iapi.services.monitor.Monitor;\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.conn.LanguageConnectionContext;\r
+import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor;\r
+import org.apache.derby.iapi.sql.dictionary.ConstraintDescriptor;\r
+import org.apache.derby.iapi.sql.dictionary.DataDictionary;\r
+import org.apache.derby.iapi.sql.dictionary.IndexRowGenerator;\r
+import org.apache.derby.iapi.sql.dictionary.TableDescriptor;\r
+import org.apache.derby.iapi.sql.execute.CursorResultSet;\r
+import org.apache.derby.iapi.sql.execute.ExecIndexRow;\r
+import org.apache.derby.iapi.sql.execute.ExecRow;\r
+import org.apache.derby.iapi.store.access.ConglomerateController;\r
+import org.apache.derby.iapi.store.access.DynamicCompiledOpenConglomInfo;\r
+import org.apache.derby.iapi.store.access.ScanController;\r
+import org.apache.derby.iapi.store.access.StaticCompiledOpenConglomInfo;\r
+import org.apache.derby.iapi.store.access.TransactionController;\r
+import org.apache.derby.iapi.types.DataValueDescriptor;\r
+import org.apache.derby.iapi.types.RowLocation;\r
+\r
+/**\r
+  Perform Index maintenace associated with DML operations for a single index.\r
+  */\r
+public class IndexChanger\r
+{\r
+       private IndexRowGenerator irg;\r
+       //Index Conglomerate ID\r
+       private long indexCID;\r
+       private DynamicCompiledOpenConglomInfo indexDCOCI;\r
+       private StaticCompiledOpenConglomInfo indexSCOCI;\r
+       private String indexName;\r
+       private ConglomerateController baseCC;\r
+       private TransactionController tc;\r
+       private int lockMode;\r
+       private FormatableBitSet baseRowReadMap;\r
+\r
+       private ConglomerateController indexCC = null;\r
+       private ScanController indexSC = null;\r
+\r
+       private LanguageConnectionContext lcc;\r
+\r
+       //\r
+       //Index rows used by this module to perform DML.\r
+       private ExecIndexRow ourIndexRow = null;\r
+       private ExecIndexRow ourUpdatedIndexRow = null;\r
+\r
+       private TemporaryRowHolderImpl  rowHolder = null;\r
+       private boolean                                 rowHolderPassedIn;\r
+       private int                                             isolationLevel;\r
+       private Activation                              activation;\r
+       private boolean                                 ownIndexSC = true;\r
+\r
+       /**\r
+         Create an IndexChanger\r
+\r
+         @param irg the IndexRowGenerator for the index.\r
+         @param indexCID the conglomerate id for the index.\r
+         @param indexSCOCI the SCOCI for the idexes. \r
+         @param indexDCOCI the DCOCI for the idexes. \r
+         @param baseCC the ConglomerateController for the base table.\r
+         @param tc                     The TransactionController\r
+         @param lockMode       The lock mode (granularity) to use\r
+         @param baseRowReadMap Map of columns read in.  1 based.\r
+         @param isolationLevel Isolation level to use.\r
+         @param activation     Current activation\r
+\r
+         @exception StandardException          Thrown on error\r
+         */\r
+       public IndexChanger\r
+       (\r
+               IndexRowGenerator               irg,\r
+               long                                    indexCID,\r
+           StaticCompiledOpenConglomInfo indexSCOCI,\r
+               DynamicCompiledOpenConglomInfo indexDCOCI,\r
+               String                                  indexName,\r
+               ConglomerateController  baseCC,\r
+               TransactionController   tc,\r
+               int                                     lockMode,\r
+               FormatableBitSet                                        baseRowReadMap,\r
+               int                                             isolationLevel,\r
+               Activation                              activation\r
+       )\r
+                throws StandardException\r
+       {\r
+               this.irg = irg;\r
+               this.indexCID = indexCID;\r
+               this.indexSCOCI = indexSCOCI;\r
+               this.indexDCOCI = indexDCOCI;\r
+               this.baseCC = baseCC;\r
+               this.tc = tc;\r
+               this.lockMode = lockMode;\r
+               this.baseRowReadMap = baseRowReadMap;\r
+               this.rowHolderPassedIn = false;\r
+               this.isolationLevel = isolationLevel;\r
+               this.activation = activation;\r
+               this.indexName = indexName;\r
+\r
+               // activation will be null when called from DataDictionary\r
+               if (activation != null && activation.getIndexConglomerateNumber() == indexCID)\r
+               {\r
+                       ownIndexSC = false;\r
+               }\r
+       \r
+               if (SanityManager.DEBUG)\r
+               {\r
+                       SanityManager.ASSERT(tc != null, \r
+                               "TransactionController argument to constructor is null");\r
+                       SanityManager.ASSERT(irg != null, \r
+                               "IndexRowGenerator argument to constructor is null");\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Set the row holder for this changer to use.\r
+        * If the row holder is set, it wont bother \r
+        * saving copies of rows needed for deferred\r
+        * processing.  Also, it will never close the\r
+        * passed in rowHolder.\r
+        *\r
+        * @param rowHolder     the row holder\r
+        */\r
+       public void setRowHolder(TemporaryRowHolderImpl rowHolder)\r
+       {\r
+               this.rowHolder = rowHolder;\r
+               rowHolderPassedIn = (rowHolder != null);\r
+       }\r
+\r
+       /**\r
+        * Propagate the heap's ConglomerateController to\r
+        * this index changer.\r
+        *\r
+        * @param baseCC        The heap's ConglomerateController.\r
+        */\r
+       public void setBaseCC(ConglomerateController baseCC)\r
+       {\r
+               this.baseCC = baseCC;\r
+       }\r
+\r
+       /**\r
+         Set the column values for 'ourIndexRow' to refer to \r
+         a base table row and location provided by the caller.\r
+         The idea here is to \r
+         @param baseRow a base table row.\r
+         @param baseRowLoc baseRowLoc baseRow's location\r
+         @exception StandardException          Thrown on error\r
+         */\r
+       private void setOurIndexRow(ExecRow baseRow,\r
+                                                               RowLocation baseRowLoc)\r
+                throws StandardException\r
+       {\r
+                       if (ourIndexRow == null)\r
+                               ourIndexRow = irg.getIndexRowTemplate();\r
+               \r
+                       irg.getIndexRow(baseRow, baseRowLoc, ourIndexRow, baseRowReadMap);\r
+       }\r
+\r
+       /**\r
+         Set the column values for 'ourUpdatedIndexRow' to refer to \r
+         a base table row and location provided by the caller.\r
+         The idea here is to \r
+         @param baseRow a base table row.\r
+         @param baseRowLoc baseRowLoc baseRow's location\r
+         @exception StandardException          Thrown on error\r
+         */\r
+       private void setOurUpdatedIndexRow(ExecRow baseRow,\r
+                                                               RowLocation baseRowLoc)\r
+                throws StandardException\r
+       {\r
+                       if (ourUpdatedIndexRow == null)\r
+                               ourUpdatedIndexRow = irg.getIndexRowTemplate();\r
+               \r
+                       irg.getIndexRow(baseRow, baseRowLoc, ourUpdatedIndexRow, baseRowReadMap);\r
+       }\r
+\r
+       /**\r
+        * Determine whether or not any columns in the current index\r
+        * row are being changed by the update.  No need to update the\r
+        * index if no columns changed.\r
+        *\r
+        * @return Nothing.\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+       private boolean indexRowChanged()\r
+               throws StandardException\r
+       {\r
+               int numColumns = ourIndexRow.nColumns();\r
+               for (int index = 1; index <= numColumns; index++)\r
+               {\r
+                       DataValueDescriptor oldOrderable = ourIndexRow.getColumn(index);\r
+                       DataValueDescriptor newOrderable = ourUpdatedIndexRow.getColumn(index);\r
+                       if (! (oldOrderable.compare(DataValueDescriptor.ORDER_OP_EQUALS, newOrderable, true, true)))\r
+                       {\r
+                               return true;\r
+                       }\r
+               }\r
+               return false;\r
+       }\r
+\r
+       private ExecIndexRow getDeferredIndexRowTemplate(ExecRow baseRow,\r
+                                                                                                       RowLocation baseRowLoc)\r
+                throws StandardException\r
+       {\r
+               ExecIndexRow    template;\r
+\r
+               template = irg.getIndexRowTemplate();\r
+\r
+               irg.getIndexRow(baseRow, baseRowLoc, template, baseRowReadMap);\r
+\r
+               return template;\r
+       }\r
+\r
+       /**\r
+         Position our index scan to 'ourIndexRow'.\r
+\r
+         <P>This creates the scan the first time it is called.\r
+\r
+         @exception StandardException          Thrown on error\r
+         */\r
+       private void setScan()\r
+                throws StandardException\r
+       {\r
+               /* Get the SC from the activation if re-using */\r
+               if (! ownIndexSC)\r
+               {\r
+                       indexSC = activation.getIndexScanController();\r
+               }\r
+               else if (indexSC == null)\r
+               {\r
+                       RowLocation templateBaseRowLocation = baseCC.newRowLocationTemplate();\r
+                       /* DataDictionary doesn't have compiled info */\r
+                       if (indexSCOCI == null)\r
+                       {\r
+                               indexSC = \r
+                           tc.openScan(\r
+                                     indexCID,\r
+                                         false,                       /* hold */\r
+                                             TransactionController.OPENMODE_FORUPDATE, /* forUpdate */\r
+                                                 lockMode,\r
+                             isolationLevel,\r
+                                 (FormatableBitSet)null,                                       /* all fields */\r
+                                     ourIndexRow.getRowArray(),    /* startKeyValue */\r
+                                         ScanController.GE,            /* startSearchOp */\r
+                                             null,                         /* qualifier */\r
+                                                 ourIndexRow.getRowArray(),    /* stopKeyValue */\r
+                                               ScanController.GT             /* stopSearchOp */\r
+                             );\r
+                       }\r
+                       else\r
+                       {\r
+                               indexSC = \r
+                           tc.openCompiledScan(\r
+                                         false,                       /* hold */\r
+                                             TransactionController.OPENMODE_FORUPDATE, /* forUpdate */\r
+                                                 lockMode,\r
+                             isolationLevel,\r
+                                 (FormatableBitSet)null,                                       /* all fields */\r
+                                     ourIndexRow.getRowArray(),    /* startKeyValue */\r
+                                         ScanController.GE,            /* startSearchOp */\r
+                                             null,                         /* qualifier */\r
+                                                 ourIndexRow.getRowArray(),    /* stopKeyValue */\r
+                                                 ScanController.GT,             /* stopSearchOp */\r
+                                                 indexSCOCI,\r
+                                                 indexDCOCI\r
+                             );\r
+            }\r
+               }\r
+               else\r
+               {\r
+                       indexSC.reopenScan(\r
+                                                          ourIndexRow.getRowArray(),                   /* startKeyValue */\r
+                                                          ScanController.GE,   /* startSearchOperator */\r
+                                                          null,                    /* qualifier */\r
+                                                          ourIndexRow.getRowArray(),                   /* stopKeyValue */\r
+                                                          ScanController.GT    /* stopSearchOperator */\r
+                                                          );\r
+               }\r
+       }\r
+\r
+       /**\r
+         Close our index Conglomerate Controller\r
+         */\r
+       private void closeIndexCC()\r
+        throws StandardException\r
+       {\r
+               if (indexCC != null)\r
+                       indexCC.close();\r
+               indexCC = null;\r
+       }\r
+\r
+       /**\r
+         Close our index ScanController.\r
+         */\r
+       private void closeIndexSC()\r
+        throws StandardException\r
+       {\r
+               /* Only consider closing index SC if we own it. */\r
+               if (ownIndexSC && indexSC != null)\r
+               {\r
+                       indexSC.close();\r
+                       indexSC = null;\r
+               }\r
+       }\r
+\r
+       /**\r
+         Delete a row from our index. This assumes our index ScanController\r
+         is positioned before the row by setScan if we own the SC, otherwise\r
+         it is positioned on the row by the underlying index scan.\r
+         \r
+         <P>This verifies the row exists and is unique.\r
+         \r
+         @exception StandardException          Thrown on error\r
+         */\r
+       private void doDelete()\r
+                throws StandardException\r
+       {\r
+               if (ownIndexSC)\r
+               {\r
+                       if (! indexSC.next())\r
+                       {\r
+                // This means that the entry for the index does not exist, this\r
+                // is a serious problem with the index.  Past fixed problems\r
+                // like track 3703 can leave db's in the field with this problem\r
+                // even though the bug in the code which caused it has long \r
+                // since been fixed.  Then the problem can surface months later\r
+                // when the customer attempts to upgrade.  By "ignoring" the\r
+                // missing row here the problem is automatically "fixed" and\r
+                // since the code is trying to delete the row anyway it doesn't\r
+                // seem like such a bad idea.  It also then gives a tool to \r
+                // support to be able to fix some system catalog problems where\r
+                // they can delete the base rows by dropping the system objects\r
+                // like stored statements.\r
+\r
+                               if (SanityManager.DEBUG)\r
+                                       SanityManager.THROWASSERT(\r
+                        "Index row "+RowUtil.toString(ourIndexRow)+\r
+                        " not found in conglomerateid " + indexCID +\r
+                        "Current scan = " + indexSC);\r
+\r
+                Object[] args = new Object[2];\r
+                args[0] = ourIndexRow.getRowArray()[ourIndexRow.getRowArray().length - 1];\r
+                args[1] = new Long(indexCID);\r
+\r
+                Monitor.getStream().println(MessageService.getCompleteMessage(\r
+                    SQLState.LANG_IGNORE_MISSING_INDEX_ROW_DURING_DELETE, \r
+                    args));\r
+\r
+                // just return indicating the row has been deleted.\r
+                return;\r
+                       }\r
+               }\r
+\r
+        indexSC.delete();\r
+       }\r
+\r
+       /**\r
+         Insert a row into our indes.\r
+         \r
+         <P>This opens our index ConglomeratController the first time it\r
+         is called. \r
+         \r
+         @exception StandardException          Thrown on error\r
+         */\r
+       private void doInsert()\r
+                throws StandardException\r
+       {\r
+               insertAndCheckDups(ourIndexRow);\r
+       }\r
+\r
+       /**\r
+         Insert a row into the temporary conglomerate\r
+         \r
+         <P>This opens our deferred ConglomeratController the first time it\r
+         is called.\r
+         \r
+         @exception StandardException          Thrown on error\r
+         */\r
+       private void doDeferredInsert()\r
+                throws StandardException\r
+       {\r
+               if (rowHolder == null)\r
+               {\r
+                       Properties properties = new Properties();\r
+\r
+                       // Get the properties on the index\r
+                       openIndexCC().getInternalTablePropertySet(properties);\r
+\r
+                       /*\r
+                       ** Create our row holder.  it is ok to skip passing\r
+                       ** in the result description because if we don't already\r
+                       ** have a row holder, then we are the only user of the\r
+                       ** row holder (the description is needed when the row\r
+                       ** holder is going to be handed to users for triggers).\r
+                       */\r
+                       rowHolder = new TemporaryRowHolderImpl(activation, properties,\r
+                                                                                                  (ResultDescription) null);\r
+               }\r
+\r
+               /*\r
+               ** If the user of the IndexChanger already\r
+               ** had a row holder, then we don't need to\r
+               ** bother saving deferred inserts -- they\r
+               ** have already done so.        \r
+               */\r
+               if (!rowHolderPassedIn)\r
+               {\r
+                       rowHolder.insert(ourIndexRow);\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Insert the given row into the given conglomerate and check for duplicate\r
+        * key error.\r
+        *\r
+        * @param row   The row to insert\r
+        *\r
+        * @exception StandardException         Thrown on duplicate key error\r
+        */\r
+       private void insertAndCheckDups(ExecIndexRow row)\r
+                               throws StandardException\r
+       {\r
+               openIndexCC();\r
+\r
+               int insertStatus = indexCC.insert(row.getRowArray());\r
+\r
+               if (insertStatus == ConglomerateController.ROWISDUPLICATE)\r
+               {\r
+                       /*\r
+                       ** We have a duplicate key error. \r
+                       */\r
+                       String indexOrConstraintName = indexName;\r
+                       // now get table name, and constraint name if needed\r
+                       LanguageConnectionContext lcc =\r
+                                       activation.getLanguageConnectionContext();\r
+                       DataDictionary dd = lcc.getDataDictionary();\r
+                       //get the descriptors\r
+                       ConglomerateDescriptor cd = dd.getConglomerateDescriptor(indexCID);\r
+\r
+                       UUID tableID = cd.getTableID();\r
+                       TableDescriptor td = dd.getTableDescriptor(tableID);\r
+                       String tableName = td.getName();\r
+                       \r
+                       if (indexOrConstraintName == null) // no index name passed in\r
+                       {\r
+                               ConstraintDescriptor conDesc = dd.getConstraintDescriptor(td,\r
+                                                                      cd.getUUID());\r
+                               indexOrConstraintName = conDesc.getConstraintName();\r
+                       }               \r
+\r
+                       StandardException se = \r
+                               StandardException.newException(\r
+                               SQLState.LANG_DUPLICATE_KEY_CONSTRAINT, indexOrConstraintName, tableName);\r
+                       throw se;\r
+               }\r
+               if (SanityManager.DEBUG)\r
+               {\r
+                       if (insertStatus != 0)\r
+                       {\r
+                               SanityManager.THROWASSERT("Unknown insert status " + insertStatus);\r
+                       }\r
+               }\r
+       }\r
+\r
+\r
+       /**\r
+        * Open the ConglomerateController for this index if it isn't open yet.\r
+        *\r
+        * @return The ConglomerateController for this index.\r
+        *\r
+        * @exception StandardException         Thrown on duplicate key error\r
+        */\r
+       private ConglomerateController openIndexCC()\r
+               throws StandardException\r
+       {\r
+               if (indexCC == null)\r
+               {\r
+                       /* DataDictionary doesn't have compiled info */\r
+                       if (indexSCOCI == null)\r
+                       {\r
+                               indexCC = \r
+                           tc.openConglomerate(\r
+                                               indexCID,\r
+                        false,\r
+                                   (TransactionController.OPENMODE_FORUPDATE |\r
+                                        TransactionController.OPENMODE_BASEROW_INSERT_LOCKED),\r
+                                           lockMode,\r
+                        isolationLevel);\r
+                       }\r
+                       else\r
+                       {\r
+                               indexCC = \r
+                           tc.openCompiledConglomerate(\r
+                        false,\r
+                                   (TransactionController.OPENMODE_FORUPDATE |\r
+                                        TransactionController.OPENMODE_BASEROW_INSERT_LOCKED),\r
+                                           lockMode,\r
+                        isolationLevel,\r
+                                               indexSCOCI,\r
+                                               indexDCOCI);\r
+                       }\r
+               }\r
+\r
+               return indexCC;\r
+       }\r
+\r
+       /**\r
+         Open this IndexChanger.\r
+\r
+         @exception StandardException          Thrown on error\r
+         */\r
+       public void open()\r
+                throws StandardException\r
+       {\r
+       }\r
+\r
+       /**\r
+         Perform index maintenance to support a delete of a base table row.\r
+\r
+         @param baseRow the base table row.\r
+         @param baseRowLocation the base table row's location.\r
+         @exception StandardException          Thrown on error\r
+         */\r
+       public void delete(ExecRow baseRow,\r
+                                          RowLocation baseRowLocation)\r
+                throws StandardException\r
+       {\r
+               setOurIndexRow(baseRow, baseRowLocation);\r
+               setScan();\r
+               doDelete();\r
+       }\r
+\r
+       /**\r
+         Perform index maintenance to support an update of a base table row.\r
+\r
+         @param oldBaseRow         the old image of the base table row.\r
+         @param newBaseRow         the new image of the base table row.\r
+         @param baseRowLocation    the base table row's location.\r
+\r
+         @exception StandardException          Thrown on error\r
+         */\r
+       public void update(ExecRow oldBaseRow,\r
+                                          ExecRow newBaseRow,\r
+                                          RowLocation baseRowLocation\r
+                                          )\r
+                throws StandardException\r
+       {\r
+               setOurIndexRow(oldBaseRow, baseRowLocation);\r
+               setOurUpdatedIndexRow(newBaseRow, baseRowLocation);\r
+\r
+               /* We skip the update in the degenerate case\r
+                * where none of the key columns changed.\r
+                * (From an actual customer case.)\r
+                */\r
+               if (indexRowChanged())\r
+               {\r
+                       setScan();\r
+                       doDelete();\r
+                       insertForUpdate(newBaseRow, baseRowLocation);\r
+               }\r
+       }\r
+\r
+       /**\r
+         Perform index maintenance to support an insert of a base table row.\r
+\r
+         @param newRow            the base table row.\r
+         @param baseRowLocation    the base table row's location.\r
+\r
+         @exception StandardException          Thrown on error\r
+         */\r
+       public void insert(ExecRow newRow, RowLocation baseRowLocation)\r
+                throws StandardException\r
+       {\r
+               setOurIndexRow(newRow, baseRowLocation);\r
+               doInsert();\r
+       }\r
+\r
+       /**\r
+         If we're updating a unique index, the inserts have to be\r
+         deferred.  This is to avoid uniqueness violations that are only\r
+         temporary.  If we do all the deletes first, only "true" uniqueness\r
+         violations can happen.  We do this here, rather than in open(),\r
+         because this is the only operation that requires deferred inserts,\r
+         and we only want to create the conglomerate if necessary.\r
+\r
+         @param newRow            the base table row.\r
+         @param baseRowLocation    the base table row's location.\r
+\r
+         @exception StandardException          Thrown on error\r
+       */\r
+       void insertForUpdate(ExecRow newRow, RowLocation baseRowLocation)\r
+                throws StandardException\r
+       {\r
+               setOurIndexRow(newRow, baseRowLocation);\r
+\r
+               if (irg.isUnique())\r
+               {\r
+                       doDeferredInsert();\r
+               }\r
+               else\r
+               {\r
+                       doInsert();\r
+               }\r
+       }\r
+\r
+       /**\r
+         Finish doing the changes for this index.  This is intended for deferred\r
+         inserts for unique indexes.  It has no effect unless we are doing an\r
+         update of a unique index.\r
+\r
+         @exception StandardException          Thrown on error\r
+        */\r
+       public void finish()\r
+               throws StandardException\r
+       {\r
+               ExecRow                 deferredRow;\r
+               ExecIndexRow    deferredIndexRow = new IndexRow();\r
+\r
+               /* Deferred processing only necessary for unique indexes */\r
+               if (rowHolder != null)\r
+               {\r
+                       CursorResultSet rs = rowHolder.getResultSet();\r
+                       try\r
+                       {\r
+                               rs.open();\r
+                               while ((deferredRow = rs.getNextRow()) != null)\r
+                               {\r
+                                       if (SanityManager.DEBUG)\r
+                                       {\r
+                                               if (!(deferredRow instanceof ExecIndexRow))\r
+                                               {\r
+                                                       SanityManager.THROWASSERT("deferredRow isn't an instance "+\r
+                                                               "of ExecIndexRow as expected. "+\r
+                                                               "It is an "+deferredRow.getClass().getName());\r
+                                               }\r
+                                       }\r
+                                       insertAndCheckDups((ExecIndexRow)deferredRow);\r
+                               }\r
+                       }\r
+                       finally\r
+                       {\r
+                               rs.close();\r
+\r
+                               /*\r
+                               ** If row holder was passed in, let the\r
+                               ** client of this method clean it up.\r
+                               */\r
+                               if (!rowHolderPassedIn)\r
+                               {\r
+                                       rowHolder.close();\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+       /**\r
+         Close this IndexChanger.\r
+\r
+         @exception StandardException          Thrown on error\r
+         */\r
+       public void close()\r
+               throws StandardException\r
+       {\r
+               closeIndexCC();\r
+               closeIndexSC();\r
+               if (rowHolder != null && !rowHolderPassedIn)\r
+               {\r
+                       rowHolder.close();\r
+               }\r
+               baseCC = null;\r
+       }\r
+}\r