Adding JMCR-Stable version
[Benchmarks_CSolver.git] / JMCR-Stable / real-world application / MyDerby-10.3 / java / engine / org / apache / derby / impl / sql / execute / CreateIndexConstantAction.java
diff --git a/JMCR-Stable/real-world application/MyDerby-10.3/java/engine/org/apache/derby/impl/sql/execute/CreateIndexConstantAction.java b/JMCR-Stable/real-world application/MyDerby-10.3/java/engine/org/apache/derby/impl/sql/execute/CreateIndexConstantAction.java
new file mode 100644 (file)
index 0000000..bbddae7
--- /dev/null
@@ -0,0 +1,781 @@
+/*\r
+\r
+   Derby - Class org.apache.derby.impl.sql.execute.CreateIndexConstantAction\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.catalog.types.StatisticsImpl;\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.loader.ClassFactory;\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+import org.apache.derby.iapi.sql.Activation;\r
+import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;\r
+import org.apache.derby.iapi.sql.depend.DependencyManager;\r
+import org.apache.derby.iapi.sql.dictionary.ColumnDescriptor;\r
+import org.apache.derby.iapi.sql.dictionary.ColumnDescriptorList;\r
+import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor;\r
+import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptorList;\r
+import org.apache.derby.iapi.sql.dictionary.ConstraintDescriptor;\r
+import org.apache.derby.iapi.sql.dictionary.DataDescriptorGenerator;\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.SchemaDescriptor;\r
+import org.apache.derby.iapi.sql.dictionary.StatisticsDescriptor;\r
+import org.apache.derby.iapi.sql.dictionary.TableDescriptor;\r
+import org.apache.derby.iapi.sql.execute.ConstantAction;\r
+import org.apache.derby.iapi.sql.execute.ExecIndexRow;\r
+import org.apache.derby.iapi.sql.execute.ExecRow;\r
+import org.apache.derby.iapi.store.access.ColumnOrdering;\r
+import org.apache.derby.iapi.store.access.ConglomerateController;\r
+import org.apache.derby.iapi.store.access.GroupFetchScanController;\r
+import org.apache.derby.iapi.store.access.RowLocationRetRowSource;\r
+import org.apache.derby.iapi.store.access.ScanController;\r
+import org.apache.derby.iapi.store.access.SortController;\r
+import org.apache.derby.iapi.store.access.SortObserver;\r
+import org.apache.derby.iapi.store.access.TransactionController;\r
+import org.apache.derby.iapi.types.DataTypeDescriptor;\r
+import org.apache.derby.iapi.types.DataValueDescriptor;\r
+import org.apache.derby.iapi.types.RowLocation;\r
+import org.apache.derby.iapi.types.TypeId;\r
+\r
+/**\r
+ * ConstantAction to create an index either through\r
+ * a CREATE INDEX statement or as a backing index to\r
+ * a constraint.\r
+ */\r
+\r
+class CreateIndexConstantAction extends IndexConstantAction\r
+{\r
+\r
+       private boolean                 unique;\r
+       private String                  indexType;\r
+       private String[]                columnNames;\r
+       private boolean[]               isAscending;\r
+       private boolean                 isConstraint;\r
+       private UUID                    conglomerateUUID;\r
+       private Properties              properties;\r
+\r
+       private ExecRow indexTemplateRow;\r
+\r
+\r
+       // CONSTRUCTORS\r
+       /**\r
+        *      Make the ConstantAction to create an index.\r
+        *\r
+        *  @param unique               True means it will be a unique index\r
+        *  @param indexType    The type of index (BTREE, for example)\r
+        *  @param schemaName   the schema that table (and index) lives in.\r
+        *  @param indexName    Name of the index\r
+        *  @param tableName    Name of table the index will be on\r
+        *  @param tableId              UUID of table\r
+        *  @param columnNames  Names of the columns in the index, in order\r
+        *      @param isAscending      Array of booleans telling asc/desc on each column\r
+        *  @param isConstraint TRUE if index is backing up a constraint, else FALSE\r
+        *  @param conglomerateUUID     ID of conglomerate\r
+        *  @param properties   The optional properties list associated with the index.\r
+        */\r
+       CreateIndexConstantAction(\r
+                                                               boolean                 unique,\r
+                                                               String                  indexType,\r
+                                                               String                  schemaName,\r
+                                                               String                  indexName,\r
+                                                               String                  tableName,\r
+                                                               UUID                    tableId,\r
+                                                               String[]                columnNames,\r
+                                                               boolean[]               isAscending,\r
+                                                               boolean                 isConstraint,\r
+                                                               UUID                    conglomerateUUID,\r
+                                                               Properties              properties)\r
+       {\r
+               super(tableId, indexName, tableName, schemaName);\r
+               this.unique = unique;\r
+               this.indexType = indexType;\r
+               this.columnNames = columnNames;\r
+               this.isAscending = isAscending;\r
+               this.isConstraint = isConstraint;\r
+               this.conglomerateUUID = conglomerateUUID;\r
+               this.properties = properties;\r
+       }\r
+\r
+       ///////////////////////////////////////////////\r
+       //\r
+       // OBJECT SHADOWS\r
+       //\r
+       ///////////////////////////////////////////////\r
+\r
+       public  String  toString()\r
+       {\r
+               // Do not put this under SanityManager.DEBUG - it is needed for\r
+               // error reporting.\r
+               return "CREATE INDEX " + indexName;\r
+       }\r
+\r
+       // INTERFACE METHODS\r
+\r
+\r
+       /**\r
+        *      This is the guts of the Execution-time logic for \r
+     *  creating an index.\r
+     *\r
+     *  <P>\r
+     *  A index is represented as:\r
+     *  <UL>\r
+     *  <LI> ConglomerateDescriptor.\r
+     *  </UL>\r
+     *  No dependencies are created.\r
+        *\r
+     *  @see ConglomerateDescriptor\r
+     *  @see SchemaDescriptor\r
+        *      @see ConstantAction#executeConstantAction\r
+        *\r
+        * @exception StandardException         Thrown on failure\r
+        */\r
+       public void     executeConstantAction( Activation activation )\r
+                                               throws StandardException\r
+       {\r
+               boolean                                         forCreateTable;\r
+               TableDescriptor                         td;\r
+               UUID                                            toid;\r
+               ColumnDescriptor                        columnDescriptor;\r
+               int[]                                           baseColumnPositions;\r
+               IndexRowGenerator                       indexRowGenerator = null;\r
+               ExecRow[]                                       baseRows;\r
+               ExecIndexRow[]                          indexRows;\r
+               ExecRow[]                                       compactBaseRows;\r
+               GroupFetchScanController    scan;\r
+               RowLocationRetRowSource     rowSource;\r
+               long                                            sortId;\r
+               int                                                     maxBaseColumnPosition = -1;\r
+\r
+               LanguageConnectionContext lcc = activation.getLanguageConnectionContext();\r
+               DataDictionary dd = lcc.getDataDictionary();\r
+               DependencyManager dm = dd.getDependencyManager();\r
+               TransactionController tc = lcc.getTransactionExecute();\r
+\r
+               /* Remember whether or not we are doing a create table */\r
+               forCreateTable = activation.getForCreateTable();\r
+\r
+               /*\r
+               ** Inform the data dictionary that we are about to write to it.\r
+               ** There are several calls to data dictionary "get" methods here\r
+               ** that might be done in "read" mode in the data dictionary, but\r
+               ** it seemed safer to do this whole operation in "write" mode.\r
+               **\r
+               ** We tell the data dictionary we're done writing at the end of\r
+               ** the transaction.\r
+               */\r
+               dd.startWriting(lcc);\r
+\r
+               /*\r
+               ** If the schema descriptor is null, then\r
+               ** we must have just read ourselves in.  \r
+               ** So we will get the corresponding schema\r
+               ** descriptor from the data dictionary.\r
+               */\r
+               SchemaDescriptor sd = dd.getSchemaDescriptor(schemaName, tc, true) ;\r
+\r
+\r
+               /* Get the table descriptor. */\r
+               /* See if we can get the TableDescriptor \r
+                * from the Activation.  (Will be there\r
+                * for backing indexes.)\r
+                */\r
+               td = activation.getDDLTableDescriptor();\r
+\r
+               if (td == null)\r
+               {\r
+                       /* tableId will be non-null if adding an index to\r
+                        * an existing table (as opposed to creating a\r
+                        * table with a constraint with a backing index).\r
+                        */\r
+                       if (tableId != null)\r
+                       {\r
+                               td = dd.getTableDescriptor(tableId);\r
+                       }\r
+                       else\r
+                       {\r
+                               td = dd.getTableDescriptor(tableName, sd);\r
+                       }\r
+               }\r
+\r
+               if (td == null)\r
+               {\r
+                       throw StandardException.newException(SQLState.LANG_CREATE_INDEX_NO_TABLE, \r
+                                               indexName, tableName);\r
+               }\r
+\r
+               if (td.getTableType() == TableDescriptor.SYSTEM_TABLE_TYPE)\r
+               {\r
+                       throw StandardException.newException(SQLState.LANG_CREATE_SYSTEM_INDEX_ATTEMPTED, \r
+                                               indexName, tableName);\r
+               }\r
+\r
+               /* Get a shared table lock on the table. We need to lock table before\r
+                * invalidate dependents, otherwise, we may interfere with the\r
+                * compilation/re-compilation of DML/DDL.  See beetle 4325 and $WS/\r
+                * docs/language/SolutionsToConcurrencyIssues.txt (point f).\r
+                */\r
+               lockTableForDDL(tc, td.getHeapConglomerateId(), false);\r
+\r
+               // invalidate any prepared statements that\r
+               // depended on this table (including this one)\r
+               if (! forCreateTable)\r
+               {\r
+                       dm.invalidateFor(td, DependencyManager.CREATE_INDEX, lcc);\r
+               }\r
+\r
+               // Translate the base column names to column positions\r
+               baseColumnPositions = new int[columnNames.length];\r
+               for (int i = 0; i < columnNames.length; i++)\r
+               {\r
+                       // Look up the column in the data dictionary\r
+                       columnDescriptor = td.getColumnDescriptor(columnNames[i]);\r
+                       if (columnDescriptor == null)\r
+                       {\r
+                               throw StandardException.newException(SQLState.LANG_COLUMN_NOT_FOUND_IN_TABLE, \r
+                                                                                                                       columnNames[i],\r
+                                                                                                                       tableName);\r
+                       }\r
+\r
+                       TypeId typeId = columnDescriptor.getType().getTypeId();\r
+\r
+                       // Don't allow a column to be created on a non-orderable type\r
+                       ClassFactory cf = lcc.getLanguageConnectionFactory().getClassFactory();\r
+                       boolean isIndexable = typeId.orderable(cf);\r
+\r
+                       if (isIndexable && typeId.userType()) {\r
+                               String userClass = typeId.getCorrespondingJavaTypeName();\r
+\r
+                               // Don't allow indexes to be created on classes that\r
+                               // are loaded from the database. This is because recovery\r
+                               // won't be able to see the class and it will need it to\r
+                               // run the compare method.\r
+                               try {\r
+                                       if (cf.isApplicationClass(cf.loadApplicationClass(userClass)))\r
+                                               isIndexable = false;\r
+                               } catch (ClassNotFoundException cnfe) {\r
+                                       // shouldn't happen as we just check the class is orderable\r
+                                       isIndexable = false;\r
+                               }\r
+                       }\r
+\r
+                       if (!isIndexable) {\r
+                               throw StandardException.newException(SQLState.LANG_COLUMN_NOT_ORDERABLE_DURING_EXECUTION, \r
+                                       typeId.getSQLTypeName());\r
+                       }\r
+\r
+                       // Remember the position in the base table of each column\r
+                       baseColumnPositions[i] = columnDescriptor.getPosition();\r
+\r
+                       if (maxBaseColumnPosition < baseColumnPositions[i])\r
+                               maxBaseColumnPosition = baseColumnPositions[i];\r
+               }\r
+\r
+               // check if we have similar indices already for this table\r
+               ConglomerateDescriptor[] congDescs = td.getConglomerateDescriptors();\r
+               boolean duplicate = false;\r
+        long conglomId = 0;\r
+\r
+               for (int i = 0; i < congDescs.length; i++)\r
+               {\r
+                       ConglomerateDescriptor cd = congDescs[i];\r
+                       if ( ! cd.isIndex())\r
+                               continue;\r
+                       IndexRowGenerator irg = cd.getIndexDescriptor();\r
+                       int[] bcps = irg.baseColumnPositions();\r
+                       boolean[] ia = irg.isAscending();\r
+                       int j = 0;\r
+\r
+                       /* For an index to be considered a duplicate of already existing index, the\r
+                        * following conditions have to be satisfied:\r
+                        * 1. the set of columns (both key and include columns) and their \r
+                        *  order in the index is the same as that of an existing index AND \r
+                        * 2. the ordering attributes are the same AND \r
+                        * 3. both the previously existing index and the one being created \r
+                        *  are non-unique OR the previously existing index is unique\r
+                        */\r
+\r
+                       if ((bcps.length == baseColumnPositions.length) &&\r
+                           (irg.isUnique() || !unique) &&\r
+                               indexType.equals(irg.indexType()))\r
+                       {\r
+                               for (; j < bcps.length; j++)\r
+                               {\r
+                                       if ((bcps[j] != baseColumnPositions[j]) || (ia[j] != isAscending[j]))\r
+                                               break;\r
+                               }\r
+                       }\r
+\r
+                       if (j == baseColumnPositions.length)    // duplicate\r
+                       {\r
+                               /*\r
+                                * Don't allow users to create a duplicate index. Allow if being done internally\r
+                                * for a constraint\r
+                                */\r
+                               if (!isConstraint)\r
+                               {\r
+                                       activation.addWarning(\r
+                                                       StandardException.newWarning(\r
+                                                               SQLState.LANG_INDEX_DUPLICATE,\r
+                                                               cd.getConglomerateName()));\r
+\r
+                                       return;\r
+                               }\r
+\r
+                               //Duplicate indexes share the physical conglomerate underneath\r
+                               conglomId = cd.getConglomerateNumber();\r
+                               indexRowGenerator = cd.getIndexDescriptor();\r
+                               //DERBY-655 and DERBY-1343  \r
+                               //Duplicate indexes will have unqiue logical conglomerate UUIDs.  \r
+                               conglomerateUUID = dd.getUUIDFactory().createUUID();\r
+                               duplicate = true;\r
+                               break;\r
+                       }\r
+               }\r
+\r
+               /* If this index already has an essentially same one, we share the\r
+                * conglomerate with the old one, and just simply add a descriptor\r
+                * entry into SYSCONGLOMERATES.\r
+                */\r
+               DataDescriptorGenerator ddg = dd.getDataDescriptorGenerator();\r
+               if (duplicate)\r
+               {\r
+                       ConglomerateDescriptor cgd =\r
+                               ddg.newConglomerateDescriptor(conglomId, indexName, true,\r
+                                                                                 indexRowGenerator, isConstraint,\r
+                                                                                 conglomerateUUID, td.getUUID(), sd.getUUID() );\r
+                       dd.addDescriptor(cgd, sd, DataDictionary.SYSCONGLOMERATES_CATALOG_NUM, false, tc);\r
+                       // add newly added conglomerate to the list of conglomerate \r
+                       // descriptors in the td.\r
+                       ConglomerateDescriptorList cdl = \r
+                               td.getConglomerateDescriptorList();\r
+                       cdl.add(cgd);\r
+\r
+                       // can't just return yet, need to get member "indexTemplateRow"\r
+                       // because create constraint may use it\r
+               }\r
+\r
+               // Describe the properties of the index to the store using Properties\r
+               // RESOLVE: The following properties assume a BTREE index.\r
+               Properties      indexProperties;\r
+               \r
+               if (properties != null)\r
+               {\r
+                       indexProperties = properties;\r
+               }\r
+               else\r
+               {\r
+                       indexProperties = new Properties();\r
+               }\r
+\r
+               // Tell it the conglomerate id of the base table\r
+               indexProperties.put("baseConglomerateId",\r
+                                                       Long.toString(td.getHeapConglomerateId()));\r
+\r
+               // All indexes are unique because they contain the RowLocation.\r
+               // The number of uniqueness columns must include the RowLocation\r
+               // if the user did not specify a unique index.\r
+               indexProperties.put("nUniqueColumns",\r
+                                       Integer.toString(unique ? baseColumnPositions.length :\r
+                                                                                               baseColumnPositions.length + 1)\r
+                                                       );\r
+\r
+               // By convention, the row location column is the last column\r
+               indexProperties.put("rowLocationColumn",\r
+                                                       Integer.toString(baseColumnPositions.length));\r
+\r
+               // For now, all columns are key fields, including the RowLocation\r
+               indexProperties.put("nKeyFields",\r
+                                                       Integer.toString(baseColumnPositions.length + 1));\r
+\r
+               // For now, assume that all index columns are ordered columns\r
+               if (! duplicate)\r
+               {\r
+                       indexRowGenerator = new IndexRowGenerator(indexType, unique,\r
+                                                                                                       baseColumnPositions,\r
+                                                                                                       isAscending,\r
+                                                                                                       baseColumnPositions.length);\r
+               }\r
+\r
+               /* Now add the rows from the base table to the conglomerate.\r
+                * We do this by scanning the base table and inserting the\r
+                * rows into a sorter before inserting from the sorter\r
+                * into the index.  This gives us better performance\r
+                * and a more compact index.\r
+                */\r
+\r
+               rowSource = null;\r
+               sortId = 0;\r
+               boolean needToDropSort = false; // set to true once the sorter is created\r
+\r
+               /* bulkFetchSIze will be 16 (for now) unless\r
+                * we are creating the table in which case it\r
+                * will be 1.  Too hard to remove scan when\r
+                * creating index on new table, so minimize\r
+                * work where we can.\r
+                */\r
+               int bulkFetchSize = (forCreateTable) ? 1 : 16;  \r
+               int numColumns = td.getNumberOfColumns();\r
+               int approximateRowSize = 0;\r
+\r
+               // Create the FormatableBitSet for mapping the partial to full base row\r
+               FormatableBitSet bitSet = new FormatableBitSet(numColumns+1);\r
+               for (int index = 0; index < baseColumnPositions.length; index++)\r
+               {\r
+                       bitSet.set(baseColumnPositions[index]);\r
+               }\r
+               FormatableBitSet zeroBasedBitSet = RowUtil.shift(bitSet, 1);\r
+\r
+               // Start by opening a full scan on the base table.\r
+               scan = tc.openGroupFetchScan(\r
+                            td.getHeapConglomerateId(),\r
+                                                       false,  // hold\r
+                                                       0,      // open base table read only\r
+                            TransactionController.MODE_TABLE,\r
+                            TransactionController.ISOLATION_SERIALIZABLE,\r
+                                                       zeroBasedBitSet,    // all fields as objects\r
+                                                       (DataValueDescriptor[]) null,   // startKeyValue\r
+                                                       0,              // not used when giving null start posn.\r
+                                                       null,   // qualifier\r
+                                                       (DataValueDescriptor[]) null,   // stopKeyValue\r
+                                                       0);             // not used when giving null stop posn.\r
+\r
+               // Create an array to put base row template\r
+               baseRows = new ExecRow[bulkFetchSize];\r
+               indexRows = new ExecIndexRow[bulkFetchSize];\r
+               compactBaseRows = new ExecRow[bulkFetchSize];\r
+\r
+               try\r
+               {\r
+                       // Create the array of base row template\r
+                       for (int i = 0; i < bulkFetchSize; i++)\r
+                       {\r
+                               // create a base row template\r
+                               baseRows[i] = activation.getExecutionFactory().getValueRow(maxBaseColumnPosition);\r
+\r
+                               // create an index row template\r
+                               indexRows[i] = indexRowGenerator.getIndexRowTemplate();\r
+\r
+                               // create a compact base row template\r
+                               compactBaseRows[i] = activation.getExecutionFactory().getValueRow(\r
+                                                                                                       baseColumnPositions.length);\r
+                       }\r
+\r
+                       indexTemplateRow = indexRows[0];\r
+\r
+                       // Fill the partial row with nulls of the correct type\r
+                       ColumnDescriptorList cdl = td.getColumnDescriptorList();\r
+                       int                                      cdlSize = cdl.size();\r
+                       for (int index = 0, numSet = 0; index < cdlSize; index++)\r
+                       {\r
+                               if (! zeroBasedBitSet.get(index))\r
+                               {\r
+                                       continue;\r
+                               }\r
+                               numSet++;\r
+                               ColumnDescriptor cd = (ColumnDescriptor) cdl.elementAt(index);\r
+                               DataTypeDescriptor dts = cd.getType();\r
+\r
+\r
+                               for (int i = 0; i < bulkFetchSize; i++)\r
+                               {\r
+                                       // Put the column in both the compact and sparse base rows\r
+                                       baseRows[i].setColumn(index + 1,\r
+                                                                 dts.getNull());\r
+                                       compactBaseRows[i].setColumn(numSet,\r
+                                                                 baseRows[i].getColumn(index + 1));\r
+                               }\r
+\r
+                               // Calculate the approximate row size for the index row\r
+                               approximateRowSize += dts.getTypeId().getApproximateLengthInBytes(dts);\r
+                       }\r
+\r
+                       // Get an array of RowLocation template\r
+                       RowLocation rl[] = new RowLocation[bulkFetchSize];\r
+                       for (int i = 0; i < bulkFetchSize; i++)\r
+                       {\r
+                               rl[i] = scan.newRowLocationTemplate();\r
+\r
+                               // Get an index row based on the base row\r
+                               indexRowGenerator.getIndexRow(compactBaseRows[i], rl[i], indexRows[i], bitSet);\r
+                       }\r
+\r
+                       /* now that we got indexTemplateRow, done for duplicate index\r
+                        */\r
+                       if (duplicate)\r
+                               return;\r
+\r
+                       /* For non-unique indexes, we order by all columns + the RID.\r
+                        * For unique indexes, we just order by the columns.\r
+                        * We create a unique index observer for unique indexes\r
+                        * so that we can catch duplicate key.\r
+                        * We create a basic sort observer for non-unique indexes\r
+                        * so that we can reuse the wrappers during an external\r
+                        * sort.\r
+                        */\r
+                       int numColumnOrderings;\r
+                       SortObserver sortObserver = null;\r
+                       if (unique)\r
+                       {\r
+                               numColumnOrderings = baseColumnPositions.length;\r
+                               // if the index is a constraint, use constraintname in possible error messagge\r
+                               String indexOrConstraintName = indexName;\r
+                               if  (conglomerateUUID != null)\r
+                               {\r
+                                       ConglomerateDescriptor cd = dd.getConglomerateDescriptor(conglomerateUUID);\r
+                                       if ((isConstraint) && (cd != null && cd.getUUID() != null && td != null))\r
+                                       {\r
+                                               ConstraintDescriptor conDesc = dd.getConstraintDescriptor(td,\r
+                                                                      cd.getUUID());\r
+                                               indexOrConstraintName = conDesc.getConstraintName();\r
+                                       }\r
+                               }\r
+                               sortObserver = new UniqueIndexSortObserver(true, isConstraint, \r
+                                                                                                                  indexOrConstraintName,\r
+                                                                                                                  indexTemplateRow,\r
+                                                                                                                  true,\r
+                                                                                                                  td.getName());\r
+                       }\r
+                       else\r
+                       {\r
+                               numColumnOrderings = baseColumnPositions.length + 1;\r
+                               sortObserver = new BasicSortObserver(true, false, \r
+                                                                                                        indexTemplateRow,\r
+                                                                                                        true);\r
+                       }\r
+\r
+                       ColumnOrdering[]        order = new ColumnOrdering[numColumnOrderings];\r
+                       for (int i=0; i < numColumnOrderings; i++) \r
+                       {\r
+                               order[i] = \r
+                    new IndexColumnOrder(\r
+                        i, \r
+                        unique || i < numColumnOrderings - 1 ? \r
+                            isAscending[i] : true);\r
+                       }\r
+\r
+                       // create the sorter\r
+                       sortId = tc.createSort((Properties)null, \r
+                                       indexTemplateRow.getRowArrayClone(),\r
+                                       order,\r
+                                       sortObserver,\r
+                                       false,                  // not in order\r
+                                       scan.getEstimatedRowCount(),\r
+                                       approximateRowSize      // est row size, -1 means no idea       \r
+                                       );\r
+\r
+                       needToDropSort = true;\r
+\r
+                       // Populate sorter and get the output of the sorter into a row\r
+                       // source.  The sorter has the indexed columns only and the columns\r
+                       // are in the correct order. \r
+                       rowSource = loadSorter(baseRows, indexRows, tc,\r
+                                                                  scan, sortId, rl);\r
+\r
+                       conglomId = \r
+                tc.createAndLoadConglomerate(\r
+                                       indexType,\r
+                                       indexTemplateRow.getRowArray(), // index row template\r
+                                       order, //colums sort order\r
+                    indexRowGenerator.getColumnCollationIds(\r
+                        td.getColumnDescriptorList()),\r
+                                       indexProperties,\r
+                                       TransactionController.IS_DEFAULT, // not temporary\r
+                                       rowSource,\r
+                                       (long[]) null);\r
+                       \r
+               }\r
+               finally\r
+               {\r
+\r
+                       /* close the table scan */\r
+                       if (scan != null)\r
+                               scan.close();\r
+\r
+                       /* close the sorter row source before throwing exception */\r
+                       if (rowSource != null)\r
+                               rowSource.closeRowSource();\r
+\r
+                       /*\r
+                       ** drop the sort so that intermediate external sort run can be\r
+                       ** removed from disk\r
+                       */\r
+                       if (needToDropSort)\r
+                               tc.dropSort(sortId);\r
+               }\r
+\r
+               ConglomerateController indexController =\r
+                       tc.openConglomerate(\r
+                conglomId, false, 0, TransactionController.MODE_TABLE,\r
+                TransactionController.ISOLATION_SERIALIZABLE);\r
+\r
+               // Check to make sure that the conglomerate can be used as an index\r
+               if ( ! indexController.isKeyed())\r
+               {\r
+                       indexController.close();\r
+                       throw StandardException.newException(SQLState.LANG_NON_KEYED_INDEX, indexName,\r
+                                                                                                                  indexType);\r
+               }\r
+               indexController.close();\r
+\r
+               //\r
+               // Create a conglomerate descriptor with the conglomId filled in and\r
+               // add it.\r
+               //\r
+\r
+               ConglomerateDescriptor cgd =\r
+                       ddg.newConglomerateDescriptor(conglomId, indexName, true,\r
+                                                                                 indexRowGenerator, isConstraint,\r
+                                                                                 conglomerateUUID, td.getUUID(), sd.getUUID() );\r
+\r
+               dd.addDescriptor(cgd, sd, DataDictionary.SYSCONGLOMERATES_CATALOG_NUM, false, tc);\r
+\r
+               // add newly added conglomerate to the list of conglomerate descriptors\r
+               // in the td.\r
+               ConglomerateDescriptorList cdl = td.getConglomerateDescriptorList();\r
+               cdl.add(cgd);\r
+\r
+               CardinalityCounter cCount = (CardinalityCounter)rowSource;\r
+               long numRows;\r
+               if ((numRows = cCount.getRowCount()) > 0)\r
+               {\r
+                       long[] c = cCount.getCardinality();\r
+                       for (int i = 0; i < c.length; i++)\r
+                       {\r
+                               StatisticsDescriptor statDesc = \r
+                                       new StatisticsDescriptor(dd, dd.getUUIDFactory().createUUID(),\r
+                                                                                               cgd.getUUID(), td.getUUID(), "I", new StatisticsImpl(numRows, c[i]),\r
+                                                                                               i + 1);\r
+                               dd.addDescriptor(statDesc, null, \r
+                                                                DataDictionary.SYSSTATISTICS_CATALOG_NUM,\r
+                                                                true, tc);\r
+                       }\r
+               }\r
+       }\r
+\r
+       // CLASS METHODS\r
+       \r
+       ///////////////////////////////////////////////////////////////////////\r
+       //\r
+       //      GETTERs called by CreateConstraint\r
+       //\r
+       ///////////////////////////////////////////////////////////////////////\r
+       ExecRow getIndexTemplateRow()\r
+       {\r
+               return indexTemplateRow;\r
+       }\r
+\r
+       /**\r
+        * Do necessary clean up (close down controllers, etc.) before throwing\r
+        * a statement exception.\r
+        *\r
+        * @param scan                          ScanController for the heap\r
+        * @param indexController       ConglomerateController for the index\r
+        */\r
+       private void statementExceptionCleanup(\r
+                                       ScanController scan, \r
+                                       ConglomerateController indexController)\r
+        throws StandardException\r
+       {\r
+               if (indexController != null)\r
+               {\r
+                       indexController.close();\r
+               }\r
+               if (scan != null)\r
+               {\r
+                       scan.close();\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Scan the base conglomerate and insert the keys into a sorter,\r
+        * returning a rowSource on the sorter. \r
+        *\r
+        * @return RowSource on the sorted index keys.\r
+        *\r
+        * @exception StandardException                                 thrown on error\r
+        */\r
+       private RowLocationRetRowSource loadSorter(ExecRow[] baseRows,\r
+                                                                              ExecIndexRow[] indexRows, \r
+                                                                              TransactionController tc,\r
+                                                                              GroupFetchScanController scan,\r
+                                                                              long sortId,\r
+                                                                              RowLocation rl[])\r
+               throws StandardException\r
+       {\r
+               SortController          sorter;\r
+               long                            rowCount = 0;\r
+\r
+               sorter = tc.openSort(sortId);\r
+\r
+               try\r
+               {\r
+                       // Step through all the rows in the base table\r
+                       // prepare an array or rows for bulk fetch\r
+                       int bulkFetchSize = baseRows.length;\r
+\r
+                       if (SanityManager.DEBUG)\r
+                       {\r
+                               SanityManager.ASSERT(bulkFetchSize == indexRows.length, \r
+                                       "number of base rows and index rows does not match");\r
+                               SanityManager.ASSERT(bulkFetchSize == rl.length,\r
+                                       "number of base rows and row locations does not match");\r
+                       }\r
+\r
+                       DataValueDescriptor[][] baseRowArray = new DataValueDescriptor[bulkFetchSize][];\r
+\r
+                       for (int i = 0; i < bulkFetchSize; i++)\r
+                               baseRowArray[i] = baseRows[i].getRowArray();\r
+\r
+                       // rl[i] and baseRowArray[i] and indexRows[i] are all tied up\r
+                       // beneath the surface.  Fetching the base row and row location\r
+                       // from the table scan will automagically set up the indexRow\r
+                       // fetchNextGroup will return how many rows are actually fetched.\r
+                       int bulkFetched = 0;\r
+\r
+                       while ((bulkFetched = scan.fetchNextGroup(baseRowArray, rl)) > 0)\r
+                       {\r
+                               for (int i = 0; i < bulkFetched; i++)\r
+                               {\r
+                                       sorter.insert(indexRows[i].getRowArray());\r
+                                       rowCount++;\r
+                               }\r
+                       }\r
+\r
+                       /*\r
+                       ** We've just done a full scan on the heap, so set the number\r
+                       ** of rows so the optimizer will have an accurate count.\r
+                       */\r
+                       scan.setEstimatedRowCount(rowCount);\r
+               }\r
+               finally\r
+               {\r
+                       sorter.completedInserts();\r
+               }\r
+\r
+               return new CardinalityCounter(tc.openSortRowSource(sortId));\r
+       }\r
+}\r
+\r