001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.compile.CreateIndexNode
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.impl.sql.compile;
023:
024: import org.apache.derby.iapi.services.context.ContextManager;
025:
026: import org.apache.derby.iapi.sql.ResultSet;
027:
028: import org.apache.derby.iapi.sql.compile.CompilerContext;
029:
030: import org.apache.derby.iapi.sql.dictionary.ColumnDescriptor;
031: import org.apache.derby.iapi.sql.dictionary.DataDictionaryContext;
032: import org.apache.derby.iapi.sql.dictionary.DataDictionary;
033: import org.apache.derby.iapi.sql.dictionary.SchemaDescriptor;
034: import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
035: import org.apache.derby.iapi.sql.dictionary.DataDescriptorGenerator;
036: import org.apache.derby.iapi.sql.dictionary.ColumnDescriptor;
037:
038: import org.apache.derby.iapi.reference.SQLState;
039: import org.apache.derby.iapi.reference.Limits;
040: import org.apache.derby.iapi.reference.Property;
041:
042: import org.apache.derby.iapi.sql.execute.ConstantAction;
043:
044: import org.apache.derby.iapi.error.StandardException;
045:
046: import org.apache.derby.iapi.services.monitor.Monitor;
047: import org.apache.derby.iapi.services.property.PropertyUtil;
048: import org.apache.derby.iapi.services.sanity.SanityManager;
049:
050: import org.apache.derby.impl.sql.compile.ActivationClassBuilder;
051: import org.apache.derby.impl.sql.execute.BaseActivation;
052: import org.apache.derby.impl.sql.execute.ColumnInfo;
053: import org.apache.derby.iapi.types.DataTypeDescriptor;
054: import org.apache.derby.catalog.UUID;
055:
056: import java.util.Hashtable;
057: import java.util.Properties;
058: import java.util.Vector;
059:
060: /**
061: * A CreateIndexNode is the root of a QueryTree that represents a CREATE INDEX
062: * statement.
063: *
064: * @author Jeff Lichtman
065: */
066:
067: public class CreateIndexNode extends DDLStatementNode {
068: boolean unique;
069: DataDictionary dd = null;
070: Properties properties;
071: String indexType;
072: TableName indexName;
073: TableName tableName;
074: Vector columnNameList;
075: String[] columnNames = null;
076: boolean[] isAscending;
077: int[] boundColumnIDs;
078:
079: TableDescriptor td;
080:
081: /**
082: * Initializer for a CreateIndexNode
083: *
084: * @param unique True means it's a unique index
085: * @param indexType The type of index
086: * @param indexName The name of the index
087: * @param tableName The name of the table the index will be on
088: * @param columnNameList A list of column names, in the order they
089: * appear in the index.
090: * @param properties The optional properties list associated with the index.
091: *
092: * @exception StandardException Thrown on error
093: */
094: public void init(Object unique, Object indexType, Object indexName,
095: Object tableName, Object columnNameList, Object properties)
096: throws StandardException {
097: initAndCheck(indexName);
098: this .unique = ((Boolean) unique).booleanValue();
099: this .indexType = (String) indexType;
100: this .indexName = (TableName) indexName;
101: this .tableName = (TableName) tableName;
102: this .columnNameList = (Vector) columnNameList;
103: this .properties = (Properties) properties;
104: }
105:
106: /**
107: * Convert this object to a String. See comments in QueryTreeNode.java
108: * for how this should be done for tree printing.
109: *
110: * @return This object as a String
111: */
112:
113: public String toString() {
114: if (SanityManager.DEBUG) {
115: return super .toString() + "unique: " + unique + "\n"
116: + "indexType: " + indexType + "\n" + "indexName: "
117: + indexName + "\n" + "tableName: " + tableName
118: + "\n" + "properties: " + properties + "\n";
119: } else {
120: return "";
121: }
122: }
123:
124: public String statementToString() {
125: return "CREATE INDEX";
126: }
127:
128: public boolean getUniqueness() {
129: return unique;
130: }
131:
132: public String getIndexType() {
133: return indexType;
134: }
135:
136: public TableName getIndexName() {
137: return indexName;
138: }
139:
140: public UUID getBoundTableID() {
141: return td.getUUID();
142: }
143:
144: public Properties getProperties() {
145: return properties;
146: }
147:
148: public TableName getIndexTableName() {
149: return tableName;
150: }
151:
152: public String[] getColumnNames() {
153: return columnNames;
154: }
155:
156: // get 1-based column ids
157: public int[] getKeyColumnIDs() {
158: return boundColumnIDs;
159: }
160:
161: public boolean[] getIsAscending() {
162: return isAscending;
163: }
164:
165: // We inherit the generate() method from DDLStatementNode.
166:
167: /**
168: * Bind this CreateIndexNode. This means doing any static error
169: * checking that can be done before actually creating the table.
170: * For example, verifying that the column name list does not
171: * contain any duplicate column names.
172: *
173: * @return The bound query tree
174: *
175: * @exception StandardException Thrown on error
176: */
177:
178: public QueryTreeNode bind() throws StandardException {
179: CompilerContext cc = getCompilerContext();
180: DataDictionary dd = getDataDictionary();
181: SchemaDescriptor sd;
182: int columnCount;
183:
184: sd = getSchemaDescriptor();
185:
186: td = getTableDescriptor(tableName);
187:
188: //throw an exception if user is attempting to create an index on a temporary table
189: if (td.getTableType() == TableDescriptor.GLOBAL_TEMPORARY_TABLE_TYPE) {
190: throw StandardException
191: .newException(SQLState.LANG_NOT_ALLOWED_FOR_DECLARED_GLOBAL_TEMP_TABLE);
192: }
193:
194: //If total number of indexes on the table so far is more than 32767, then we need to throw an exception
195: if (td.getTotalNumberOfIndexes() > Limits.DB2_MAX_INDEXES_ON_TABLE) {
196: throw StandardException.newException(
197: SQLState.LANG_TOO_MANY_INDEXES_ON_TABLE, String
198: .valueOf(td.getTotalNumberOfIndexes()),
199: tableName, String
200: .valueOf(Limits.DB2_MAX_INDEXES_ON_TABLE));
201: }
202:
203: /* Validate the column name list */
204: verifyAndGetUniqueNames();
205:
206: columnCount = columnNames.length;
207: boundColumnIDs = new int[columnCount];
208:
209: // Verify that the columns exist
210: for (int i = 0; i < columnCount; i++) {
211: ColumnDescriptor columnDescriptor;
212:
213: columnDescriptor = td.getColumnDescriptor(columnNames[i]);
214: if (columnDescriptor == null) {
215: throw StandardException.newException(
216: SQLState.LANG_COLUMN_NOT_FOUND_IN_TABLE,
217: columnNames[i], tableName);
218: }
219: boundColumnIDs[i] = columnDescriptor.getPosition();
220:
221: // Don't allow a column to be created on a non-orderable type
222: if (!columnDescriptor.getType().getTypeId().orderable(
223: getClassFactory())) {
224: throw StandardException
225: .newException(
226: SQLState.LANG_COLUMN_NOT_ORDERABLE_DURING_EXECUTION,
227: columnDescriptor.getType().getTypeId()
228: .getSQLTypeName());
229: }
230: }
231:
232: /* Check for number of key columns to be less than 16 to match DB2 */
233: if (columnCount > 16)
234: throw StandardException
235: .newException(SQLState.LANG_TOO_MANY_INDEX_KEY_COLS);
236:
237: /* See if the index already exists in this schema.
238: * NOTE: We still need to check at execution time
239: * since the index name is only unique to the schema,
240: * not the table.
241: */
242: // if (dd.getConglomerateDescriptor(indexName.getTableName(), sd, false) != null)
243: // {
244: // throw StandardException.newException(SQLState.LANG_OBJECT_ALREADY_EXISTS_IN_OBJECT,
245: // "Index",
246: // indexName.getTableName(),
247: // "schema",
248: // sd.getSchemaName());
249: // }
250: /* Statement is dependent on the TableDescriptor */
251: getCompilerContext().createDependency(td);
252:
253: return this ;
254: }
255:
256: /**
257: * Return true if the node references SESSION schema tables (temporary or permanent)
258: *
259: * @return true if references SESSION schema tables, else false
260: *
261: * @exception StandardException Thrown on error
262: */
263: public boolean referencesSessionSchema() throws StandardException {
264: //If create index is on a SESSION schema table, then return true.
265: return isSessionSchema(td.getSchemaName());
266: }
267:
268: /**
269: * Create the Constant information that will drive the guts of Execution.
270: *
271: * @exception StandardException Thrown on failure
272: */
273: public ConstantAction makeConstantAction() throws StandardException {
274: long conglomId = 0;
275: SchemaDescriptor sd = getSchemaDescriptor();
276:
277: int columnCount = columnNames.length;
278: int approxLength = 0;
279: boolean index_has_long_column = false;
280:
281: // bump the page size for the index,
282: // if the approximate sizes of the columns in the key are
283: // greater than the bump threshold.
284: // Ideally, we would want to have atleast 2 or 3 keys fit in one page
285: // With fix for beetle 5728, indexes on long types is not allowed
286: // so we do not have to consider key columns of long types
287: for (int i = 0; i < columnCount; i++) {
288: ColumnDescriptor columnDescriptor = td
289: .getColumnDescriptor(columnNames[i]);
290: DataTypeDescriptor dts = columnDescriptor.getType();
291: approxLength += dts.getTypeId()
292: .getApproximateLengthInBytes(dts);
293: }
294:
295: if (approxLength > Property.IDX_PAGE_SIZE_BUMP_THRESHOLD) {
296:
297: if (((properties == null) || (properties
298: .get(Property.PAGE_SIZE_PARAMETER) == null))
299: && (PropertyUtil.getServiceProperty(
300: getLanguageConnectionContext()
301: .getTransactionCompile(),
302: Property.PAGE_SIZE_PARAMETER) == null)) {
303: // do not override the user's choice of page size, whether it
304: // is set for the whole database or just set on this statement.
305:
306: if (properties == null)
307: properties = new Properties();
308:
309: properties.put(Property.PAGE_SIZE_PARAMETER,
310: Property.PAGE_SIZE_DEFAULT_LONG);
311:
312: }
313: }
314:
315: return getGenericConstantActionFactory()
316: .getCreateIndexConstantAction(unique, indexType,
317: sd.getSchemaName(), indexName.getTableName(),
318: tableName.getTableName(), td.getUUID(),
319: conglomId, columnNames, isAscending, false,
320: null, properties);
321: }
322:
323: /**
324: * Check the uniqueness of the column names within the derived column list.
325: *
326: * @exception StandardException Thrown if column list contains a
327: * duplicate name.
328: */
329: private void verifyAndGetUniqueNames() throws StandardException {
330: int size = columnNameList.size();
331: Hashtable ht = new Hashtable(size + 2, (float) .999);
332: columnNames = new String[size];
333: isAscending = new boolean[size];
334:
335: for (int index = 0; index < size; index++) {
336: /* Verify that this column's name is unique within the list
337: * Having a space at the end meaning descending on the column
338: */
339: columnNames[index] = (String) columnNameList
340: .elementAt(index);
341: if (columnNames[index].endsWith(" ")) {
342: columnNames[index] = columnNames[index].substring(0,
343: columnNames[index].length() - 1);
344: isAscending[index] = false;
345: } else
346: isAscending[index] = true;
347:
348: Object object = ht.put(columnNames[index],
349: columnNames[index]);
350:
351: if (object != null
352: && ((String) object).equals(columnNames[index])) {
353: throw StandardException
354: .newException(
355: SQLState.LANG_DUPLICATE_COLUMN_NAME_CREATE_INDEX,
356: columnNames[index]);
357: }
358: }
359: }
360: }
|