001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.compile.RenameNode
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.reference.SQLState;
025:
026: import org.apache.derby.iapi.sql.compile.CompilerContext;
027: import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor;
028: import org.apache.derby.iapi.sql.dictionary.DataDictionary;
029: import org.apache.derby.iapi.sql.dictionary.TupleDescriptor;
030: import org.apache.derby.iapi.sql.dictionary.SchemaDescriptor;
031: import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
032: import org.apache.derby.iapi.sql.dictionary.ColumnDescriptor;
033: import org.apache.derby.iapi.sql.dictionary.ColumnDescriptorList;
034: import org.apache.derby.iapi.sql.dictionary.ConstraintDescriptor;
035: import org.apache.derby.iapi.sql.dictionary.ConstraintDescriptorList;
036: import org.apache.derby.iapi.sql.dictionary.CheckConstraintDescriptor;
037: import org.apache.derby.iapi.sql.execute.ConstantAction;
038:
039: import org.apache.derby.iapi.error.StandardException;
040:
041: import org.apache.derby.iapi.services.sanity.SanityManager;
042:
043: import org.apache.derby.iapi.sql.StatementType;
044:
045: /**
046: * A RenameNode is the root of a QueryTree that represents a
047: * RENAME TABLE/COLUMN/INDEX statement.
048: *
049: * @author Mamta Satoor
050: */
051:
052: public class RenameNode extends DDLStatementNode {
053: protected TableName newTableName;
054:
055: // original name of the object being renamed
056: protected String oldObjectName;
057: // original name for that object
058: protected String newObjectName;
059:
060: protected TableDescriptor td;
061: private long conglomerateNumber;
062:
063: /* You can rename using either alter table or rename command to
064: * rename a table/column. An index can only be renamed with rename
065: * command. usedAlterTable flag is used to keep that information.
066: */
067: protected boolean usedAlterTable;
068:
069: /* renamingWhat will be set to 1 if user is renaming a table.
070: * Will be set to 2 if user is renaming a column and will be
071: * set to 3 if user is renaming an index
072: */
073: protected int renamingWhat;
074:
075: /**
076: * Initializer for a RenameNode
077: *
078: * @param tableName The name of the table. This is the table which is
079: * being renamed in case of rename table. In case of rename
080: * column, the column being renamed belongs to this table.
081: * In case of rename index, this is null because index name
082: * is unique within a schema and doesn't have to be
083: * associated with a table name
084: * @param oldObjectName This is either the name of column/index in case
085: * of rename column/index. For rename table, this is null.
086: * @param newObjectName This is new name for table/column/index
087: * @param usedAlterTable True-Used Alter Table, False-Used Rename.
088: * For rename index, this will always be false because
089: * there is no alter table command to rename index
090: * @param renamingWhat Rename a 1 - table, 2 - column, 3 - index
091: *
092: * @exception StandardException Thrown on error
093: */
094: public void init(Object tableName, Object oldObjectName,
095: Object newObjectName, Object usedAlterTable,
096: Object renamingWhat) throws StandardException {
097: this .usedAlterTable = ((Boolean) usedAlterTable).booleanValue();
098: this .renamingWhat = ((Integer) renamingWhat).intValue();
099:
100: switch (this .renamingWhat) {
101: case StatementType.RENAME_TABLE:
102: initAndCheck((TableName) tableName);
103: this .newTableName = makeTableName(getObjectName()
104: .getSchemaName(), (String) newObjectName);
105: this .oldObjectName = null;
106: this .newObjectName = this .newTableName.getTableName();
107: break;
108:
109: case StatementType.RENAME_COLUMN:
110: /* coming from ALTER TABLE path, tableName will
111: * be TableName object. Coming from RENAME COLUMN
112: * path, tableName will be just a String.
113: */
114: TableName actingObjectName;
115: if (tableName instanceof TableName)
116: actingObjectName = (TableName) tableName;
117: else
118: actingObjectName = makeTableName(null,
119: (String) tableName);
120: initAndCheck(actingObjectName);
121:
122: this .oldObjectName = (String) oldObjectName;
123: this .newObjectName = (String) newObjectName;
124: break;
125:
126: case StatementType.RENAME_INDEX:
127: this .oldObjectName = (String) oldObjectName;
128: this .newObjectName = (String) newObjectName;
129: break;
130:
131: default:
132: if (SanityManager.DEBUG)
133: SanityManager
134: .THROWASSERT("Unexpected rename action in RenameNode");
135: }
136: }
137:
138: /**
139: * Convert this object to a String. See comments in QueryTreeNode.java
140: * for how this should be done for tree printing.
141: *
142: * @return This object as a String
143: */
144:
145: public String toString() {
146: if (SanityManager.DEBUG) {
147:
148: switch (renamingWhat) {
149: case StatementType.RENAME_TABLE:
150: return super .toString() + "oldTableName: " + "\n"
151: + getRelativeName() + "\n" + "newTableName: "
152: + "\n" + newTableName + "\n";
153:
154: case StatementType.RENAME_COLUMN:
155: return super .toString() + "oldTableName.oldColumnName:"
156: + "\n" + getRelativeName() + "."
157: + oldObjectName + "\n" + "newColumnName: "
158: + "\n" + newObjectName + "\n";
159:
160: case StatementType.RENAME_INDEX:
161: return super .toString() + "oldIndexName:" + "\n"
162: + oldObjectName + "\n" + "newIndexName: "
163: + "\n" + newObjectName + "\n";
164:
165: default:
166: SanityManager
167: .THROWASSERT("Unexpected rename action in RenameNode");
168: return "UNKNOWN";
169: }
170: } else {
171: return "";
172: }
173: }
174:
175: public String statementToString() {
176: if (usedAlterTable)
177: return "ALTER TABLE";
178: else {
179: switch (renamingWhat) {
180: case StatementType.RENAME_TABLE:
181: return "RENAME TABLE";
182:
183: case StatementType.RENAME_COLUMN:
184: return "RENAME COLUMN";
185:
186: case StatementType.RENAME_INDEX:
187: return "RENAME INDEX";
188:
189: default:
190: if (SanityManager.DEBUG)
191: SanityManager
192: .THROWASSERT("Unexpected rename action in RenameNode");
193: return "UNKNOWN";
194: }
195: }
196: }
197:
198: // We inherit the generate() method from DDLStatementNode.
199:
200: /**
201: * Bind this node. This means doing any static error checking that
202: * can be done before actually renaming the table/column/index.
203: *
204: * For a table rename: looking up the from table, verifying it exists
205: * verifying it's not a system table, verifying it's not view
206: * and looking up to table, verifying it doesn't exist.
207: *
208: * For a column rename: looking up the table, verifying it exists,
209: * verifying it's not a system table, verifying it's not view, verifying
210: * the from column exists, verifying the to column doesn't exist.
211: *
212: * For a index rename: looking up the table, verifying it exists,
213: * verifying it's not a system table, verifying it's not view, verifying
214: * the from index exists, verifying the to index doesn't exist.
215: *
216: * @return The bound query tree
217: *
218: * @exception StandardException Thrown on error
219: */
220:
221: public QueryTreeNode bind() throws StandardException {
222: CompilerContext cc = getCompilerContext();
223: DataDictionary dd = getDataDictionary();
224: ConglomerateDescriptor cd;
225: SchemaDescriptor sd;
226:
227: /* in case of rename index, the only thing we get from parser is
228: * current and new index names with no information about the
229: * table it belongs to. This is because index names are unique
230: * within a schema and hence then is no need to qualify an index
231: * name with a table name which we have to do for rename column.
232: * But from the index name, using the data dictionary, you can
233: * find the table it belongs to. Since most of the checking
234: * in bind is done using table descriptor, in the following if
235: * statement, we are trying to get the table information from the
236: * index name so it is available for the rest of he bind code.
237: */
238: TableName baseTable;
239:
240: if (renamingWhat == StatementType.RENAME_INDEX) {
241:
242: sd = getSchemaDescriptor((String) null);
243:
244: ConglomerateDescriptor indexDescriptor = dd
245: .getConglomerateDescriptor(oldObjectName, sd, false);
246: if (indexDescriptor == null)
247: throw StandardException.newException(
248: SQLState.LANG_INDEX_NOT_FOUND, oldObjectName);
249: /* Get the table descriptor */
250: td = dd.getTableDescriptor(indexDescriptor.getTableID());
251: initAndCheck(makeTableName(td.getSchemaName(), td.getName()));
252: } else
253: sd = getSchemaDescriptor();
254:
255: td = getTableDescriptor();
256:
257: //throw an exception if user is attempting a rename on temporary table
258: if (td.getTableType() == TableDescriptor.GLOBAL_TEMPORARY_TABLE_TYPE) {
259: throw StandardException
260: .newException(SQLState.LANG_NOT_ALLOWED_FOR_DECLARED_GLOBAL_TEMP_TABLE);
261: }
262:
263: switch (this .renamingWhat) {
264: case StatementType.RENAME_TABLE:
265: /* Verify that new table name does not exist in the database */
266: TableDescriptor td = getTableDescriptor(newObjectName, sd);
267: if (td != null)
268: throw descriptorExistsException(td, sd);
269: renameTableBind(dd);
270: break;
271:
272: case StatementType.RENAME_COLUMN:
273: renameColumnBind(dd);
274: break;
275:
276: case StatementType.RENAME_INDEX:
277: ConglomerateDescriptor conglomDesc = dd
278: .getConglomerateDescriptor(newObjectName, sd, false);
279: if (conglomDesc != null)
280: throw descriptorExistsException(conglomDesc, sd);
281: break;
282:
283: default:
284: if (SanityManager.DEBUG)
285: SanityManager
286: .THROWASSERT("Unexpected rename action in RenameNode");
287: break;
288: }
289:
290: conglomerateNumber = td.getHeapConglomerateId();
291:
292: /* Get the base conglomerate descriptor */
293: cd = td.getConglomerateDescriptor(conglomerateNumber);
294:
295: /* Statement is dependent on the TableDescriptor and ConglomerateDescriptor */
296: cc.createDependency(td);
297: cc.createDependency(cd);
298:
299: return this ;
300: }
301:
302: /**
303: * Return true if the node references SESSION schema tables (temporary or permanent)
304: *
305: * @return true if references SESSION schema tables, else false
306: *
307: * @exception StandardException Thrown on error
308: */
309: public boolean referencesSessionSchema() throws StandardException {
310: //If rename is on a SESSION schema table, then return true.
311: if (isSessionSchema(td.getSchemaName()))//existing table with rename action
312: return true;
313:
314: //new name in rename action
315: if (renamingWhat == StatementType.RENAME_TABLE
316: && isSessionSchema(getSchemaDescriptor()))
317: return true;
318:
319: return false;
320: }
321:
322: //do any checking needs to be done at bind time for rename table
323: private void renameTableBind(DataDictionary dd)
324: throws StandardException {
325: /* Verify that there are no check constraints on the table */
326: ConstraintDescriptorList constraintDescriptorList = dd
327: .getConstraintDescriptors(td);
328: int size = constraintDescriptorList == null ? 0
329: : constraintDescriptorList.size();
330:
331: ConstraintDescriptor constraintDescriptor;
332:
333: // go through all the constraints defined on the table
334: for (int index = 0; index < size; index++) {
335: constraintDescriptor = constraintDescriptorList
336: .elementAt(index);
337: // if it is a check constraint, error
338: if (constraintDescriptor.getConstraintType() == DataDictionary.CHECK_CONSTRAINT) {
339: throw StandardException.newException(
340: SQLState.LANG_PROVIDER_HAS_DEPENDENT_OBJECT,
341: "RENAME", td.getName(), "CONSTRAINT",
342: constraintDescriptor.getConstraintName());
343: }
344: }
345: }
346:
347: //do any checking needs to be done at bind time for rename column
348: private void renameColumnBind(DataDictionary dd)
349: throws StandardException {
350: ColumnDescriptor columnDescriptor = td
351: .getColumnDescriptor(oldObjectName);
352:
353: /* Verify that old column name does exist in the table */
354: if (columnDescriptor == null)
355: throw StandardException.newException(
356: SQLState.LANG_COLUMN_NOT_FOUND_IN_TABLE,
357: oldObjectName, getFullName());
358:
359: /* Verify that new column name does not exist in the table */
360: ColumnDescriptor cd = td.getColumnDescriptor(newObjectName);
361: if (cd != null)
362: throw descriptorExistsException(cd, td);
363:
364: /* Verify that there are no check constraints using the column being renamed */
365: ConstraintDescriptorList constraintDescriptorList = dd
366: .getConstraintDescriptors(td);
367: int size = constraintDescriptorList == null ? 0
368: : constraintDescriptorList.size();
369:
370: ConstraintDescriptor constraintDescriptor;
371: ColumnDescriptorList checkConstraintCDL;
372: int checkConstraintCDLSize;
373:
374: // go through all the constraints defined on the table
375: for (int index = 0; index < size; index++) {
376: constraintDescriptor = constraintDescriptorList
377: .elementAt(index);
378: // if it is a check constraint, verify that column being
379: // renamed is not used in it's sql
380: if (constraintDescriptor.getConstraintType() == DataDictionary.CHECK_CONSTRAINT) {
381: checkConstraintCDL = constraintDescriptor
382: .getColumnDescriptors();
383: checkConstraintCDLSize = checkConstraintCDL.size();
384:
385: for (int index2 = 0; index2 < checkConstraintCDLSize; index2++)
386: if (checkConstraintCDL.elementAt(index2) == columnDescriptor)
387: throw StandardException
388: .newException(
389: SQLState.LANG_RENAME_COLUMN_WILL_BREAK_CHECK_CONSTRAINT,
390: oldObjectName,
391: constraintDescriptor
392: .getConstraintName());
393: }
394: }
395: }
396:
397: /**
398: * Create the Constant information that will drive the guts of Execution
399: *
400: * @exception StandardException Thrown on failure
401: */
402: public ConstantAction makeConstantAction() throws StandardException {
403: return getGenericConstantActionFactory()
404: .getRenameConstantAction(getFullName(),
405: getRelativeName(), oldObjectName,
406: newObjectName, getSchemaDescriptor(),
407: td.getUUID(), usedAlterTable, renamingWhat);
408: }
409:
410: private StandardException descriptorExistsException(
411: TupleDescriptor tuple, TupleDescriptor parent) {
412: return StandardException.newException(
413: SQLState.LANG_OBJECT_ALREADY_EXISTS_IN_OBJECT, tuple
414: .getDescriptorType(),
415: tuple.getDescriptorName(), parent.getDescriptorType(),
416: parent.getDescriptorName());
417: }
418: }
|