--- /dev/null
+/*\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