All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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