blob: da75ddb278dd53170615d7d1948c0eecb844338e [file] [log] [blame]
/*
Derby - Class org.apache.derby.impl.sql.compile.RenameNode
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.SQLState;
import org.apache.derby.iapi.services.context.ContextManager;
import org.apache.derby.shared.common.sanity.SanityManager;
import org.apache.derby.iapi.sql.StatementType;
import org.apache.derby.iapi.sql.compile.CompilerContext;
import org.apache.derby.iapi.sql.compile.Visitor;
import org.apache.derby.iapi.sql.dictionary.ColumnDescriptor;
import org.apache.derby.iapi.sql.dictionary.ColumnDescriptorList;
import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor;
import org.apache.derby.iapi.sql.dictionary.ConstraintDescriptor;
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.dictionary.TupleDescriptor;
import org.apache.derby.iapi.sql.execute.ConstantAction;
/**
* A RenameNode is the root of a QueryTree that represents a
* RENAME TABLE/COLUMN/INDEX statement.
*
*/
class RenameNode extends DDLStatementNode
{
protected TableName newTableName;
// original name of the object being renamed
protected String oldObjectName;
// original name for that object
protected String newObjectName;
protected TableDescriptor td;
private long conglomerateNumber;
/* You can rename using either alter table or rename command to
* rename a table/column. An index can only be renamed with rename
* command. usedAlterTable flag is used to keep that information.
*/
protected boolean usedAlterTable;
/* renamingWhat will be set to 1 if user is renaming a table.
* Will be set to 2 if user is renaming a column and will be
* set to 3 if user is renaming an index
*/
protected int renamingWhat;
/**
* Constructor for a RenameNode
*
* @param tableName The name of the table. This is the table which is
* being renamed in case of rename table. In case of rename
* column, the column being renamed belongs to this table.
* In case of rename index, this is null because index name
* is unique within a schema and doesn't have to be
* associated with a table name.
* Coming from ALTER TABLE path, tableName will
* be TableName object. Mostly a TableName object, but coming from
* RENAME COLUMN path, tableName will be a String.
* @param oldObjectName This is either the name of column/index in case
* of rename column/index. For rename table, this is null.
* @param newObjectName This is new name for table/column/index
* @param usedAlterTable True-Used Alter Table, False-Used Rename.
* For rename index, this will always be false because
* there is no alter table command to rename index
* @param renamingWhat Rename a 1 - table, 2 - column, 3 - index
* @param cm context manager
*
* @exception StandardException Thrown on error
*/
RenameNode(Object tableName,
String oldObjectName,
String newObjectName,
boolean usedAlterTable,
int renamingWhat,
ContextManager cm) throws StandardException
{
super(cm);
this.usedAlterTable = usedAlterTable;
this.renamingWhat = renamingWhat;
switch (this.renamingWhat)
{
case StatementType.RENAME_TABLE:
initAndCheck((TableName) tableName);
this.newTableName =
makeTableName(getObjectName().getSchemaName(), newObjectName);
this.oldObjectName = null;
this.newObjectName = this.newTableName.getTableName();
break;
case StatementType.RENAME_COLUMN:
/* coming from ALTER TABLE path, tableName will
* be TableName object. Coming from RENAME COLUMN
* path, tableName will be just a String.
*/
TableName actingObjectName;
if (tableName instanceof TableName)
actingObjectName = (TableName) tableName;
else
actingObjectName = makeTableName(null,
(String)tableName);
initAndCheck(actingObjectName);
this.oldObjectName = oldObjectName;
this.newObjectName = newObjectName;
break;
case StatementType.RENAME_INDEX:
this.oldObjectName = oldObjectName;
this.newObjectName = newObjectName;
break;
default:
if (SanityManager.DEBUG)
SanityManager.THROWASSERT(
"Unexpected rename action in RenameNode");
}
}
/**
* 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) {
switch (renamingWhat)
{
case StatementType.RENAME_TABLE:
return super.toString() +
"oldTableName: " + "\n" + getRelativeName() + "\n" +
"newTableName: " + "\n" + newTableName + "\n" ;
case StatementType.RENAME_COLUMN:
return super.toString() +
"oldTableName.oldColumnName:" + "\n" +
getRelativeName() + "." + oldObjectName + "\n" +
"newColumnName: " + "\n" + newObjectName + "\n" ;
case StatementType.RENAME_INDEX:
return super.toString() +
"oldIndexName:" + "\n" + oldObjectName + "\n" +
"newIndexName: " + "\n" + newObjectName + "\n" ;
default:
SanityManager.THROWASSERT(
"Unexpected rename action in RenameNode");
return "UNKNOWN";
}
} else {
return "";
}
}
String statementToString()
{
if (usedAlterTable)
return "ALTER TABLE";
else {
switch (renamingWhat)
{
case StatementType.RENAME_TABLE:
return "RENAME TABLE";
case StatementType.RENAME_COLUMN:
return "RENAME COLUMN";
case StatementType.RENAME_INDEX:
return "RENAME INDEX";
default:
if (SanityManager.DEBUG)
SanityManager.THROWASSERT(
"Unexpected rename action in RenameNode");
return "UNKNOWN";
}
}
}
// We inherit the generate() method from DDLStatementNode.
/**
* Bind this node. This means doing any static error checking that
* can be done before actually renaming the table/column/index.
*
* For a table rename: looking up the from table, verifying it exists
* verifying it's not a system table, verifying it's not view
* and looking up to table, verifying it doesn't exist.
*
* For a column rename: looking up the table, verifying it exists,
* verifying it's not a system table, verifying it's not view, verifying
* the from column exists, verifying the to column doesn't exist.
*
* For a index rename: looking up the table, verifying it exists,
* verifying it's not a system table, verifying it's not view, verifying
* the from index exists, verifying the to index doesn't exist.
*
* @exception StandardException Thrown on error
*/
@Override
public void bindStatement() throws StandardException
{
CompilerContext cc = getCompilerContext();
DataDictionary dd = getDataDictionary();
ConglomerateDescriptor cd;
SchemaDescriptor sd;
/* in case of rename index, the only thing we get from parser is
* current and new index names with no information about the
* table it belongs to. This is because index names are unique
* within a schema and hence then is no need to qualify an index
* name with a table name which we have to do for rename column.
* But from the index name, using the data dictionary, you can
* find the table it belongs to. Since most of the checking
* in bind is done using table descriptor, in the following if
* statement, we are trying to get the table information from the
* index name so it is available for the rest of he bind code.
*/
TableName baseTable;
if (renamingWhat == StatementType.RENAME_INDEX) {
sd = getSchemaDescriptor((String)null);
ConglomerateDescriptor indexDescriptor =
dd.getConglomerateDescriptor(oldObjectName, sd, false);
if (indexDescriptor == null)
throw StandardException.newException(
SQLState.LANG_INDEX_NOT_FOUND, oldObjectName);
/* Get the table descriptor */
td = dd.getTableDescriptor(indexDescriptor.getTableID());
initAndCheck(makeTableName(td.getSchemaName(),
td.getName()));
} else
sd = getSchemaDescriptor();
td = getTableDescriptor();
//throw an exception if user is attempting a rename on temporary table
if (td.getTableType() == TableDescriptor.GLOBAL_TEMPORARY_TABLE_TYPE)
{
throw StandardException.newException(SQLState.LANG_NOT_ALLOWED_FOR_DECLARED_GLOBAL_TEMP_TABLE);
}
switch (this.renamingWhat)
{
case StatementType.RENAME_TABLE:
/* Verify that new table name does not exist in the database */
TableDescriptor tabDesc = getTableDescriptor(newObjectName, sd);
if (tabDesc != null)
throw descriptorExistsException(tabDesc, sd);
renameTableBind(dd);
break;
case StatementType.RENAME_COLUMN:
renameColumnBind(dd);
break;
case StatementType.RENAME_INDEX:
ConglomerateDescriptor conglomDesc = dd.getConglomerateDescriptor(newObjectName, sd, false);
if (conglomDesc != null)
throw descriptorExistsException(conglomDesc, sd);
break;
default:
if (SanityManager.DEBUG)
SanityManager.THROWASSERT(
"Unexpected rename action in RenameNode");
break;
}
conglomerateNumber = td.getHeapConglomerateId();
/* Get the base conglomerate descriptor */
cd = td.getConglomerateDescriptor(conglomerateNumber);
/* Statement is dependent on the TableDescriptor and ConglomerateDescriptor */
cc.createDependency(td);
cc.createDependency(cd);
}
/**
* 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 rename is on a SESSION schema table, then return true.
if (isSessionSchema(td.getSchemaName()))//existing table with rename action
return true;
//new name in rename action
if (renamingWhat == StatementType.RENAME_TABLE && isSessionSchema(getSchemaDescriptor()))
return true;
return false;
}
//do any checking needs to be done at bind time for rename table
private void renameTableBind(DataDictionary dd)
throws StandardException
{
/* Verify that there are no check constraints on the table */
ConstraintDescriptorList constraintDescriptorList = dd.getConstraintDescriptors(td);
int size =
constraintDescriptorList == null ? 0 : constraintDescriptorList.size();
ConstraintDescriptor constraintDescriptor;
// go through all the constraints defined on the table
for (int index = 0; index < size; index++)
{
constraintDescriptor = constraintDescriptorList.elementAt(index);
// if it is a check constraint, error
if (constraintDescriptor.getConstraintType() == DataDictionary.CHECK_CONSTRAINT)
{
throw StandardException.newException(
SQLState.LANG_PROVIDER_HAS_DEPENDENT_OBJECT,
"RENAME",
td.getName(),
"CONSTRAINT",
constraintDescriptor.getConstraintName());
}
}
}
//do any checking needs to be done at bind time for rename column
private void renameColumnBind(DataDictionary dd)
throws StandardException
{
ColumnDescriptor columnDescriptor = td.getColumnDescriptor(oldObjectName);
/* Verify that old column name does exist in the table */
if (columnDescriptor == null)
throw StandardException.newException(SQLState.LANG_COLUMN_NOT_FOUND_IN_TABLE,
oldObjectName, getFullName());
/* Verify that new column name does not exist in the table */
ColumnDescriptor cd = td.getColumnDescriptor(newObjectName);
if (cd != null)
throw descriptorExistsException(cd, td);
//
// You cannot rename a column which is referenced by the generation
// clause of a generated column.
//
ColumnDescriptorList generatedColumns = td.getGeneratedColumns();
int generatedColumnCount = generatedColumns.size();
for ( int i = 0; i < generatedColumnCount; i++ )
{
ColumnDescriptor gc = generatedColumns.elementAt( i );
String[] referencedColumns = gc.getDefaultInfo().getReferencedColumnNames();
int refColCount = referencedColumns.length;
for ( int j = 0; j < refColCount; j++ )
{
String refName = referencedColumns[ j ];
if ( oldObjectName.equals( refName ) )
{
throw StandardException.newException( SQLState.LANG_GEN_COL_BAD_RENAME, oldObjectName, gc.getColumnName() );
}
}
}
/* Verify that there are no check constraints using the column being renamed */
ConstraintDescriptorList constraintDescriptorList =
dd.getConstraintDescriptors(td);
int size =
constraintDescriptorList == null ? 0 : constraintDescriptorList.size();
ConstraintDescriptor constraintDescriptor;
ColumnDescriptorList checkConstraintCDL;
int checkConstraintCDLSize;
// go through all the constraints defined on the table
for (int index = 0; index < size; index++)
{
constraintDescriptor = constraintDescriptorList.elementAt(index);
// if it is a check constraint, verify that column being
// renamed is not used in it's sql
if (constraintDescriptor.getConstraintType() == DataDictionary.CHECK_CONSTRAINT)
{
checkConstraintCDL = constraintDescriptor.getColumnDescriptors();
checkConstraintCDLSize = checkConstraintCDL.size();
for (int index2 = 0; index2 < checkConstraintCDLSize; index2++)
if (checkConstraintCDL.elementAt( index2 ) == columnDescriptor)
throw StandardException.newException(
SQLState.LANG_RENAME_COLUMN_WILL_BREAK_CHECK_CONSTRAINT,
oldObjectName,
constraintDescriptor.getConstraintName());
}
}
}
/**
* Create the Constant information that will drive the guts of Execution
*
* @exception StandardException Thrown on failure
*/
@Override
public ConstantAction makeConstantAction()
throws StandardException
{
return getGenericConstantActionFactory().getRenameConstantAction(getFullName(),
getRelativeName(),
oldObjectName,
newObjectName,
getSchemaDescriptor(),
td.getUUID(),
usedAlterTable,
renamingWhat);
}
private StandardException descriptorExistsException(TupleDescriptor tuple,
TupleDescriptor parent)
{
return
StandardException.newException(SQLState.LANG_OBJECT_ALREADY_EXISTS_IN_OBJECT,
tuple.getDescriptorType(),
tuple.getDescriptorName(),
parent.getDescriptorType(),
parent.getDescriptorName());
}
@Override
void acceptChildren(Visitor v) throws StandardException {
super.acceptChildren(v);
if (newTableName != null) {
newTableName = (TableName) newTableName.accept(v);
}
}
}