com.mckoi.database.interpret.AlterTable Maven / Gradle / Ivy
/**
* com.mckoi.database.interpret.AlterTable 14 Sep 2001
*
* Mckoi SQL Database ( http://www.mckoi.com/database )
* Copyright (C) 2000, 2001, 2002 Diehl and Associates, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* Version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License Version 2 for more details.
*
* You should have received a copy of the GNU General Public License
* Version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Change Log:
*
*
*/
package com.mckoi.database.interpret;
import com.mckoi.database.*;
import com.mckoi.util.IntegerVector;
import java.util.ArrayList;
import java.util.List;
/**
* Logic for the ALTER TABLE SQL statement.
*
* @author Tobias Downer
*/
public class AlterTable extends Statement {
/**
* The create statement that we use to alter the current table. This is
* only for compatibility reasons.
*/
StatementTree create_statement;
/**
* The name of the table we are altering.
*/
String table_name;
/**
* The list of actions to perform in this alter statement.
*/
private ArrayList actions;
/**
* The TableName object.
*/
private TableName tname;
/**
* The prepared create table statement.
*/
CreateTable create_stmt;
/**
* Adds an action to perform in this alter statement.
*/
public void addAction(AlterTableAction action) {
if (actions == null) {
actions = new ArrayList();
}
actions.add(action);
}
/**
* Returns true if the column names match. If the database is in case
* insensitive mode then the columns will match if the case insensitive
* search matches.
*/
public boolean checkColumnNamesMatch(DatabaseConnection db,
String col1, String col2) {
if (db.isInCaseInsensitiveMode()) {
return col1.equalsIgnoreCase(col2);
}
return col1.equals(col2);
}
private void checkColumnConstraint(String col_name, String[] cols,
TableName table, String constraint_name) {
for (int i = 0; i < cols.length; ++i) {
if (col_name.equals(cols[i])) {
throw new DatabaseConstraintViolationException(
DatabaseConstraintViolationException.DROP_COLUMN_VIOLATION,
"Constraint violation (" + constraint_name +
") dropping column " + col_name + " because of " +
"referential constraint in " + table);
}
}
}
// ---------- Implemented from Statement ----------
public void prepare() throws DatabaseException {
// Get variables from the model
table_name = (String) cmd.getObject("table_name");
addAction((AlterTableAction) cmd.getObject("alter_action"));
create_statement = (StatementTree) cmd.getObject("create_statement");
// ---
if (create_statement != null) {
create_stmt = new CreateTable();
create_stmt.init(database, create_statement, null);
create_stmt.prepare();
this.table_name = create_stmt.table_name;
// create_statement.doPrepare(db, user);
}
else {
// If we don't have a create statement, then this is an SQL alter
// command.
}
// tname = TableName.resolve(db.getCurrentSchema(), table_name);
tname = resolveTableName(table_name, database);
if (tname.getName().indexOf('.') != -1) {
throw new DatabaseException("Table name can not contain '.' character.");
}
}
public Table evaluate() throws DatabaseException {
DatabaseQueryContext context = new DatabaseQueryContext(database);
String schema_name = database.getCurrentSchema();
// Does the user have privs to alter this tables?
if (!database.getDatabase().canUserAlterTableObject(context, user, tname)) {
throw new UserAccessException(
"User not permitted to alter table: " + table_name);
}
if (create_statement != null) {
// Create the data table definition and tell the database to update it.
DataTableDef table_def = create_stmt.createDataTableDef();
TableName tname = table_def.getTableName();
// Is the table in the database already?
if (database.tableExists(tname)) {
// Drop any schema for this table,
database.dropAllConstraintsForTable(tname);
database.updateTable(table_def);
}
// If the table isn't in the database,
else {
database.createTable(table_def);
}
// Setup the constraints
create_stmt.setupAllConstraints();
// Return '0' if we created the table.
return FunctionTable.resultTable(context, 0);
}
else {
// SQL alter command using the alter table actions,
// Get the table definition for the table name,
DataTableDef table_def = database.getTable(tname).getDataTableDef();
String table_name = table_def.getName();
DataTableDef new_table = table_def.noColumnCopy();
// Returns a ColumnChecker implementation for this table.
ColumnChecker checker =
ColumnChecker.standardColumnChecker(database, tname);
// Set to true if the table topology is alter, or false if only
// the constraints are changed.
boolean table_altered = false;
for (int n = 0; n < table_def.columnCount(); ++n) {
DataTableColumnDef column =
new DataTableColumnDef(table_def.columnAt(n));
String col_name = column.getName();
// Apply any actions to this column
boolean mark_dropped = false;
for (int i = 0; i < actions.size(); ++i) {
AlterTableAction action = (AlterTableAction) actions.get(i);
if (action.getAction().equals("ALTERSET") &&
checkColumnNamesMatch(database,
(String) action.getElement(0),
col_name)) {
Expression exp = (Expression) action.getElement(1);
checker.checkExpression(exp);
column.setDefaultExpression(exp);
table_altered = true;
}
else if (action.getAction().equals("DROPDEFAULT") &&
checkColumnNamesMatch(database,
(String) action.getElement(0),
col_name)) {
column.setDefaultExpression(null);
table_altered = true;
}
else if (action.getAction().equals("DROP") &&
checkColumnNamesMatch(database,
(String) action.getElement(0),
col_name)) {
// Check there are no referential links to this column
Transaction.ColumnGroupReference[] refs =
database.queryTableImportedForeignKeyReferences(tname);
for (int p = 0; p < refs.length; ++p) {
checkColumnConstraint(col_name, refs[p].ref_columns,
refs[p].ref_table_name, refs[p].name);
}
// Or from it
refs = database.queryTableForeignKeyReferences(tname);
for (int p = 0; p < refs.length; ++p) {
checkColumnConstraint(col_name, refs[p].key_columns,
refs[p].key_table_name, refs[p].name);
}
// Or that it's part of a primary key
Transaction.ColumnGroup primary_key =
database.queryTablePrimaryKeyGroup(tname);
if (primary_key != null) {
checkColumnConstraint(col_name, primary_key.columns,
tname, primary_key.name);
}
// Or that it's part of a unique set
Transaction.ColumnGroup[] uniques =
database.queryTableUniqueGroups(tname);
for (int p = 0; p < uniques.length; ++p) {
checkColumnConstraint(col_name, uniques[p].columns,
tname, uniques[p].name);
}
mark_dropped = true;
table_altered = true;
}
}
// If not dropped then add to the new table definition.
if (!mark_dropped) {
new_table.addColumn(column);
}
}
// Add any new columns,
for (int i = 0; i < actions.size(); ++i) {
AlterTableAction action = (AlterTableAction) actions.get(i);
if (action.getAction().equals("ADD")) {
ColumnDef cdef = (ColumnDef) action.getElement(0);
if (cdef.isUnique() || cdef.isPrimaryKey()) {
throw new DatabaseException("Can not use UNIQUE or PRIMARY KEY " +
"column constraint when altering a column. Use " +
"ADD CONSTRAINT instead.");
}
// Convert to a DataTableColumnDef
DataTableColumnDef col = CreateTable.convertColumnDef(cdef);
checker.checkExpression(
col.getDefaultExpression(database.getSystem()));
String col_name = col.getName();
// If column name starts with [table_name]. then strip it off
col.setName(checker.stripTableName(table_name, col_name));
new_table.addColumn(col);
table_altered = true;
}
}
// Any constraints to drop...
for (int i = 0; i < actions.size(); ++i) {
AlterTableAction action = (AlterTableAction) actions.get(i);
if (action.getAction().equals("DROP_CONSTRAINT")) {
String constraint_name = (String) action.getElement(0);
int drop_count = database.dropNamedConstraint(tname, constraint_name);
if (drop_count == 0) {
throw new DatabaseException(
"Named constraint to drop on table " + tname +
" was not found: " + constraint_name);
}
}
else if (action.getAction().equals("DROP_CONSTRAINT_PRIMARY_KEY")) {
boolean constraint_dropped =
database.dropPrimaryKeyConstraintForTable(tname, null);
if (!constraint_dropped) {
throw new DatabaseException(
"No primary key to delete on table " + tname);
}
}
}
// Any constraints to add...
for (int i = 0; i < actions.size(); ++i) {
AlterTableAction action = (AlterTableAction) actions.get(i);
if (action.getAction().equals("ADD_CONSTRAINT")) {
ConstraintDef constraint = (ConstraintDef) action.getElement(0);
boolean foreign_constraint =
(constraint.type == ConstraintDef.FOREIGN_KEY);
TableName ref_tname = null;
if (foreign_constraint) {
ref_tname =
resolveTableName(constraint.reference_table_name, database);
if (database.isInCaseInsensitiveMode()) {
ref_tname = database.tryResolveCase(ref_tname);
}
constraint.reference_table_name = ref_tname.toString();
}
checker.stripColumnList(table_name, constraint.column_list);
checker.stripColumnList(constraint.reference_table_name,
constraint.column_list2);
checker.checkExpression(constraint.check_expression);
checker.checkColumnList(constraint.column_list);
if (foreign_constraint && constraint.column_list2 != null) {
ColumnChecker referenced_checker =
ColumnChecker.standardColumnChecker(database, ref_tname);
referenced_checker.checkColumnList(constraint.column_list2);
}
CreateTable.addSchemaConstraint(database, tname, constraint);
}
}
// Alter the existing table to the new format...
if (table_altered) {
if (new_table.columnCount() == 0) {
throw new DatabaseException(
"Can not ALTER table to have 0 columns.");
}
database.updateTable(new_table);
}
else {
// If the table wasn't physically altered, check the constraints.
// Calling this method will also make the transaction check all
// deferred constraints during the next commit.
database.checkAllConstraints(tname);
}
// Return '0' if everything successful.
return FunctionTable.resultTable(context, 0);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy