| /* |
| |
| Derby - Class org.apache.derby.impl.sql.compile.AlterTableNode |
| |
| Licensed to the Apache Software Foundation (ASF) under one or more |
| contributor license agreements. See the NOTICE file distributed with |
| this work for additional information regarding copyright ownership. |
| The ASF licenses this file to you under the Apache License, Version 2.0 |
| (the "License"); you may not use this file except in compliance with |
| the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| |
| */ |
| |
| package org.apache.derby.impl.sql.compile; |
| |
| import org.apache.derby.iapi.error.StandardException; |
| import org.apache.derby.iapi.reference.Limits; |
| import org.apache.derby.iapi.reference.SQLState; |
| import org.apache.derby.iapi.services.context.ContextManager; |
| import org.apache.derby.iapi.services.io.FormatableBitSet; |
| import org.apache.derby.shared.common.sanity.SanityManager; |
| import org.apache.derby.iapi.sql.compile.Visitor; |
| import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor; |
| import org.apache.derby.iapi.sql.dictionary.ConstraintDescriptorList; |
| import org.apache.derby.iapi.sql.dictionary.DataDictionary; |
| import org.apache.derby.iapi.sql.dictionary.SchemaDescriptor; |
| import org.apache.derby.iapi.sql.dictionary.TableDescriptor; |
| import org.apache.derby.iapi.sql.execute.ConstantAction; |
| import org.apache.derby.impl.sql.execute.ColumnInfo; |
| import org.apache.derby.impl.sql.execute.ConstraintConstantAction; |
| import org.apache.derby.impl.sql.execute.CreateConstraintConstantAction; |
| |
| /** |
| * A AlterTableNode represents a DDL statement that alters a table. |
| * It contains the name of the object to be created. |
| * |
| */ |
| |
| class AlterTableNode extends DDLStatementNode |
| { |
| // The alter table action |
| public TableElementList tableElementList = null; |
| char lockGranularity; |
| |
| /** |
| * updateStatistics will indicate that we are here for updating the |
| * statistics. It could be statistics of just one index or all the |
| * indexes on a given table. |
| */ |
| private boolean updateStatistics = false; |
| /** |
| * The flag updateStatisticsAll will tell if we are going to update the |
| * statistics of all indexes or just one index on a table. |
| */ |
| private boolean updateStatisticsAll = false; |
| /** |
| * dropStatistics will indicate that we are here for dropping the |
| * statistics. It could be statistics of just one index or all the |
| * indexes on a given table. |
| */ |
| private boolean dropStatistics; |
| /** |
| * The flag dropStatisticsAll will tell if we are going to drop the |
| * statistics of all indexes or just one index on a table. |
| */ |
| private boolean dropStatisticsAll; |
| /** |
| * If statistic is getting updated/dropped for just one index, then |
| * indexNameForStatistics will tell the name of the specific index |
| * whose statistics need to be updated/dropped. |
| */ |
| private String indexNameForStatistics; |
| |
| public boolean compressTable = false; |
| public boolean sequential = false; |
| //The following three (purge, defragment and truncateEndOfTable) apply for |
| //inplace compress |
| public boolean purge = false; |
| public boolean defragment = false; |
| public boolean truncateEndOfTable = false; |
| |
| public int behavior; // currently for drop column |
| |
| public TableDescriptor baseTable; |
| |
| protected int numConstraints; |
| |
| private int changeType = UNKNOWN_TYPE; |
| |
| private boolean truncateTable = false; |
| |
| // constant action arguments |
| |
| protected SchemaDescriptor schemaDescriptor = null; |
| protected ColumnInfo[] colInfos = null; |
| protected ConstraintConstantAction[] conActions = null; |
| |
| |
| /** |
| * Constructor for TRUNCATE TABLE |
| * |
| * @param tableName The name of the table being truncated |
| * @param cm Context manager |
| * @exception StandardException |
| */ |
| AlterTableNode(TableName tableName, |
| ContextManager cm) throws StandardException { |
| super(tableName, cm); |
| truncateTable = true; |
| schemaDescriptor = getSchemaDescriptor(); |
| } |
| |
| /** |
| * Constructor for COMPRESS using temporary tables |
| * rather than in place compress |
| * |
| * @param tableName The name of the table being altered |
| * @param sequential Whether or not the COMPRESS is SEQUENTIAL |
| * @param cm Context manager |
| * |
| * @exception StandardException Thrown on error |
| */ |
| AlterTableNode(TableName tableName, |
| boolean sequential, |
| ContextManager cm) throws StandardException { |
| super(tableName, cm); |
| this.sequential = sequential; |
| compressTable = true; |
| schemaDescriptor = getSchemaDescriptor(); |
| } |
| |
| /** |
| * Constructor for INPLACE COMPRESS |
| * |
| * @param tableName The name of the table being altered |
| * @param purge PURGE during INPLACE COMPRESS? |
| * @param defragment DEFRAGMENT during INPLACE COMPRESS? |
| * @param truncateEndOfTable TRUNCATE END during INPLACE COMPRESS? |
| * @param cm Context manager |
| * |
| * @exception StandardException Thrown on error |
| */ |
| AlterTableNode(TableName tableName, |
| boolean purge, |
| boolean defragment, |
| boolean truncateEndOfTable, |
| ContextManager cm) throws StandardException { |
| super(tableName, cm); |
| this.purge = purge; |
| this.defragment = defragment; |
| this.truncateEndOfTable = truncateEndOfTable; |
| compressTable = true; |
| schemaDescriptor = getSchemaDescriptor(true, false); |
| } |
| |
| /** |
| * Constructor for UPDATE_STATISTICS or DROP_STATISTICS |
| * |
| * @param tableName The name of the table being altered |
| * @param changeType update or drop statistics |
| * @param statsAll {@code true} means update or drop |
| * the statistics of all the indexes on the table. |
| * {@code false} means update or drop the statistics of |
| * only the index name provided by next parameter. |
| * @param indexName Name of the index for which statistics is to be updated |
| * or dropped |
| * @param cm Context manager |
| * @throws StandardException |
| */ |
| AlterTableNode( |
| TableName tableName, |
| int changeType, |
| boolean statsAll, |
| String indexName, |
| ContextManager cm) throws StandardException { |
| super(tableName, cm); |
| this.changeType = changeType; |
| this.indexNameForStatistics = indexName; |
| |
| switch (changeType) { |
| case UPDATE_STATISTICS: |
| this.updateStatisticsAll = statsAll; |
| updateStatistics = true; |
| break; |
| case DROP_STATISTICS: |
| this.dropStatisticsAll = statsAll; |
| dropStatistics = true; |
| break; |
| default: |
| if (SanityManager.DEBUG) { |
| SanityManager.NOTREACHED(); |
| } |
| } |
| |
| schemaDescriptor = getSchemaDescriptor(); |
| } |
| |
| /** |
| * Constructor for ADD_TYPE, DROP_TYPE, MODIFY_TYPE and LOCK_TYPE |
| * @param tableName The name of the table being altered |
| * @param changeType add, drop, modify or lock |
| * @param impactedElements list of table elements impacted |
| * @param lockGranularity lock granularity encoded in a single character |
| * @param behavior cascade or restrict (for DROP_TYPE) |
| * @param cm Context Manager |
| * @throws StandardException |
| */ |
| AlterTableNode( |
| TableName tableName, |
| int changeType, |
| TableElementList impactedElements, |
| char lockGranularity, |
| int behavior, |
| ContextManager cm) throws StandardException { |
| super(tableName, cm); |
| this.changeType = changeType; |
| |
| switch (changeType) { |
| case ADD_TYPE: |
| case DROP_TYPE: |
| case MODIFY_TYPE: |
| case LOCKING_TYPE: |
| this.tableElementList = impactedElements; |
| this.lockGranularity = lockGranularity; |
| this.behavior = behavior; |
| break; |
| default: |
| if (SanityManager.DEBUG) { |
| SanityManager.NOTREACHED(); |
| } |
| } |
| schemaDescriptor = getSchemaDescriptor(); |
| } |
| |
| /** |
| * Convert this object to a String. See comments in QueryTreeNode.java |
| * for how this should be done for tree printing. |
| * |
| * @return This object as a String |
| */ |
| @Override |
| public String toString() |
| { |
| if (SanityManager.DEBUG) |
| { |
| return super.toString() + |
| "objectName: " + getObjectName() + "\n" + |
| "lockGranularity: " + lockGranularity + "\n" + |
| "compressTable: " + compressTable + "\n" + |
| "sequential: " + sequential + "\n" + |
| "truncateTable: " + truncateTable + "\n" + |
| "purge: " + purge + "\n" + |
| "defragment: " + defragment + "\n" + |
| "truncateEndOfTable: " + truncateEndOfTable + "\n" + |
| "updateStatistics: " + updateStatistics + "\n" + |
| "updateStatisticsAll: " + updateStatisticsAll + "\n" + |
| "dropStatistics: " + dropStatistics + "\n" + |
| "dropStatisticsAll: " + dropStatisticsAll + "\n" + |
| "indexNameForStatistics: " + |
| indexNameForStatistics + "\n"; |
| } |
| else |
| { |
| return ""; |
| } |
| } |
| |
| /** |
| * Prints the sub-nodes of this object. See QueryTreeNode.java for |
| * how tree printing is supposed to work. |
| * @param depth The depth to indent the sub-nodes |
| */ |
| @Override |
| void printSubNodes(int depth) { |
| if (SanityManager.DEBUG) { |
| if (tableElementList != null) { |
| printLabel(depth, "tableElementList: "); |
| tableElementList.treePrint(depth + 1); |
| } |
| |
| } |
| } |
| |
| public String statementToString() |
| { |
| if(truncateTable) |
| return "TRUNCATE TABLE"; |
| else |
| return "ALTER TABLE"; |
| } |
| |
| public int getChangeType() { return changeType; } |
| |
| // We inherit the generate() method from DDLStatementNode. |
| |
| /** |
| * Bind this AlterTableNode. This means doing any static error |
| * checking that can be done before actually creating the table. |
| * For example, verifying that the user is not trying to add a |
| * non-nullable column. |
| * |
| * |
| * @exception StandardException Thrown on error |
| */ |
| @Override |
| public void bindStatement() throws StandardException |
| { |
| DataDictionary dd = getDataDictionary(); |
| int numCheckConstraints = 0; |
| int numReferenceConstraints = 0; |
| int numGenerationClauses = 0; |
| int numBackingIndexes = 0; |
| |
| /* |
| ** Get the table descriptor. Checks the schema |
| ** and the table. |
| */ |
| if(compressTable && (purge || defragment || truncateEndOfTable)) { |
| //We are dealing with inplace compress here and inplace compress is |
| //allowed on system schemas. In order to support inplace compress |
| //on user as well as system tables, we need to use special |
| //getTableDescriptor(boolean) call to get TableDescriptor. This |
| //getTableDescriptor(boolean) allows getting TableDescriptor for |
| //system tables without throwing an exception. |
| baseTable = getTableDescriptor(false); |
| } else |
| baseTable = getTableDescriptor(); |
| |
| //throw an exception if user is attempting to alter a temporary table |
| if (baseTable.getTableType() == TableDescriptor.GLOBAL_TEMPORARY_TABLE_TYPE) |
| { |
| throw StandardException.newException(SQLState.LANG_NOT_ALLOWED_FOR_DECLARED_GLOBAL_TEMP_TABLE); |
| } |
| |
| /* Statement is dependent on the TableDescriptor */ |
| getCompilerContext().createDependency(baseTable); |
| |
| //If we are dealing with add column character type, then set that |
| //column's collation type to be the collation type of the schema. |
| //The collation derivation of such a column would be "implicit". |
| if (changeType == ADD_TYPE) {//the action is of type add. |
| if (tableElementList != null) {//check if is is add column |
| for (int i=0; i<tableElementList.size();i++) { |
| if (tableElementList.elementAt(i) instanceof ColumnDefinitionNode) { |
| ColumnDefinitionNode cdn = (ColumnDefinitionNode) tableElementList.elementAt(i); |
| //check if we are dealing with add character column |
| // |
| // For generated columns which omit an explicit |
| // datatype, we have to defer this work until we bind |
| // the generation clause |
| // |
| |
| if ( cdn.hasGenerationClause() && ( cdn.getType() == null ) ) { continue; } |
| |
| if ( cdn.getType() == null ) |
| { |
| throw StandardException.newException |
| ( SQLState.LANG_NEEDS_DATATYPE, cdn.getColumnName() ); |
| } |
| |
| if (cdn.getType().getTypeId().isStringTypeId()) { |
| //we found what we are looking for. Set the |
| //collation type of this column to be the same as |
| //schema descriptor's collation. Set the collation |
| //derivation as implicit |
| cdn.setCollationType(schemaDescriptor.getCollationType()); |
| } |
| } |
| } |
| |
| } |
| } |
| if (tableElementList != null) |
| { |
| tableElementList.validate(this, dd, baseTable); |
| |
| /* Only 1012 columns allowed per table */ |
| if ((tableElementList.countNumberOfColumns() + baseTable.getNumberOfColumns()) > Limits.DB2_MAX_COLUMNS_IN_TABLE) |
| { |
| throw StandardException.newException(SQLState.LANG_TOO_MANY_COLUMNS_IN_TABLE_OR_VIEW, |
| String.valueOf(tableElementList.countNumberOfColumns() + baseTable.getNumberOfColumns()), |
| getRelativeName(), |
| String.valueOf(Limits.DB2_MAX_COLUMNS_IN_TABLE)); |
| } |
| /* Number of backing indexes in the alter table statment */ |
| numBackingIndexes = tableElementList.countConstraints(DataDictionary.PRIMARYKEY_CONSTRAINT) + |
| tableElementList.countConstraints(DataDictionary.FOREIGNKEY_CONSTRAINT) + |
| tableElementList.countConstraints(DataDictionary.UNIQUE_CONSTRAINT); |
| /* Check the validity of all check constraints */ |
| numCheckConstraints = tableElementList.countConstraints( |
| DataDictionary.CHECK_CONSTRAINT); |
| |
| numReferenceConstraints = tableElementList.countConstraints( |
| DataDictionary.FOREIGNKEY_CONSTRAINT); |
| |
| numGenerationClauses = tableElementList.countGenerationClauses(); |
| } |
| |
| //If the sum of backing indexes for constraints in alter table statement and total number of indexes on the table |
| //so far is more than 32767, then we need to throw an exception |
| if ((numBackingIndexes + baseTable.getTotalNumberOfIndexes()) > Limits.DB2_MAX_INDEXES_ON_TABLE) |
| { |
| throw StandardException.newException(SQLState.LANG_TOO_MANY_INDEXES_ON_TABLE, |
| String.valueOf(numBackingIndexes + baseTable.getTotalNumberOfIndexes()), |
| getRelativeName(), |
| String.valueOf(Limits.DB2_MAX_INDEXES_ON_TABLE)); |
| } |
| |
| if ( (numCheckConstraints > 0) || (numGenerationClauses > 0) || (numReferenceConstraints > 0)) |
| { |
| /* In order to check the validity of the check constraints and |
| * generation clauses |
| * we must goober up a FromList containing a single table, |
| * the table being alter, with an RCL containing the existing and |
| * new columns and their types. This will allow us to |
| * bind the constraint definition trees against that |
| * FromList. When doing this, we verify that there are |
| * no nodes which can return non-deterministic results. |
| */ |
| FromList fromList = makeFromList( dd, tableElementList, false ); |
| FormatableBitSet generatedColumns = baseTable.makeColumnMap( baseTable.getGeneratedColumns() ); |
| |
| /* Now that we've finally goobered stuff up, bind and validate |
| * the check constraints and generation clauses. |
| */ |
| if (numGenerationClauses > 0) |
| { tableElementList.bindAndValidateGenerationClauses( schemaDescriptor, fromList, generatedColumns, baseTable ); } |
| if (numCheckConstraints > 0) { tableElementList.bindAndValidateCheckConstraints(fromList); } |
| if ( numReferenceConstraints > 0) { tableElementList.validateForeignKeysOnGenerationClauses( fromList, generatedColumns ); } |
| } |
| |
| // must be done after resolving the datatypes of the generation clauses |
| if (tableElementList != null) { tableElementList.validatePrimaryKeyNullability(); } |
| |
| //Check if we are in alter table to update/drop the statistics. If yes, |
| // then check if we are here to update/drop the statistics of a specific |
| // index. If yes, then verify that the indexname provided is a valid one. |
| if ((updateStatistics && !updateStatisticsAll) || (dropStatistics && !dropStatisticsAll)) |
| { |
| ConglomerateDescriptor cd = null; |
| if (schemaDescriptor.getUUID() != null) |
| cd = dd.getConglomerateDescriptor(indexNameForStatistics, schemaDescriptor, false); |
| |
| if (cd == null) |
| { |
| throw StandardException.newException( |
| SQLState.LANG_INDEX_NOT_FOUND, |
| schemaDescriptor.getSchemaName() + "." + indexNameForStatistics); |
| } |
| } |
| |
| /* Unlike most other DDL, we will make this ALTER TABLE statement |
| * dependent on the table being altered. In general, we try to |
| * avoid this for DDL, but we are already requiring the table to |
| * exist at bind time (not required for create index) and we don't |
| * want the column ids to change out from under us before |
| * execution. |
| */ |
| getCompilerContext().createDependency(baseTable); |
| } |
| |
| /** |
| * Return true if the node references SESSION schema tables (temporary or permanent) |
| * |
| * @return true if references SESSION schema tables, else false |
| * |
| * @exception StandardException Thrown on error |
| */ |
| @Override |
| public boolean referencesSessionSchema() |
| throws StandardException |
| { |
| //If alter table is on a SESSION schema table, then return true. |
| return isSessionSchema(baseTable.getSchemaName()); |
| } |
| |
| /** |
| * Create the Constant information that will drive the guts of Execution. |
| * |
| * @exception StandardException Thrown on failure |
| */ |
| @Override |
| public ConstantAction makeConstantAction() throws StandardException |
| { |
| prepConstantAction(); |
| |
| return getGenericConstantActionFactory().getAlterTableConstantAction(schemaDescriptor, |
| getRelativeName(), |
| baseTable.getUUID(), |
| baseTable.getHeapConglomerateId(), |
| TableDescriptor.BASE_TABLE_TYPE, |
| colInfos, |
| conActions, |
| lockGranularity, |
| compressTable, |
| behavior, |
| sequential, |
| truncateTable, |
| purge, |
| defragment, |
| truncateEndOfTable, |
| updateStatistics, |
| updateStatisticsAll, |
| dropStatistics, |
| dropStatisticsAll, |
| indexNameForStatistics); |
| } |
| |
| /** |
| * Generate arguments to constant action. Called by makeConstantAction() in this class and in |
| * our subclass RepAlterTableNode. |
| * |
| * |
| * @exception StandardException Thrown on failure |
| */ |
| private void prepConstantAction() throws StandardException |
| { |
| if (tableElementList != null) |
| { |
| genColumnInfo(); |
| } |
| |
| /* If we've seen a constraint, then build a constraint list */ |
| |
| if (numConstraints > 0) |
| { |
| conActions = new ConstraintConstantAction[numConstraints]; |
| |
| tableElementList.genConstraintActions(false, conActions, getRelativeName(), schemaDescriptor, |
| getDataDictionary()); |
| |
| for (int conIndex = 0; conIndex < conActions.length; conIndex++) |
| { |
| ConstraintConstantAction cca = conActions[conIndex]; |
| |
| if (cca instanceof CreateConstraintConstantAction) |
| { |
| int constraintType = cca.getConstraintType(); |
| if (constraintType == DataDictionary.PRIMARYKEY_CONSTRAINT) |
| { |
| DataDictionary dd = getDataDictionary(); |
| // Check to see if a constraint of the same type |
| // already exists |
| ConstraintDescriptorList cdl = |
| dd.getConstraintDescriptors(baseTable); |
| |
| if (cdl.getPrimaryKey() != null) |
| { |
| throw StandardException.newException( |
| SQLState.LANG_ADD_PRIMARY_KEY_FAILED1, |
| baseTable.getQualifiedName()); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Generate the ColumnInfo argument for the constant action. Return the number of constraints. |
| */ |
| public void genColumnInfo() |
| throws StandardException |
| { |
| // for each column, stuff system.column |
| colInfos = new ColumnInfo[tableElementList.countNumberOfColumns()]; |
| |
| numConstraints = tableElementList.genColumnInfos(colInfos); |
| } |
| |
| |
| /** |
| * Accept the visitor for all visitable children of this node. |
| * |
| * @param v the visitor |
| * |
| * @exception StandardException on error |
| */ |
| @Override |
| void acceptChildren(Visitor v) |
| throws StandardException |
| { |
| super.acceptChildren(v); |
| |
| if (tableElementList != null) |
| { |
| tableElementList.accept(v); |
| } |
| } |
| |
| /* |
| * class interface |
| */ |
| } |
| |
| |
| |
| |