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

org.hsqldb.TableWorks Maven / Gradle / Ivy

There is a newer version: 2.7.4
Show newest version
/* Copyright (c) 2001-2020, The HSQL Development Group
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the HSQL Development Group nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


package org.hsqldb;

import org.hsqldb.HsqlNameManager.HsqlName;
import org.hsqldb.error.Error;
import org.hsqldb.error.ErrorCode;
import org.hsqldb.index.Index;
import org.hsqldb.lib.ArrayUtil;
import org.hsqldb.lib.HsqlArrayList;
import org.hsqldb.lib.OrderedHashSet;
import org.hsqldb.navigator.RowIterator;
import org.hsqldb.persist.DataSpaceManager;
import org.hsqldb.persist.PersistentStore;
import org.hsqldb.rights.Grantee;
import org.hsqldb.types.Type;
import org.hsqldb.types.Types;

/**
 * The methods in this class perform alterations to the structure of an
 * existing table which may result in a new Table object
 *
 * @author Fred Toussi (fredt@users dot sourceforge.net)
 * @version 2.5.1
 * @since 1.7.0
 */
public class TableWorks {

    OrderedHashSet   emptySet = new OrderedHashSet();
    private Database database;
    private Table    table;
    private Session  session;

    public TableWorks(Session session, Table table) {

        this.database = table.database;
        this.table    = table;
        this.session  = session;

        if (table.isView()) {
            throw Error.error(ErrorCode.X_42524);
        }
    }

    public Table getTable() {
        return table;
    }

    void checkCreateForeignKey(Table fkTable, Constraint c) {

        int[] cols = c.getRefColumns();

        for (int i = 0; i < cols.length; i++) {
            ColumnSchema column = fkTable.getColumn(cols[i]);

            if (column.isSystemPeriod()) {
                throw Error.error(ErrorCode.X_42517);
            }
        }

        boolean check = c.hasTriggeredAction();

        if (check) {
            for (int i = 0; i < c.core.refCols.length; i++) {
                ColumnSchema col = fkTable.getColumn(c.core.refCols[i]);

                if (col.isGenerated()) {
                    throw Error.error(ErrorCode.X_42524, col.getNameString());
                }
            }
        }

        if (c.core.mainName == fkTable.getName()) {
            if (ArrayUtil.haveCommonElement(c.core.refCols, c.core.mainCols)) {
                throw Error.error(ErrorCode.X_42527);
            }
        }

        // column defaults
        check =
            c.getUpdateAction() == SchemaObject.ReferentialAction.SET_DEFAULT
            || c.getDeleteAction()
               == SchemaObject.ReferentialAction.SET_DEFAULT;

        if (check) {
            for (int i = 0; i < c.core.refCols.length; i++) {
                ColumnSchema col     = fkTable.getColumn(c.core.refCols[i]);
                Expression   defExpr = col.getDefaultExpression();

                if (defExpr == null) {
                    String columnName = col.getName().statementName;

                    throw Error.error(ErrorCode.X_42521, columnName);
                }
            }
        }

        check = c.core.updateAction == SchemaObject.ReferentialAction.SET_NULL
                || c.core.deleteAction
                   == SchemaObject.ReferentialAction.SET_NULL;

        if (check && !session.isProcessingScript()) {
            for (int i = 0; i < c.core.refCols.length; i++) {
                ColumnSchema col = fkTable.getColumn(c.core.refCols[i]);

                if (!col.isNullable() || col.isPrimaryKey()) {
                    String columnName = col.getName().statementName;

                    throw Error.error(ErrorCode.X_42520, columnName);
                }
            }
        }

        database.schemaManager.checkSchemaObjectNotExists(c.getName());

        // duplicate name check for a new fkTable
        if (fkTable.getConstraint(c.getName().name) != null) {
            throw Error.error(ErrorCode.X_42504, c.getName().statementName);
        }

        // existing FK check
        if (fkTable.getFKConstraintForColumns(
                c.core.mainTable, c.core.mainCols, c.core.refCols) != null) {
            throw Error.error(ErrorCode.X_42528, c.getName().statementName);
        }

        if (c.core.mainTable.isTemp() != fkTable.isTemp()) {
            throw Error.error(ErrorCode.X_42524, c.getName().statementName);
        }

        Constraint unique =
            c.core.mainTable.getUniqueConstraintForColumns(c.core.mainCols);

        if (unique == null) {
            throw Error.error(ErrorCode.X_42529,
                              c.getMain().getName().statementName);
        }

        // check after UNIQUE check
        c.core.mainTable.checkReferentialColumnsMatch(c.core.mainCols,
                fkTable, c.core.refCols);
        ArrayUtil.reorderMaps(unique.getMainColumns(), c.getMainColumns(),
                              c.getRefColumns());

        boolean[] checkList =
            c.core.mainTable.getColumnCheckList(c.core.mainCols);
        Grantee grantee = session.getGrantee();

        grantee.checkReferences(c.core.mainTable, checkList);
    }

    /**
     * Creates a foreign key on an existing table. Foreign keys are enforced by
     * indexes on both the referencing (child) and referenced (main) tables.
     *
     * 

Since version 1.7.2, a unique constraint on the referenced columns * must exist. The non-unique index on the referencing table is now always * created whether or not a PK or unique constraint index on the columns * exist. Foreign keys on temp tables can reference other temp tables with * the same rules above. Foreign keys on permanent tables cannot reference * temp tables. Duplicate foreign keys are now disallowed. * * @param c the constraint object */ void addForeignKey(Constraint c) { checkModifyTable(false); checkCreateForeignKey(table, c); Constraint uniqueConstraint = c.core.mainTable.getUniqueConstraintForColumns(c.core.mainCols); Index mainIndex = uniqueConstraint.getMainIndex(); boolean isForward = false; if (c.core.mainTable.getSchemaName() == table.getSchemaName()) { int offset = database.schemaManager.getTableIndex(table); if (offset != -1 && offset < database.schemaManager.getTableIndex( c.core.mainTable)) { isForward = true; } } else { isForward = true; } HsqlName mainName = database.nameManager.newAutoName("REF", c.getName().name, table.getSchemaName(), table.getName(), SchemaObject.INDEX); HsqlName indexName = session.database.nameManager.newConstraintIndexName( table.getName(), c.getName(), session.database.sqlSysIndexNames); Index refIndex = table.createIndexStructure(indexName, c.core.refCols, null, null, false, false, true, isForward); c.core.uniqueName = uniqueConstraint.getName(); c.core.mainName = mainName; c.core.mainIndex = mainIndex; c.core.refTable = table; c.core.refName = c.getName(); c.core.refIndex = refIndex; c.isForward = isForward; if (!session.isProcessingScript()) { c.checkReferencedRows(session, table); } Table tn = table.moveDefinition(session, table.tableType, ColumnSchema.emptyArray, c, refIndex, new int[0], 0, emptySet, emptySet); if (!session.isProcessingScript()) { moveData(table, tn, new int[]{}, 0); } database.schemaManager.addSchemaObject(c); setNewTableInSchema(tn); Table mainTable = database.schemaManager.getUserTable( c.core.mainTable.getName().name, c.core.mainTable.getSchemaName().name); mainTable.addConstraint(new Constraint(mainName, c)); updateConstraints(tn, emptySet); database.schemaManager.recompileDependentObjects(tn); database.schemaManager.recompileDependentObjects(mainTable); table = tn; } /** * Checks if the attributes of the Column argument, c, are compatible with * the operation of adding such a Column to the Table argument, table. * * @param col the Column to add to the Table, t */ void checkAddColumn(ColumnSchema col) { checkModifyTable(true); if (table.isText() && !table.isEmpty(session)) { throw Error.error(ErrorCode.X_S0521); } if (table.findColumn(col.getName().name) != -1) { throw Error.error(ErrorCode.X_42504); } if (col.isPrimaryKey() && table.hasPrimaryKey()) { throw Error.error(ErrorCode.X_42530); } if (col.isIdentity() && table.hasIdentityColumn()) { throw Error.error(ErrorCode.X_42525); } if (!table.isEmpty(session) && !col.hasDefault() && (!col.isNullable() || col.isPrimaryKey()) && !col.isIdentity()) { throw Error.error(ErrorCode.X_42531); } } void addColumn(ColumnSchema column, int colIndex, HsqlArrayList constraints) { Index index = null; Constraint mainConstraint = null; boolean addFK = false; boolean addUnique = false; boolean addCheck = false; checkAddColumn(column); Constraint c = (Constraint) constraints.get(0); if (c.getConstraintType() == SchemaObject.ConstraintTypes.PRIMARY_KEY) { if (column.getDataType().isLobType()) { throw Error.error(ErrorCode.X_42534); } c.core.mainCols = new int[]{ colIndex }; database.schemaManager.checkSchemaObjectNotExists(c.getName()); if (table.hasPrimaryKey()) { throw Error.error(ErrorCode.X_42530); } addUnique = true; } else { c = null; } Table tn = table.moveDefinition(session, table.tableType, new ColumnSchema[]{ column }, c, null, new int[]{ colIndex }, 1, emptySet, emptySet); for (int i = 1; i < constraints.size(); i++) { c = (Constraint) constraints.get(i); switch (c.getConstraintType()) { case SchemaObject.ConstraintTypes.UNIQUE : { if (addUnique) { throw Error.error(ErrorCode.X_42522); } if (column.getDataType().isLobType()) { throw Error.error(ErrorCode.X_42534); } addUnique = true; c.core.mainCols = new int[]{ colIndex }; database.schemaManager.checkSchemaObjectNotExists( c.getName()); HsqlName indexName = database.nameManager.newAutoName("IDX", c.getName().name, table.getSchemaName(), table.getName(), SchemaObject.INDEX); // create an autonamed index index = tn.createAndAddIndexStructure(session, indexName, c.getMainColumns(), null, null, true, true, false); c.core.mainTable = tn; c.core.mainIndex = index; tn.addConstraint(c); break; } case SchemaObject.ConstraintTypes.FOREIGN_KEY : { if (addFK) { throw Error.error(ErrorCode.X_42528); } addFK = true; c.core.refCols = new int[]{ colIndex }; c.core.mainTable = database.schemaManager.getUserTable( c.getMainTableName()); c.core.refTable = tn; c.core.refName = c.getName(); boolean isSelf = table == c.core.mainTable; if (isSelf) { c.core.mainTable = tn; } c.setColumnsIndexes(tn); checkCreateForeignKey(tn, c); Constraint uniqueConstraint = c.core.mainTable.getUniqueConstraintForColumns( c.core.mainCols); boolean isForward = c.core.mainTable.getSchemaName() != table.getSchemaName(); int offset = database.schemaManager.getTableIndex(table); if (!isSelf && offset < database.schemaManager.getTableIndex( c.core.mainTable)) { isForward = true; } HsqlName indexName = database.nameManager.newAutoName("IDX", c.getName().name, table.getSchemaName(), table.getName(), SchemaObject.INDEX); index = tn.createAndAddIndexStructure(session, indexName, c.getRefColumns(), null, null, false, true, isForward); c.core.uniqueName = uniqueConstraint.getName(); c.core.mainName = database.nameManager.newAutoName("REF", c.core.refName.name, table.getSchemaName(), table.getName(), SchemaObject.INDEX); c.core.mainIndex = uniqueConstraint.getMainIndex(); c.core.refIndex = index; c.isForward = isForward; tn.addConstraint(c); mainConstraint = new Constraint(c.core.mainName, c); break; } case SchemaObject.ConstraintTypes.CHECK : if (addCheck) { throw Error.error(ErrorCode.X_42528); } addCheck = true; c.prepareCheckConstraint(session, tn); tn.addConstraint(c); if (c.isNotNull()) { column.setNullable(false); tn.setColumnTypeVars(colIndex); if (!table.isEmpty(session) && !column.hasDefault()) { throw Error.error(ErrorCode.X_42531); } } break; } } column.compile(session, tn); moveData(table, tn, new int[]{ colIndex }, 1); if (mainConstraint != null) { mainConstraint.getMain().addConstraint(mainConstraint); } registerConstraintNames(constraints); setNewTableInSchema(tn); updateConstraints(tn, emptySet); database.schemaManager.addSchemaObject(column); database.schemaManager.recompileDependentObjects(tn); tn.compile(session, null); TriggerDef[] triggers = table.getTriggers(); for (int i = 0; i < triggers.length; i++) { if (triggers[i] instanceof TriggerDefSQL) { triggers[i].compile(session, null); } } table = tn; database.granteeManager.updateAddColumn(table.getName(), column.getName()); } void updateConstraints(OrderedHashSet tableSet, OrderedHashSet dropConstraints) { for (int i = 0; i < tableSet.size(); i++) { Table t = (Table) tableSet.get(i); updateConstraints(t, dropConstraints); } } void updateConstraints(Table t, OrderedHashSet dropConstraints) { for (int i = t.constraintList.length - 1; i >= 0; i--) { Constraint c = t.constraintList[i]; if (dropConstraints.contains(c.getName())) { t.removeConstraint(i); continue; } if (c.getConstraintType() == SchemaObject.ConstraintTypes.FOREIGN_KEY) { Table refT = database.schemaManager.getUserTable( c.core.refTable.getName()); c.core.refTable = refT; Table mainT = database.schemaManager.getUserTable( c.core.mainTable.getName()); Constraint mainC = mainT.getConstraint(c.getMainName().name); mainC.core = c.core; } else if (c.getConstraintType() == SchemaObject.ConstraintTypes.MAIN) { Table mainT = database.schemaManager.getUserTable( c.core.mainTable.getName()); c.core.mainTable = mainT; Table refT = database.schemaManager.getUserTable( c.core.refTable.getName()); Constraint refC = refT.getConstraint(c.getRefName().name); refC.core = c.core; } } } OrderedHashSet dropConstraintsAndIndexes(OrderedHashSet tableSet, OrderedHashSet dropConstraintSet, OrderedHashSet dropIndexSet) { OrderedHashSet newSet = new OrderedHashSet(); for (int i = 0; i < tableSet.size(); i++) { Table t = (Table) tableSet.get(i); TableWorks tw = new TableWorks(session, t); tw.dropConstraintsAndIndexes(dropConstraintSet, dropIndexSet); newSet.add(tw.getTable()); } return newSet; } /** * Drops a set of fk constraints, their indexes and other indexes in table. * Uses sets of names which may contain names that are unrelated to * this table. */ void dropConstraintsAndIndexes(OrderedHashSet dropConstraintSet, OrderedHashSet dropIndexSet) { Table tn = table.moveDefinition(session, table.tableType, ColumnSchema.emptyArray, null, null, new int[0], 0, dropConstraintSet, dropIndexSet); if (tn.indexList.length == table.indexList.length) { database.persistentStoreCollection.removeStore(tn); return; } moveData(table, tn, new int[]{}, 0); table = tn; } void alterIndex(Index index, int[] columns) { PersistentStore store = database.persistentStoreCollection.getStore(table); int position = index.getPosition(); boolean[] modeFlags = new boolean[columns.length]; Type[] colTypes = new Type[columns.length]; ArrayUtil.projectRow(table.getColumnTypes(), columns, colTypes); Index newIndex = database.logger.newIndex(index.getName(), index.getPersistenceId(), table, columns, modeFlags, modeFlags, colTypes, false, false, false, false); newIndex.setPosition(position); table.getIndexList()[position] = newIndex; table.setBestRowIdentifiers(); Index[] indexes = store.getAccessorKeys(); indexes[position] = newIndex; store.reindex(session, newIndex, null); database.schemaManager.recompileDependentObjects(table); } /** * Because of the way indexes and column data are held in memory and on * disk, it is necessary to recreate the table when an index is added to a * non-empty cached table. * *

With empty tables, Index objects are simply added * *

With MEOMRY and TEXT tables, a new index is built up and nodes for * earch row are interlinked (fredt@users) * * @param col int[] * @param name HsqlName * @param unique boolean * @return new index */ Index addIndex(int[] col, HsqlName name, boolean unique) { Index newIndex; checkModifyTable(false); if (session.isProcessingScript() || table.isEmpty(session) || table.isIndexingMutable()) { newIndex = table.createIndex(session, name, col, null, null, unique, false, false); } else { newIndex = table.createIndexStructure(name, col, null, null, false, unique, false, false); Table tn = table.moveDefinition(session, table.tableType, ColumnSchema.emptyArray, null, newIndex, new int[0], 0, emptySet, emptySet); moveData(table, tn, new int[]{}, 0); table = tn; setNewTableInSchema(table); updateConstraints(table, emptySet); } database.schemaManager.addSchemaObject(newIndex); database.schemaManager.recompileDependentObjects(table); return newIndex; } void addPrimaryKey(Constraint constraint) { checkModifyTable(true); if (table.hasPrimaryKey()) { throw Error.error(ErrorCode.X_42532); } database.schemaManager.checkSchemaObjectNotExists( constraint.getName()); Table tn = table.moveDefinition(session, table.tableType, ColumnSchema.emptyArray, constraint, null, new int[0], 0, emptySet, emptySet); moveData(table, tn, new int[]{}, 0); table = tn; database.schemaManager.addSchemaObject(constraint); setNewTableInSchema(table); updateConstraints(table, emptySet); database.schemaManager.recompileDependentObjects(table); } /** * A unique constraint relies on a unique index on the table. It can cover * a single column or multiple columns. * *

All constraint names are unique * within the database. Duplicate constraints (more than one unique * constraint on the same set of columns) are not allowed. (fredt@users) * * @param cols int[] * @param name HsqlName */ void addUniqueConstraint(int[] cols, HsqlName name) { for (int i = 0; i < cols.length; i++) { ColumnSchema column = table.getColumn(cols[i]); if (column.isSystemPeriod()) { throw Error.error(ErrorCode.X_42517); } } checkModifyTable(false); database.schemaManager.checkSchemaObjectNotExists(name); if (table.getUniqueConstraintForColumns(cols) != null) { throw Error.error(ErrorCode.X_42522); } // create an autonamed index HsqlName indexname = database.nameManager.newAutoName("IDX", name.name, table.getSchemaName(), table.getName(), SchemaObject.INDEX); Index index = table.createIndexStructure(indexname, cols, null, null, false, true, true, false); Constraint constraint = new Constraint(name, table, index, SchemaObject.ConstraintTypes.UNIQUE); Table tn = table.moveDefinition(session, table.tableType, ColumnSchema.emptyArray, constraint, index, new int[0], 0, emptySet, emptySet); moveData(table, tn, new int[]{}, 0); table = tn; database.schemaManager.addSchemaObject(constraint); setNewTableInSchema(table); updateConstraints(table, emptySet); database.schemaManager.recompileDependentObjects(table); } void addUniqueConstraint(Constraint constraint) { int[] cols = constraint.getMainColumns(); for (int i = 0; i < cols.length; i++) { ColumnSchema column = table.getColumn(cols[i]); if (column.isSystemPeriod()) { throw Error.error(ErrorCode.X_42517); } } checkModifyTable(false); database.schemaManager.checkSchemaObjectNotExists( constraint.getName()); if (table.getUniqueConstraintForColumns(constraint.getMainColumns()) != null) { throw Error.error(ErrorCode.X_42522); } Table tn = table.moveDefinition(session, table.tableType, ColumnSchema.emptyArray, constraint, constraint.getMainIndex(), new int[0], 0, emptySet, emptySet); moveData(table, tn, new int[]{}, 0); table = tn; database.schemaManager.addSchemaObject(constraint); setNewTableInSchema(table); updateConstraints(table, emptySet); database.schemaManager.recompileDependentObjects(table); } void addCheckConstraint(Constraint c) { checkModifyTable(false); database.schemaManager.checkSchemaObjectNotExists(c.getName()); c.prepareCheckConstraint(session, table); c.checkCheckConstraint(session, table); table.addConstraint(c); if (c.isNotNull()) { ColumnSchema column = table.getColumn(c.notNullColumnIndex); column.setNullable(false); table.setColumnTypeVars(c.notNullColumnIndex); } database.schemaManager.addSchemaObject(c); } /** * Because of the way indexes and column data are held in memory and on * disk, it is necessary to recreate the table when an index is added to or * removed from a non-empty table. * *

Originally, this method would break existing foreign keys as the * table order in the DB was changed. The new table is now linked in place * of the old table (fredt@users) * * @param indexName String */ void dropIndex(String indexName) { Index index; checkModifyTable(false); index = table.getUserIndex(indexName); if (table.isIndexingMutable()) { table.dropIndex(session, index.getPosition()); } else { OrderedHashSet indexSet = new OrderedHashSet(); indexSet.add(table.getIndex(indexName).getName()); Table tn = table.moveDefinition(session, table.tableType, ColumnSchema.emptyArray, null, null, new int[0], 0, emptySet, indexSet); moveData(table, tn, new int[]{}, 0); setNewTableInSchema(tn); updateConstraints(tn, emptySet); table = tn; } if (!index.isConstraint()) { database.schemaManager.removeSchemaObject(index.getName()); } database.schemaManager.recompileDependentObjects(table); } void dropColumn(int colIndex, boolean cascade) { ColumnSchema column = table.getColumn(colIndex); if (column.isSystemPeriod()) { throw Error.error(ErrorCode.X_42517); } OrderedHashSet constraintNameSet = new OrderedHashSet(); OrderedHashSet dependentConstraints = table.getDependentConstraints(colIndex); OrderedHashSet cascadingConstraints = table.getContainingConstraints(colIndex); OrderedHashSet indexNameSet = table.getContainingIndexNames(colIndex); HsqlName columnName = column.getName(); OrderedHashSet referencingObjects = database.schemaManager.getReferencesTo(table.getName(), columnName); checkModifyTable(true); TriggerDef[] triggers = table.getTriggers(); for (int i = 0; i < triggers.length; i++) { TriggerDef trig = triggers[i]; if (trig instanceof TriggerDefSQL) { if (trig.hasOldTable() || trig.hasNewTable() || trig.hasOldRow() || trig.hasNewRow()) { throw Error.error( ErrorCode.X_42502, trig.getName().getSchemaQualifiedStatementName()); } } } if (!cascade) { if (!cascadingConstraints.isEmpty()) { Constraint c = (Constraint) cascadingConstraints.get(0); HsqlName name = c.getName(); if (c.getConstraintType() == SchemaObject.ConstraintTypes.MAIN) { name = c.getRefName(); } throw Error.error(ErrorCode.X_42536, name.getSchemaQualifiedStatementName()); } if (!referencingObjects.isEmpty()) { mainLoop: for (int i = 0; i < referencingObjects.size(); i++) { HsqlName name = (HsqlName) referencingObjects.get(i); if (name == columnName) { continue; } for (int j = 0; j < dependentConstraints.size(); j++) { Constraint c = (Constraint) dependentConstraints.get(j); if (c.getName() == name) { continue mainLoop; } } throw Error.error(ErrorCode.X_42536, name.getSchemaQualifiedStatementName()); } } } dependentConstraints.addAll(cascadingConstraints); cascadingConstraints.clear(); OrderedHashSet tableSet = new OrderedHashSet(); for (int i = 0; i < dependentConstraints.size(); i++) { Constraint c = (Constraint) dependentConstraints.get(i); if (c.getConstraintType() == SchemaObject.ConstraintTypes.FOREIGN_KEY) { tableSet.add(c.getMain()); constraintNameSet.add(c.getMainName()); constraintNameSet.add(c.getRefName()); indexNameSet.add(c.getRefIndex().getName()); } if (c.getConstraintType() == SchemaObject.ConstraintTypes.MAIN) { tableSet.add(c.getRef()); constraintNameSet.add(c.getMainName()); constraintNameSet.add(c.getRefName()); indexNameSet.add(c.getRefIndex().getName()); } constraintNameSet.add(c.getName()); } tableSet = dropConstraintsAndIndexes(tableSet, constraintNameSet, indexNameSet); Table tn = table.moveDefinition(session, table.tableType, ColumnSchema.emptyArray, null, null, new int[]{ colIndex }, -1, constraintNameSet, indexNameSet); moveData(table, tn, new int[]{ colIndex }, -1); database.schemaManager.removeSchemaObjects(referencingObjects); database.schemaManager.removeSchemaObjects(constraintNameSet); database.schemaManager.removeSchemaObjects(indexNameSet); database.schemaManager.removeSchemaObject(columnName); setNewTableInSchema(tn); setNewTablesInSchema(tableSet); updateConstraints(tn, emptySet); updateConstraints(tableSet, constraintNameSet); database.schemaManager.recompileDependentObjects(tableSet); database.schemaManager.recompileDependentObjects(tn); tn.compile(session, null); table = tn; } void registerConstraintNames(HsqlArrayList constraints) { for (int i = 0; i < constraints.size(); i++) { Constraint c = (Constraint) constraints.get(i); switch (c.getConstraintType()) { case SchemaObject.ConstraintTypes.PRIMARY_KEY : case SchemaObject.ConstraintTypes.UNIQUE : case SchemaObject.ConstraintTypes.CHECK : database.schemaManager.addSchemaObject(c); } } } void dropConstraint(String name, boolean cascade) { Constraint constraint = table.getConstraint(name); if (constraint == null) { throw Error.error(ErrorCode.X_42501, name); } switch (constraint.getConstraintType()) { case SchemaObject.ConstraintTypes.MAIN : throw Error.error(ErrorCode.X_28502); case SchemaObject.ConstraintTypes.PRIMARY_KEY : case SchemaObject.ConstraintTypes.UNIQUE : { checkModifyTable(false); OrderedHashSet dependentConstraints = table.getDependentConstraints(constraint); // throw if unique constraint is referenced by foreign key if (!cascade && !dependentConstraints.isEmpty()) { Constraint c = (Constraint) dependentConstraints.get(0); HsqlName constraintName = c.getName(); if (c.getConstraintType() == SchemaObject.ConstraintTypes.MAIN) { constraintName = c.getRefName(); } throw Error.error( ErrorCode.X_42533, constraintName.getSchemaQualifiedStatementName()); } OrderedHashSet tableSet = new OrderedHashSet(); OrderedHashSet constraintNameSet = new OrderedHashSet(); OrderedHashSet indexNameSet = new OrderedHashSet(); for (int i = 0; i < dependentConstraints.size(); i++) { Constraint c = (Constraint) dependentConstraints.get(i); Table t = c.getMain(); if (t != table) { tableSet.add(t); } t = c.getRef(); if (t != table) { tableSet.add(t); } constraintNameSet.add(c.getMainName()); constraintNameSet.add(c.getRefName()); indexNameSet.add(c.getRefIndex().getName()); } constraintNameSet.add(constraint.getName()); if (constraint.getConstraintType() == SchemaObject.ConstraintTypes.UNIQUE) { indexNameSet.add(constraint.getMainIndex().getName()); } Table tn = table.moveDefinition(session, table.tableType, ColumnSchema.emptyArray, null, null, new int[0], 0, constraintNameSet, indexNameSet); moveData(table, tn, new int[]{}, 0); tableSet = dropConstraintsAndIndexes(tableSet, constraintNameSet, indexNameSet); if (constraint.getConstraintType() == SchemaObject.ConstraintTypes.PRIMARY_KEY) { int[] cols = constraint.getMainColumns(); for (int i = 0; i < cols.length; i++) { // todo - check if table arrays reflect the not-null correctly tn.getColumn(cols[i]).setPrimaryKey(false); tn.setColumnTypeVars(cols[i]); } } // database.schemaManager.removeSchemaObjects(constraintNameSet); setNewTableInSchema(tn); setNewTablesInSchema(tableSet); updateConstraints(tn, emptySet); updateConstraints(tableSet, constraintNameSet); database.schemaManager.recompileDependentObjects(tableSet); database.schemaManager.recompileDependentObjects(tn); table = tn; // handle cascadingConstraints and cascadingTables break; } case SchemaObject.ConstraintTypes.FOREIGN_KEY : { checkModifyTable(false); OrderedHashSet constraints = new OrderedHashSet(); Table mainTable = constraint.getMain(); HsqlName mainName = constraint.getMainName(); constraints.add(mainName); constraints.add(constraint.getRefName()); OrderedHashSet indexes = new OrderedHashSet(); indexes.add(constraint.getRefIndex().getName()); Table tn = table.moveDefinition(session, table.tableType, ColumnSchema.emptyArray, null, null, new int[0], 0, constraints, indexes); moveData(table, tn, new int[]{}, 0); // database.schemaManager.removeSchemaObject( constraint.getName()); setNewTableInSchema(tn); // if constraint references same table, nothing changes mainTable.removeConstraint(mainName.name); updateConstraints(tn, emptySet); database.schemaManager.recompileDependentObjects(table); table = tn; break; } case SchemaObject.ConstraintTypes.CHECK : database.schemaManager.removeSchemaObject( constraint.getName()); if (constraint.isNotNull()) { ColumnSchema column = table.getColumn(constraint.notNullColumnIndex); column.setNullable(false); table.setColumnTypeVars(constraint.notNullColumnIndex); } break; } } /** * Allows changing the type only. * * @param oldCol Column * @param newCol Column */ void retypeColumn(ColumnSchema oldCol, ColumnSchema newCol) { Type oldType = oldCol.getDataType(); Type newType = newCol.getDataType(); if (oldType.equals(newType) && oldCol.getIdentitySequence() == newCol.getIdentitySequence()) { return; } if (oldCol.isGenerated() || oldCol.isSystemPeriod()) { throw Error.error(ErrorCode.X_42517); } checkModifyTable(true); if (!table.isEmpty(session) && oldType.typeCode != newType.typeCode) { boolean allowed = newCol.getDataType().canConvertFrom(oldCol.getDataType()); switch (oldType.typeCode) { case Types.OTHER : case Types.JAVA_OBJECT : allowed = false; break; } if (!allowed) { throw Error.error(ErrorCode.X_42561); } } int colIndex = table.getColumnIndex(oldCol.getName().name); // 0 if only metadata change is required ; 1 only range check is required ; -1 data conversion is required int checkData = newType.canMoveFrom(oldType); if (newCol.isIdentity() && table.hasIdentityColumn() && table.identityColumn != colIndex) { throw Error.error(ErrorCode.X_42525); } if (checkData == 0) { if (newCol.isIdentity()) { if (!(oldCol.isIdentity() || !oldCol.isNullable() || oldCol.isPrimaryKey())) { checkData = 1; } } if (newType.isDomainType() && newType.userTypeModifier.getConstraints().length > 0) { checkData = 1; } } if (checkData == 1) { checkConvertColDataType(oldCol, newCol); checkData = 0; } if (checkData == 0) { // size of some types may be increased // default expressions can change // identity can be added or removed oldCol.setType(newCol); oldCol.setDefaultExpression(newCol.getDefaultExpression()); oldCol.setIdentity(newCol.getIdentitySequence()); table.setColumnTypeVars(colIndex); table.resetDefaultFlags(); return; } database.schemaManager.checkColumnIsReferenced(table.getName(), table.getColumn(colIndex).getName()); table.checkColumnInCheckConstraint(colIndex); table.checkColumnInFKConstraint(colIndex); checkConvertColDataType(oldCol, newCol); retypeColumn(newCol, colIndex); } /** * * @param oldCol Column * @param newCol Column */ void checkConvertColDataType(ColumnSchema oldCol, ColumnSchema newCol) { int colIndex = table.getColumnIndex(oldCol.getName().name); Type oldType = oldCol.getDataType(); Type newType = newCol.getDataType(); RowIterator it = table.rowIterator(session); while (it.next()) { Row row = it.getCurrentRow(); Object o = row.getField(colIndex); if (!newCol.isNullable() && o == null) { throw Error.error(ErrorCode.X_23502); } newType.convertToType(session, o, oldType); if (newType.isDomainType()) { Constraint[] checks = newType.userTypeModifier.getConstraints(); for (int i = 0; i < checks.length; i++) { checks[i].checkCheckConstraint(session, table, oldCol, o); checkAddDomainConstraint(newType, checks[i]); } } } } /** * * @param domain domain * @param check added constraint */ void checkAddDomainConstraint(Type domain, Constraint check) { Type[] dataTypes = table.getColumnTypes(); RowIterator it = table.rowIterator(session); while (it.next()) { Row row = it.getCurrentRow(); for (int i = 0; i < table.getColumnCount(); i++) { if (dataTypes[i] == domain) { ColumnSchema column = table.getColumn(i); check.checkCheckConstraint(session, table, column, row.getField(i)); } } } } /** * * @param column Column * @param colIndex int */ private void retypeColumn(ColumnSchema column, int colIndex) { Table tn = table.moveDefinition(session, table.tableType, new ColumnSchema[]{ column }, null, null, new int[]{ colIndex }, 0, emptySet, emptySet); moveData(table, tn, new int[]{ colIndex }, 0); setNewTableInSchema(tn); updateConstraints(tn, emptySet); database.schemaManager.recompileDependentObjects(table); table = tn; } /** * performs the work for changing the nullability of a column * * @param column Column * @param nullable boolean */ void setColNullability(ColumnSchema column, boolean nullable) { Constraint c = null; int colIndex = table.getColumnIndex(column.getName().name); if (column.isGenerated() || column.isSystemPeriod()) { throw Error.error(ErrorCode.X_42517); } if (column.isNullable() == nullable) { return; } if (nullable) { if (column.isPrimaryKey()) { throw Error.error(ErrorCode.X_42526); } table.checkColumnInFKConstraint( colIndex, SchemaObject.ReferentialAction.SET_NULL); removeColumnNotNullConstraints(colIndex); } else { HsqlName constName = database.nameManager.newAutoName("CT", table.getSchemaName(), table.getName(), SchemaObject.CONSTRAINT); c = new Constraint(constName, null, SchemaObject.ConstraintTypes.CHECK); c.check = new ExpressionLogical(column); c.prepareCheckConstraint(session, table); c.checkCheckConstraint(session, table); column.setNullable(false); table.addConstraint(c); table.setColumnTypeVars(colIndex); database.schemaManager.addSchemaObject(c); } } /** * performs the work for changing the default value of a column * * @param colIndex int * @param def Expression */ void setColDefaultExpression(int colIndex, Expression def) { if (def == null) { table.checkColumnInFKConstraint( colIndex, SchemaObject.ReferentialAction.SET_DEFAULT); } ColumnSchema column = table.getColumn(colIndex); if (column.isGenerated() || column.isSystemPeriod()) { throw Error.error(ErrorCode.X_42517); } column.setDefaultExpression(def); table.setColumnTypeVars(colIndex); } /** * Changes the type of a table * * @param session Session * @param newType int * @return boolean */ public boolean setTableType(Session session, int newType) { int currentType = table.getTableType(); if (currentType == newType) { return false; } switch (newType) { case TableBase.CACHED_TABLE : break; case TableBase.MEMORY_TABLE : break; default : return false; } Table tn; try { tn = table.moveDefinition(session, newType, ColumnSchema.emptyArray, null, null, new int[0], 0, emptySet, emptySet); moveData(table, tn, new int[]{}, 0); } catch (HsqlException e) { return false; } setNewTableInSchema(tn); updateConstraints(tn, emptySet); table = tn; database.schemaManager.recompileDependentObjects(table); return true; } void addSystemPeriod(PeriodDefinition period) { if (table.systemPeriod != null) { throw Error.error(ErrorCode.X_0A501); } int columnCount = table.getColumnCount(); int[] colIndex = new int[] { columnCount, columnCount + 1 }; ColumnSchema[] columns = new ColumnSchema[] { period.startColumn, period.endColumn }; checkAddColumn(period.startColumn); checkAddColumn(period.endColumn); Table tn = table.moveDefinition(session, table.tableType, columns, null, null, colIndex, 2, emptySet, emptySet); tn.systemPeriod = period; // move data moveData(table, tn, colIndex, 2); setNewTableInSchema(tn); table = tn; database.granteeManager.updateAddColumn(table.getName(), period.startColumn.getName()); database.granteeManager.updateAddColumn(table.getName(), period.endColumn.getName()); } void dropSystemPeriod(boolean cascade) { if (table.systemPeriod == null) { throw Error.error(ErrorCode.X_0A501); } // references in conditions and to the columns OrderedHashSet referencingObjects = database.schemaManager.getReferencesTo(table.getName(), table.systemPeriod.startColumn.getName()); OrderedHashSet endReferences = database.schemaManager.getReferencesTo(table.getName(), table.systemPeriod.startColumn.getName()); referencingObjects.addAll(endReferences); if (cascade) { if (!referencingObjects.isEmpty()) { HsqlName objectName = (HsqlName) referencingObjects.get(0); throw Error.error( ErrorCode.X_42502, objectName.getSchemaQualifiedStatementName()); } } else { if (!referencingObjects.isEmpty()) { HsqlName objectName = (HsqlName) referencingObjects.get(0); throw Error.error( ErrorCode.X_42502, objectName.getSchemaQualifiedStatementName()); } } int[] colIndex = new int[] { table.systemPeriodStartColumn, table.systemPeriodEndColumn }; Table tn = table.moveDefinition(session, table.tableType, ColumnSchema.emptyArray, null, null, colIndex, -2, emptySet, emptySet); tn.systemPeriod = null; moveData(table, tn, colIndex, -2); updateConstraints(tn, emptySet); database.schemaManager.recompileDependentObjects(tn); tn.compile(session, null); setNewTableInSchema(tn); table = tn; } void dropSystemVersioning(boolean cascade) { // references in range variable conditions OrderedHashSet referencingObjects = database.schemaManager.getReferencesTo(table.getName(), table.systemPeriod.getName()); if (cascade) { if (!referencingObjects.isEmpty()) { HsqlName objectName = (HsqlName) referencingObjects.get(0); throw Error.error( ErrorCode.X_42502, objectName.getSchemaQualifiedStatementName()); } } else { if (!referencingObjects.isEmpty()) { HsqlName objectName = (HsqlName) referencingObjects.get(0); throw Error.error( ErrorCode.X_42502, objectName.getSchemaQualifiedStatementName()); } } } void setNewTablesInSchema(OrderedHashSet tableSet) { for (int i = 0; i < tableSet.size(); i++) { Table t = (Table) tableSet.get(i); setNewTableInSchema(t); } } void setNewTableInSchema(Table newTable) { int i = database.schemaManager.getTableIndex(newTable); if (i != -1) { database.schemaManager.setTable(i, newTable); } } void removeColumnNotNullConstraints(int colIndex) { for (int i = table.constraintList.length - 1; i >= 0; i--) { Constraint c = table.constraintList[i]; if (c.isNotNull()) { if (c.notNullColumnIndex == colIndex) { database.schemaManager.removeSchemaObject(c.getName()); } } } ColumnSchema column = table.getColumn(colIndex); column.setNullable(true); table.setColumnTypeVars(colIndex); } private void checkModifyTable(boolean withContents) { if (session.getUser().isSystem()) { return; } if (session.isProcessingScript()) { return; } if (database.isFilesReadOnly() || table.isReadOnly()) { throw Error.error(ErrorCode.DATA_IS_READONLY); } if (table.isText() && table.isConnected()) { throw Error.error(ErrorCode.X_S0521); } } void moveData(Table oldTable, Table newTable, int[] colIndex, int adjust) { if (oldTable.isTemp) { Session[] sessions = database.sessionManager.getAllSessions(); for (int i = 0; i < sessions.length; i++) { sessions[i].sessionData.persistentStoreCollection.moveData( oldTable, newTable, colIndex, adjust); } } else { PersistentStore oldStore = database.persistentStoreCollection.getStore(oldTable); boolean newSpaceID = false; if (newTable.isCached()) { newSpaceID = oldTable.getSpaceID() != DataSpaceManager.tableIdDefault; if (newSpaceID) { int tableSpaceID = database.logger.getCache().spaceManager .getNewTableSpaceID(); newTable.setSpaceID(tableSpaceID); } } PersistentStore newStore = database.persistentStoreCollection.getStore(newTable); try { newStore.moveData(session, oldStore, colIndex, adjust); } catch (HsqlException e) { database.persistentStoreCollection.removeStore(newTable); throw e; } database.persistentStoreCollection.removeStore(oldTable); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy