Package org.hsqldb

Source Code of org.hsqldb.Table

/*
* For work developed by the HSQL Development Group:
*
* Copyright (c) 2001-2010, 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.
*
*
*
* For work originally developed by the Hypersonic SQL Group:
*
* Copyright (c) 1995-2000, The Hypersonic SQL 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 Hypersonic SQL 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 THE HYPERSONIC SQL GROUP,
* 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.
*
* This software consists of voluntary contributions made by many individuals
* on behalf of the Hypersonic SQL Group.
*/


package org.hsqldb;

import org.hsqldb.HsqlNameManager.HsqlName;
import org.hsqldb.RangeVariable.RangeIteratorBase;
import org.hsqldb.error.Error;
import org.hsqldb.error.ErrorCode;
import org.hsqldb.index.Index;
import org.hsqldb.lib.ArrayUtil;
import org.hsqldb.lib.HashMappedList;
import org.hsqldb.lib.HsqlArrayList;
import org.hsqldb.lib.OrderedHashSet;
import org.hsqldb.lib.OrderedIntHashSet;
import org.hsqldb.lib.Set;
import org.hsqldb.lib.StringUtil;
import org.hsqldb.navigator.RowIterator;
import org.hsqldb.navigator.RowSetNavigator;
import org.hsqldb.persist.CachedObject;
import org.hsqldb.persist.PersistentStore;
import org.hsqldb.result.Result;
import org.hsqldb.rights.Grantee;
import org.hsqldb.store.ValuePool;
import org.hsqldb.types.Type;

/**
* Holds the data structures and methods for creation of a database table.
*
*
* Extensively rewritten and extended in successive versions of HSQLDB.
*
* @author Thomas Mueller (Hypersonic SQL Group)
* @author Fred Toussi (fredt@users dot sourceforge.net)
* @version 1.9.0
* @since Hypersonic SQL
*/
public class Table extends TableBase implements SchemaObject {

    public static final Table[] emptyArray = new Table[]{};

    // main properties
    private HsqlName tableName;
    protected long   changeTimestamp;

    //
    public HashMappedList columnList;          // columns in table
    int                   identityColumn;      // -1 means no such column
    NumberSequence        identitySequence;    // next value of identity column

// -----------------------------------------------------------------------
    Constraint[]    constraintList;            // constrainst for the table
    Constraint[]    fkConstraints;             //
    Constraint[]    fkMainConstraints;
    Constraint[]    checkConstraints;
    TriggerDef[]    triggerList;
    TriggerDef[][]  triggerLists;              // array of trigger lists
    Expression[]    colDefaults;               // expressions of DEFAULT values
    private boolean hasDefaultValues;          // shortcut for above
    boolean[]       colGenerated;              // generated columns
    private boolean hasGeneratedValues;        // shortcut for above
    boolean[]       colRefFK;                  // foreign key columns
    boolean[]       colMainFK;                 // columns referenced by foreign key
    boolean         hasReferentialAction;      // has set null, set default or cascade
    private boolean hasDomainColumns;          // shortcut
    private boolean hasNotNullColumns;         // shortcut
    protected int[] defaultColumnMap;          // holding 0,1,2,3,...
    RangeVariable[] defaultRanges;

    //
    public Table(Database database, HsqlName name, int type) {

        this.database = database;
        tableName     = name;
        persistenceId = database.persistentStoreCollection.getNextId();

        switch (type) {

            case SYSTEM_SUBQUERY :
                persistenceScope = SCOPE_STATEMENT;
                isSessionBased   = true;
                break;

            case SYSTEM_TABLE :
                persistenceScope = SCOPE_FULL;
                isSchemaBased    = true;
                break;

            case CACHED_TABLE :
                if (DatabaseURL.isFileBasedDatabaseType(database.getType())) {
                    persistenceScope = SCOPE_FULL;
                    isSchemaBased    = true;
                    isCached         = true;
                    isLogged         = !database.isFilesReadOnly();

                    break;
                }

                type = MEMORY_TABLE;

            // fall through
            case MEMORY_TABLE :
                persistenceScope = SCOPE_FULL;
                isSchemaBased    = true;
                isLogged         = !database.isFilesReadOnly();
                break;

            case TEMP_TABLE :
                persistenceScope = SCOPE_TRANSACTION;
                isTemp           = true;
                isSchemaBased    = true;
                isSessionBased   = true;
                break;

            case TEMP_TEXT_TABLE :
                persistenceScope = SCOPE_SESSION;

                if (!DatabaseURL.isFileBasedDatabaseType(database.getType())) {
                    throw Error.error(ErrorCode.DATABASE_IS_MEMORY_ONLY);
                }

                isSchemaBased  = true;
                isSessionBased = true;
                isTemp         = true;
                isText         = true;
                isReadOnly     = true;
                break;

            case TEXT_TABLE :
                persistenceScope = SCOPE_FULL;

                if (!DatabaseURL.isFileBasedDatabaseType(database.getType())) {
                    throw Error.error(ErrorCode.DATABASE_IS_MEMORY_ONLY);
                }

                isSchemaBased = true;
                isText        = true;
                break;

            case VIEW_TABLE :
                persistenceScope = SCOPE_STATEMENT;
                isSchemaBased    = true;
                isSessionBased   = true;
                isView           = true;
                break;

            case RESULT_TABLE :
                persistenceScope = SCOPE_SESSION;
                isSessionBased   = true;
                break;

            case TableBase.FUNCTION_TABLE :
                persistenceScope = SCOPE_SESSION;
                isSessionBased   = true;
                break;

            default :
                throw Error.runtimeError(ErrorCode.U_S0500, "Table");
        }

        // type may have changed above for CACHED tables
        tableType         = type;
        primaryKeyCols    = null;
        primaryKeyTypes   = null;
        identityColumn    = -1;
        columnList        = new HashMappedList();
        indexList         = Index.emptyArray;
        constraintList    = Constraint.emptyArray;
        fkConstraints     = Constraint.emptyArray;
        fkMainConstraints = Constraint.emptyArray;
        checkConstraints  = Constraint.emptyArray;
        triggerList       = TriggerDef.emptyArray;
        triggerLists      = new TriggerDef[TriggerDef.NUM_TRIGS][];

        for (int i = 0; i < TriggerDef.NUM_TRIGS; i++) {
            triggerLists[i] = TriggerDef.emptyArray;
        }

        if (database.isFilesReadOnly() && isFileBased()) {
            this.isReadOnly = true;
        }

        if (isSchemaBased && !isTemp) {
            createDefaultStore();
        }
    }

    /** trigger transition table */
    public Table(Table table, HsqlName name) {

        persistenceScope    = SCOPE_STATEMENT;
        name.schema         = SqlInvariants.SYSTEM_SCHEMA_HSQLNAME;
        this.tableName      = name;
        this.database       = table.database;
        this.tableType      = RESULT_TABLE;
        this.columnList     = table.columnList;
        this.columnCount    = table.columnCount;
        this.indexList      = Index.emptyArray;
        this.constraintList = Constraint.emptyArray;

        createPrimaryKey();
    }

    public void createDefaultStore() {

        store = database.logger.newStore(null,
                                         database.persistentStoreCollection,
                                         this, true);
    }

    public int getType() {
        return SchemaObject.TABLE;
    }

    /**
     *  Returns the HsqlName object fo the table
     */
    public final HsqlName getName() {
        return tableName;
    }

    public final void setName(HsqlName name) {
        tableName = name;
    }

    /**
     * Returns the catalog name or null, depending on a database property.
     */
    public HsqlName getCatalogName() {
        return database.getCatalogName();
    }

    /**
     * Returns the schema name.
     */
    public HsqlName getSchemaName() {
        return tableName.schema;
    }

    public Grantee getOwner() {
        return tableName.schema.owner;
    }

    public OrderedHashSet getReferences() {

        OrderedHashSet set = new OrderedHashSet();

        for (int i = 0; i < colTypes.length; i++) {
            ColumnSchema   column = getColumn(i);
            OrderedHashSet refs   = column.getReferences();

            if (!refs.isEmpty()) {
                set.add(column.getName());
            }
        }

        return set;
    }

    public OrderedHashSet getComponents() {

        OrderedHashSet set = new OrderedHashSet();

        set.addAll(constraintList);
        set.addAll(triggerList);

        for (int i = 0; i < indexList.length; i++) {
            if (!indexList[i].isConstraint()) {
                set.add(indexList[i]);
            }
        }

        return set;
    }

    public void compile(Session session, SchemaObject parentObject) {

        for (int i = 0; i < columnCount; i++) {
            ColumnSchema column = getColumn(i);

            column.compile(session, this);
        }
    }

    public String getSQL() {

        StringBuffer sb = new StringBuffer();

        sb.append(Tokens.T_CREATE).append(' ');

        if (isTemp()) {
            sb.append(Tokens.T_GLOBAL).append(' ');
            sb.append(Tokens.T_TEMPORARY).append(' ');
        } else if (isText()) {
            sb.append(Tokens.T_TEXT).append(' ');
        } else if (isCached()) {
            sb.append(Tokens.T_CACHED).append(' ');
        } else {
            sb.append(Tokens.T_MEMORY).append(' ');
        }

        sb.append(Tokens.T_TABLE).append(' ');
        sb.append(getName().getSchemaQualifiedStatementName());
        sb.append('(');

        int[]      pk      = getPrimaryKey();
        Constraint pkConst = getPrimaryConstraint();

        for (int j = 0; j < columnCount; j++) {
            ColumnSchema column  = getColumn(j);
            String       colname = column.getName().statementName;
            Type         type    = column.getDataType();

            if (j > 0) {
                sb.append(',');
            }

            sb.append(colname);
            sb.append(' ');
            sb.append(type.getTypeDefinition());

            String defaultString = column.getDefaultSQL();

            if (defaultString != null) {
                sb.append(' ').append(Tokens.T_DEFAULT).append(' ');
                sb.append(defaultString);
            }

            if (column.isIdentity()) {
                sb.append(' ').append(column.getIdentitySequence().getSQL());
            }

            if (!column.isNullable()) {
                Constraint c = getNotNullConstraintForColumn(j);

                if (c != null && !c.getName().isReservedName()) {
                    sb.append(' ').append(Tokens.T_CONSTRAINT).append(
                        ' ').append(c.getName().statementName);
                }

                sb.append(' ').append(Tokens.T_NOT).append(' ').append(
                    Tokens.T_NULL);
            }

            if (column.isGenerated()) {
                sb.append(' ').append(Tokens.T_GENERATED).append(' ');
                sb.append(Tokens.T_ALWAYS).append(' ').append(
                    Tokens.T_AS).append(Tokens.T_OPENBRACKET);
                sb.append(column.getGeneratingExpression().getSQL());
                sb.append(Tokens.T_CLOSEBRACKET);
            }

            if (pk.length == 1 && j == pk[0]
                    && pkConst.getName().isReservedName()) {
                sb.append(' ').append(Tokens.T_PRIMARY).append(' ').append(
                    Tokens.T_KEY);
            }
        }

        Constraint[] constraintList = getConstraints();

        for (int j = 0, vSize = constraintList.length; j < vSize; j++) {
            Constraint c = constraintList[j];

            if (!c.isForward) {
                String d = c.getSQL();

                if (d.length() > 0) {
                    sb.append(',');
                    sb.append(d);
                }
            }
        }

        sb.append(')');

        if (onCommitPreserve()) {
            sb.append(' ').append(Tokens.T_ON).append(' ');
            sb.append(Tokens.T_COMMIT).append(' ').append(Tokens.T_PRESERVE);
            sb.append(' ').append(Tokens.T_ROWS);
        }

        return sb.toString();
    }

    String[] getSQL(OrderedHashSet resolved, OrderedHashSet unresolved) {

        for (int i = 0; i < constraintList.length; i++) {
            Constraint c = constraintList[i];

            if (c.isForward) {
                unresolved.add(c);
            } else if (c.getConstraintType() == SchemaObject.ConstraintTypes
                    .UNIQUE || c.getConstraintType() == SchemaObject
                    .ConstraintTypes.PRIMARY_KEY) {
                resolved.add(c.getName());
            }
        }

        HsqlArrayList list = new HsqlArrayList();

        list.add(getSQL());

        if (!isTemp && !isText && hasIdentityColumn()) {
            list.add(NumberSequence.getRestartSQL(this));
        }

        for (int i = 0; i < indexList.length; i++) {
            if (!indexList[i].isConstraint()
                    && indexList[i].getColumnCount() > 0) {
                list.add(indexList[i].getSQL());
            }
        }

        String[] array = new String[list.size()];

        list.toArray(array);

        return array;
    }

    public String getSQLForReadOnly() {

        if (isReadOnly) {
            StringBuffer sb = new StringBuffer(64);

            sb.append(Tokens.T_SET).append(' ').append(Tokens.T_TABLE).append(
                ' ');
            sb.append(getName().getSchemaQualifiedStatementName());
            sb.append(' ').append(Tokens.T_READ).append(' ');
            sb.append(Tokens.T_ONLY);

            return sb.toString();
        } else {
            return null;
        }
    }

    public String[] getSQLForTextSource(boolean withHeader) {

        // readonly for TEXT tables only
        if (isText()) {
            HsqlArrayList list     = new HsqlArrayList();
            boolean       readonly = isDataReadOnly();

            if (readonly) {
                list.add(getSQLForReadOnly());
            }

            // data source
            String dataSource = ((TextTable) this).getDataSourceDDL();

            if (dataSource != null) {
                list.add(dataSource);
            }

            // header
            String header = ((TextTable) this).getDataSourceHeader();

            if (withHeader && header != null && !readonly) {
                list.add(header);
            }

            String[] array = new String[list.size()];

            list.toArray(array);

            return array;
        } else {
            return null;
        }
    }

    public String[] getTriggerSQL() {

        String[] array = new String[triggerList.length];

        for (int i = 0; i < triggerList.length; i++) {
            array[i] = triggerList[i].getSQL();
        }

        return array;
    }

    public String getIndexRootsSQL(int[] roots) {

        StringBuffer sb = new StringBuffer(128);

        sb.append(Tokens.T_SET).append(' ').append(Tokens.T_TABLE).append(' ');
        sb.append(getName().getSchemaQualifiedStatementName());
        sb.append(' ').append(Tokens.T_INDEX).append(' ').append('\'');
        sb.append(StringUtil.getList(roots, " ", ""));
        sb.append('\'');

        return sb.toString();
    }

    public String getColumnListSQL(int[] col, int len) {

        StringBuffer sb = new StringBuffer();

        sb.append('(');

        for (int i = 0; i < len; i++) {
            sb.append(getColumn(col[i]).getName().statementName);

            if (i < len - 1) {
                sb.append(',');
            }
        }

        sb.append(')');

        return sb.toString();
    }

    public String getColumnListWithTypeSQL() {

        StringBuffer sb = new StringBuffer();

        sb.append('(');

        for (int j = 0; j < columnCount; j++) {
            ColumnSchema column  = getColumn(j);
            String       colname = column.getName().statementName;
            Type         type    = column.getDataType();

            if (j > 0) {
                sb.append(',');
            }

            sb.append(colname);
            sb.append(' ');
            sb.append(type.getTypeDefinition());
        }

        sb.append(')');

        return sb.toString();
    }

    public long getChangeTimestamp() {
        return changeTimestamp;
    }

    public boolean isConnected() {
        return true;
    }

    /**
     * compares two full table rows based on a set of columns
     *
     * @param a a full row
     * @param b a full row
     * @param cols array of column indexes to compare
     * @param coltypes array of column types for the full row
     *
     * @return comparison result, -1,0,+1
     */
    public static int compareRows(Session session, Object[] a, Object[] b,
                                  int[] cols, Type[] coltypes) {

        int fieldcount = cols.length;

        for (int j = 0; j < fieldcount; j++) {
            int i = coltypes[cols[j]].compare(session, a[cols[j]], b[cols[j]]);

            if (i != 0) {
                return i;
            }
        }

        return 0;
    }

    /**
     * Used to create row id's
     */
    public int getId() {
        return tableName.hashCode();
    }

    public final boolean isText() {
        return isText;
    }

    public final boolean isTemp() {
        return isTemp;
    }

    public final boolean isReadOnly() {
        return isReadOnly;
    }

    public final boolean isView() {
        return isView;
    }

    public boolean isCached() {
        return isCached;
    }

    public boolean isDataReadOnly() {
        return isReadOnly;
    }

    /**
     * returns false if the table has to be recreated in order to add / drop
     * indexes. Only CACHED tables return false.
     */
    final boolean isIndexingMutable() {
        return !isIndexCached();
    }

    /**
     *  Returns true if table is CACHED
     */
    boolean isIndexCached() {
        return isCached;
    }

    /**
     * Used by INSERT, DELETE, UPDATE operations
     */
    void checkDataReadOnly() {

        if (isReadOnly) {
            throw Error.error(ErrorCode.DATA_IS_READONLY);
        }
    }

// ----------------------------------------------------------------------------
// akede@users - 1.7.2 patch Files readonly
    public void setDataReadOnly(boolean value) {

        // Changing the Read-Only mode for the table is only allowed if the
        // the database can realize it.
        if (!value && database.isFilesReadOnly() && isFileBased()) {
            throw Error.error(ErrorCode.DATA_IS_READONLY);
        }

        isReadOnly = value;
    }

    /**
     * Text or Cached Tables are normally file based
     */
    public boolean isFileBased() {
        return isCached || isText;
    }

    /**
     *  Adds a constraint.
     */
    public void addConstraint(Constraint c) {

        int index = c.getConstraintType()
                    == SchemaObject.ConstraintTypes.PRIMARY_KEY ? 0
                                                                : constraintList
                                                                    .length;

        constraintList =
            (Constraint[]) ArrayUtil.toAdjustedArray(constraintList, c, index,
                1);

        updateConstraintLists();
    }

    void updateConstraintLists() {

        int fkCount    = 0;
        int mainCount  = 0;
        int checkCount = 0;

        hasReferentialAction = false;

        for (int i = 0; i < constraintList.length; i++) {
            switch (constraintList[i].getConstraintType()) {

                case SchemaObject.ConstraintTypes.FOREIGN_KEY :
                    fkCount++;
                    break;

                case SchemaObject.ConstraintTypes.MAIN :
                    mainCount++;
                    break;

                case SchemaObject.ConstraintTypes.CHECK :
                    if (constraintList[i].isNotNull()) {
                        break;
                    }

                    checkCount++;
                    break;
            }
        }

        fkConstraints     = fkCount == 0 ? Constraint.emptyArray
                                         : new Constraint[fkCount];
        fkCount           = 0;
        fkMainConstraints = mainCount == 0 ? Constraint.emptyArray
                                           : new Constraint[mainCount];
        mainCount         = 0;
        checkConstraints  = checkCount == 0 ? Constraint.emptyArray
                                            : new Constraint[checkCount];
        checkCount        = 0;
        colRefFK          = new boolean[columnCount];
        colMainFK         = new boolean[columnCount];

        for (int i = 0; i < constraintList.length; i++) {
            switch (constraintList[i].getConstraintType()) {

                case SchemaObject.ConstraintTypes.FOREIGN_KEY :
                    fkConstraints[fkCount] = constraintList[i];

                    ArrayUtil.intIndexesToBooleanArray(
                        constraintList[i].getRefColumns(), colRefFK);

                    fkCount++;
                    break;

                case SchemaObject.ConstraintTypes.MAIN :
                    fkMainConstraints[mainCount] = constraintList[i];

                    ArrayUtil.intIndexesToBooleanArray(
                        constraintList[i].getMainColumns(), colMainFK);

                    if (constraintList[i].hasTriggeredAction()) {
                        hasReferentialAction = true;
                    }

                    mainCount++;
                    break;

                case SchemaObject.ConstraintTypes.CHECK :
                    if (constraintList[i].isNotNull()) {
                        break;
                    }

                    checkConstraints[checkCount] = constraintList[i];

                    checkCount++;
                    break;
            }
        }
    }

    void verifyConstraintsIntegrity() {

        for (int i = 0; i < constraintList.length; i++) {
            Constraint c = constraintList[i];

            if (c.getConstraintType() == SchemaObject.ConstraintTypes
                    .FOREIGN_KEY || c.getConstraintType() == SchemaObject
                    .ConstraintTypes.MAIN) {
                if (c.getMain()
                        != database.schemaManager.findUserTable(null,
                            c.getMain().getName().name,
                            c.getMain().getName().schema.name)) {
                    throw Error.runtimeError(ErrorCode.U_S0500,
                                             "FK mismatch : "
                                             + c.getName().name);
                }

                if (c.getRef()
                        != database.schemaManager.findUserTable(null,
                            c.getRef().getName().name,
                            c.getRef().getName().schema.name)) {
                    throw Error.runtimeError(ErrorCode.U_S0500,
                                             "FK mismatch : "
                                             + c.getName().name);
                }
            }
        }
    }

    /**
     *  Returns the list of constraints.
     */
    public Constraint[] getConstraints() {
        return constraintList;
    }

    /**
     *  Returns the list of FK constraints.
     */
    public Constraint[] getFKConstraints() {
        return fkConstraints;
    }

    /**
     *  Returns the primary constraint.
     */
    public Constraint getPrimaryConstraint() {
        return primaryKeyCols.length == 0 ? null
                                          : constraintList[0];
    }

    /** columnMap is null for deletes */
    void collectFKReadLocks(int[] columnMap, OrderedHashSet set) {

        for (int i = 0; i < fkMainConstraints.length; i++) {
            Constraint constraint  = fkMainConstraints[i];
            Table      ref         = constraint.getRef();
            int[]      mainColumns = constraint.getMainColumns();

            if (ref == this) {
                continue;
            }

            if (columnMap == null) {
                if (constraint.core.hasDeleteAction) {
                    int[] cols =
                        constraint.core.deleteAction
                        == SchemaObject.ReferentialAction.CASCADE ? null
                                                                  : constraint
                                                                      .getRefColumns();

                    if (set.add(ref.getName())) {
                        ref.collectFKReadLocks(cols, set);
                    }
                }
            } else if (ArrayUtil.haveCommonElement(columnMap, mainColumns)) {
                if (set.add(ref.getName())) {
                    ref.collectFKReadLocks(constraint.getRefColumns(), set);
                }
            }
        }
    }

    /** columnMap is null for deletes */
    void collectFKWriteLocks(int[] columnMap, OrderedHashSet set) {

        for (int i = 0; i < fkMainConstraints.length; i++) {
            Constraint constraint  = fkMainConstraints[i];
            Table      ref         = constraint.getRef();
            int[]      mainColumns = constraint.getMainColumns();

            if (ref == this) {
                continue;
            }

            if (columnMap == null) {
                if (constraint.core.hasDeleteAction) {
                    int[] cols =
                        constraint.core.deleteAction
                        == SchemaObject.ReferentialAction.CASCADE ? null
                                                                  : constraint
                                                                      .getRefColumns();

                    if (set.add(ref.getName())) {
                        ref.collectFKWriteLocks(cols, set);
                    }
                }
            } else if (ArrayUtil.haveCommonElement(columnMap, mainColumns)) {
                if (constraint.core.hasUpdateAction) {
                    if (set.add(ref.getName())) {
                        ref.collectFKWriteLocks(constraint.getRefColumns(),
                                                set);
                    }
                }
            }
        }
    }

    Constraint getNotNullConstraintForColumn(int colIndex) {

        for (int i = 0, size = constraintList.length; i < size; i++) {
            Constraint c = constraintList[i];

            if (c.isNotNull() && c.notNullColumnIndex == colIndex) {
                return c;
            }
        }

        return null;
    }

    /**
     * Returns the UNIQUE or PK constraint with the given column signature.
     */
    Constraint getUniqueConstraintForColumns(int[] cols) {

        for (int i = 0, size = constraintList.length; i < size; i++) {
            Constraint c = constraintList[i];

            if (c.isUniqueWithColumns(cols)) {
                return c;
            }
        }

        return null;
    }

    /**
     * Returns the UNIQUE or PK constraint with the given column signature.
     * Modifies the composition of refTableCols if necessary.
     */
    Constraint getUniqueConstraintForColumns(int[] mainTableCols,
            int[] refTableCols) {

        for (int i = 0, size = constraintList.length; i < size; i++) {
            Constraint c    = constraintList[i];
            int        type = c.getConstraintType();

            if (type != SchemaObject.ConstraintTypes.UNIQUE
                    && type != SchemaObject.ConstraintTypes.PRIMARY_KEY) {
                continue;
            }

            int[] constraintCols = c.getMainColumns();

            if (constraintCols.length != mainTableCols.length) {
                continue;
            }

            if (ArrayUtil.areEqual(constraintCols, mainTableCols,
                                   mainTableCols.length, true)) {
                return c;
            }

            if (ArrayUtil.areEqualSets(constraintCols, mainTableCols)) {
                int[] newRefTableCols = new int[mainTableCols.length];

                for (int j = 0; j < mainTableCols.length; j++) {
                    int pos = ArrayUtil.find(constraintCols, mainTableCols[j]);

                    newRefTableCols[pos] = refTableCols[j];
                }

                for (int j = 0; j < mainTableCols.length; j++) {
                    refTableCols[j] = newRefTableCols[j];
                }

                return c;
            }
        }

        return null;
    }

    /**
     *  Returns any foreign key constraint equivalent to the column sets
     */
    Constraint getFKConstraintForColumns(Table tableMain, int[] mainCols,
                                         int[] refCols) {

        for (int i = 0, size = constraintList.length; i < size; i++) {
            Constraint c = constraintList[i];

            if (c.isEquivalent(tableMain, mainCols, this, refCols)) {
                return c;
            }
        }

        return null;
    }

    /**
     *  Returns any unique Constraint using this index
     *
     * @param  index
     */
    public Constraint getUniqueOrPKConstraintForIndex(Index index) {

        for (int i = 0, size = constraintList.length; i < size; i++) {
            Constraint c = constraintList[i];

            if (c.getMainIndex() == index && (c
                    .getConstraintType() == SchemaObject.ConstraintTypes
                    .UNIQUE || c.getConstraintType() == SchemaObject
                    .ConstraintTypes.PRIMARY_KEY)) {
                return c;
            }
        }

        return null;
    }

    /**
     *  Returns the next constraint of a given type
     *
     * @param  from
     * @param  type
     */
    int getNextConstraintIndex(int from, int type) {

        for (int i = from, size = constraintList.length; i < size; i++) {
            Constraint c = constraintList[i];

            if (c.getConstraintType() == type) {
                return i;
            }
        }

        return -1;
    }

    /**
     *  Performs the table level checks and adds a column to the table at the
     *  DDL level. Only used at table creation, not at alter column.
     */
    public void addColumn(ColumnSchema column) {

        String name = column.getName().name;

        if (findColumn(name) >= 0) {
            throw Error.error(ErrorCode.X_42504, name);
        }

        if (column.isIdentity()) {
            if (identityColumn != -1) {
                throw Error.error(ErrorCode.X_42525, name);
            }

            identityColumn   = columnCount;
            identitySequence = column.getIdentitySequence();
        }

        addColumnNoCheck(column);
    }

    public void addColumnNoCheck(ColumnSchema column) {

        if (primaryKeyCols != null) {
            throw Error.runtimeError(ErrorCode.U_S0500, "Table");
        }

        columnList.add(column.getName().name, column);

        columnCount++;

        if (column.getDataType().isLobType()) {
            hasLobColumn = true;
        }
    }

    public boolean hasLobColumn() {
        return hasLobColumn;
    }

    public boolean hasIdentityColumn() {
        return identityColumn != -1;
    }

    public long getNextIdentity() {
        return identitySequence.peek();
    }

    /**
     * Match two valid, equal length, columns arrays for type of columns
     *
     * @param col column array from this Table
     * @param other the other Table object
     * @param othercol column array from the other Table
     */
    void checkColumnsMatch(int[] col, Table other, int[] othercol) {

        for (int i = 0; i < col.length; i++) {
            Type type      = colTypes[col[i]];
            Type otherType = other.colTypes[othercol[i]];

            if (type.typeComparisonGroup != otherType.typeComparisonGroup) {
                throw Error.error(ErrorCode.X_42562);
            }
        }
    }

    void checkColumnsMatch(ColumnSchema column, int colIndex) {

        Type type      = colTypes[colIndex];
        Type otherType = column.getDataType();

        if (type.typeComparisonGroup != otherType.typeComparisonGroup) {
            throw Error.error(ErrorCode.X_42562);
        }
    }

    /**
     * For removal or addition of columns, constraints and indexes
     *
     * Does not work in this form for FK's as Constraint.ConstraintCore
     * is not transfered to a referencing or referenced table
     */
    Table moveDefinition(Session session, int newType, ColumnSchema column,
                         Constraint constraint, Index index, int colIndex,
                         int adjust, OrderedHashSet dropConstraints,
                         OrderedHashSet dropIndexes) {

        boolean newPK = false;

        if (constraint != null
                && constraint.constType
                   == SchemaObject.ConstraintTypes.PRIMARY_KEY) {
            newPK = true;
        }

        Table tn;

        if (isText) {
            tn = new TextTable(database, tableName, newType);
            ((TextTable) tn).dataSource  = ((TextTable) this).dataSource;
            ((TextTable) tn).isReversed  = ((TextTable) this).isReversed;
            ((TextTable) tn).isConnected = ((TextTable) this).isConnected;
        } else {
            tn = new Table(database, tableName, newType);
        }

        if (tableType == TEMP_TABLE) {
            tn.persistenceScope = persistenceScope;
        }

        for (int i = 0; i < columnCount; i++) {
            ColumnSchema col = (ColumnSchema) columnList.get(i);

            if (i == colIndex) {
                if (column != null) {
                    tn.addColumn(column);
                }

                if (adjust <= 0) {
                    continue;
                }
            }

            tn.addColumn(col);
        }

        if (columnCount == colIndex) {
            tn.addColumn(column);
        }

        int[] pkCols = null;

        if (hasPrimaryKey()
                && !dropConstraints.contains(
                    getPrimaryConstraint().getName())) {
            pkCols = primaryKeyCols;
            pkCols = ArrayUtil.toAdjustedColumnArray(pkCols, colIndex, adjust);
        } else if (newPK) {
            pkCols = constraint.getMainColumns();
        }

        tn.createPrimaryKey(getIndex(0).getName(), pkCols, false);

        for (int i = 1; i < indexList.length; i++) {
            Index idx = indexList[i];

            if (dropIndexes.contains(idx.getName())) {
                continue;
            }

            int[] colarr = ArrayUtil.toAdjustedColumnArray(idx.getColumns(),
                colIndex, adjust);

            idx = tn.createIndexStructure(idx.getName(), colarr,
                                          idx.getColumnDesc(), null,
                                          idx.isUnique(), idx.isConstraint(),
                                          idx.isForward());

            tn.addIndex(session, idx);
        }

        if (index != null) {
            tn.addIndex(session, index);
        }

        HsqlArrayList newList = new HsqlArrayList();

        if (newPK) {
            constraint.core.mainIndex     = tn.indexList[0];
            constraint.core.mainTable     = tn;
            constraint.core.mainTableName = tn.tableName;

            newList.add(constraint);
        }

        for (int i = 0; i < constraintList.length; i++) {
            Constraint c = constraintList[i];

            if (dropConstraints.contains(c.getName())) {
                continue;
            }

            c = c.duplicate();

            c.updateTable(session, this, tn, colIndex, adjust);
            newList.add(c);
        }

        if (!newPK && constraint != null) {
            constraint.updateTable(session, this, tn, -1, 0);
            newList.add(constraint);
        }

        tn.constraintList = new Constraint[newList.size()];

        newList.toArray(tn.constraintList);
        tn.updateConstraintLists();
        tn.setBestRowIdentifiers();

        tn.triggerList  = triggerList;
        tn.triggerLists = triggerLists;

        return tn;
    }

    /**
     * Used for drop / retype column.
     */
    void checkColumnInCheckConstraint(int colIndex) {

        for (int i = 0, size = constraintList.length; i < size; i++) {
            Constraint c = constraintList[i];

            if (c.constType == SchemaObject.ConstraintTypes.CHECK
                    && !c.isNotNull() && c.hasColumn(colIndex)) {
                HsqlName name = c.getName();

                throw Error.error(ErrorCode.X_42502,
                                  name.getSchemaQualifiedStatementName());
            }
        }
    }

    /**
     * Used for retype column. Checks whether column is in an FK or is
     * referenced by a FK
     * @param colIndex index
     */
    void checkColumnInFKConstraint(int colIndex) {

        for (int i = 0, size = constraintList.length; i < size; i++) {
            Constraint c = constraintList[i];

            if (c.hasColumn(colIndex) && (c.getConstraintType() == SchemaObject
                    .ConstraintTypes.MAIN || c
                    .getConstraintType() == SchemaObject.ConstraintTypes
                    .FOREIGN_KEY)) {
                HsqlName name = c.getName();

                throw Error.error(ErrorCode.X_42533,
                                  name.getSchemaQualifiedStatementName());
            }
        }
    }

    /**
     * Returns list of constraints dependent only on one column
     */
    OrderedHashSet getDependentConstraints(int colIndex) {

        OrderedHashSet set = new OrderedHashSet();

        for (int i = 0, size = constraintList.length; i < size; i++) {
            Constraint c = constraintList[i];

            if (c.hasColumnOnly(colIndex)) {
                set.add(c);
            }
        }

        return set;
    }

    /**
     * Returns list of constraints dependent on more than one column
     */
    OrderedHashSet getContainingConstraints(int colIndex) {

        OrderedHashSet set = new OrderedHashSet();

        for (int i = 0, size = constraintList.length; i < size; i++) {
            Constraint c = constraintList[i];

            if (c.hasColumnPlus(colIndex)) {
                set.add(c);
            }
        }

        return set;
    }

    OrderedHashSet getContainingIndexNames(int colIndex) {

        OrderedHashSet set = new OrderedHashSet();

        for (int i = 0, size = indexList.length; i < size; i++) {
            Index index = indexList[i];

            if (ArrayUtil.find(index.getColumns(), colIndex) != -1) {
                set.add(index.getName());
            }
        }

        return set;
    }

    /**
     * Returns list of MAIN constraints dependent on this PK or UNIQUE constraint
     */
    OrderedHashSet getDependentConstraints(Constraint constraint) {

        OrderedHashSet set = new OrderedHashSet();

        for (int i = 0, size = constraintList.length; i < size; i++) {
            Constraint c = constraintList[i];

            if (c.getConstraintType() == SchemaObject.ConstraintTypes.MAIN) {
                if (c.core.uniqueName == constraint.getName()) {
                    set.add(c);
                }
            }
        }

        return set;
    }

    public OrderedHashSet getDependentExternalConstraints() {

        OrderedHashSet set = new OrderedHashSet();

        for (int i = 0, size = constraintList.length; i < size; i++) {
            Constraint c = constraintList[i];

            if (c.getConstraintType() == SchemaObject.ConstraintTypes.MAIN
                    || c.getConstraintType()
                       == SchemaObject.ConstraintTypes.FOREIGN_KEY) {
                if (c.core.mainTable != c.core.refTable) {
                    set.add(c);
                }
            }
        }

        return set;
    }

    /**
     * Used for column defaults and nullability. Checks whether column is in an
     * FK with a given referential action type.
     *
     * @param colIndex index of column
     * @param actionType referential action of the FK
     */
    void checkColumnInFKConstraint(int colIndex, int actionType) {

        for (int i = 0, size = constraintList.length; i < size; i++) {
            Constraint c = constraintList[i];

            if (c.getConstraintType() == SchemaObject.ConstraintTypes
                    .FOREIGN_KEY && c
                    .hasColumn(colIndex) && (actionType == c
                        .getUpdateAction() || actionType == c
                        .getDeleteAction())) {
                HsqlName name = c.getName();

                throw Error.error(ErrorCode.X_42533,
                                  name.getSchemaQualifiedStatementName());
            }
        }
    }

    /**
     *  Returns the identity column index.
     */
    int getIdentityColumnIndex() {
        return identityColumn;
    }

    /**
     *  Returns the index of given column name or throws if not found
     */
    public int getColumnIndex(String name) {

        int i = findColumn(name);

        if (i == -1) {
            throw Error.error(ErrorCode.X_42501, name);
        }

        return i;
    }

    /**
     *  Returns the index of given column name or -1 if not found.
     */
    public int findColumn(String name) {

        int index = columnList.getIndex(name);

        return index;
    }

    /**
     * Sets the SQL default value for a columm.
     */
    void setDefaultExpression(int columnIndex, Expression def) {

        ColumnSchema column = getColumn(columnIndex);

        column.setDefaultExpression(def);
        setColumnTypeVars(columnIndex);
    }

    /**
     * sets the flag for the presence of any default expression
     */
    void resetDefaultsFlag() {

        hasDefaultValues = false;

        for (int i = 0; i < colDefaults.length; i++) {
            hasDefaultValues |= colDefaults[i] != null;
        }

        hasGeneratedValues = false;

        for (int i = 0; i < colGenerated.length; i++) {
            hasGeneratedValues |= colGenerated[i];
        }

        hasNotNullColumns = false;

        for (int i = 0; i < colNotNull.length; i++) {
            hasNotNullColumns |= colNotNull[i];
        }
    }

    public int[] getBestRowIdentifiers() {
        return bestRowIdentifierCols;
    }

    public boolean isBestRowIdentifiersStrict() {
        return bestRowIdentifierStrict;
    }

    /**
     *  Finds an existing index for a column
     */
    Index getIndexForColumn(Session session, int col) {

        int i = bestIndexForColumn[col];

        if (i > -1) {
            return indexList[i];
        }

        switch (tableType) {

//            case TableBase.MEMORY_TABLE :
            case TableBase.SYSTEM_SUBQUERY :
            case TableBase.SYSTEM_TABLE :
            case TableBase.VIEW_TABLE :
            case TableBase.TEMP_TABLE : {
                Index index = createIndexForColumns(session, new int[]{ col });

                return index;
            }
        }

        return null;
    }

    boolean isIndexed(int colIndex) {
        return bestIndexForColumn[colIndex] != -1;
    }

    int[] getUniqueNotNullColumnGroup(boolean[] usedColumns) {

        for (int i = 0, count = constraintList.length; i < count; i++) {
            Constraint constraint = constraintList[i];

            if (constraint.constType == SchemaObject.ConstraintTypes.UNIQUE) {
                int[] indexCols = constraint.getMainColumns();

                if (ArrayUtil.areAllIntIndexesInBooleanArray(
                        indexCols, colNotNull) && ArrayUtil
                            .areAllIntIndexesInBooleanArray(
                                indexCols, usedColumns)) {
                    return indexCols;
                }
            } else if (constraint.constType
                       == SchemaObject.ConstraintTypes.PRIMARY_KEY) {
                int[] indexCols = constraint.getMainColumns();

                if (ArrayUtil.areAllIntIndexesInBooleanArray(indexCols,
                        usedColumns)) {
                    return indexCols;
                }
            }
        }

        return null;
    }

    boolean areColumnsNotNull(int[] indexes) {
        return ArrayUtil.areAllIntIndexesInBooleanArray(indexes, colNotNull);
    }

    /**
     *  Shortcut for creating default PK's.
     */
    public void createPrimaryKey() {
        createPrimaryKey(null, null, false);
    }

    /**
     *  Creates a single or multi-column primary key and index. sets the
     *  colTypes array. Finalises the creation of the table. (fredt@users)
     */
    public void createPrimaryKey(HsqlName indexName, int[] columns,
                                 boolean columnsNotNull) {

        if (primaryKeyCols != null) {
            throw Error.runtimeError(ErrorCode.U_S0500, "Table");
        }

        if (columns == null) {
            columns = ValuePool.emptyIntArray;
        } else {
            for (int i = 0; i < columns.length; i++) {
                getColumn(columns[i]).setPrimaryKey(true);
            }
        }

        primaryKeyCols = columns;

        setColumnStructures();

        primaryKeyTypes = new Type[primaryKeyCols.length];

        ArrayUtil.projectRow(colTypes, primaryKeyCols, primaryKeyTypes);

        primaryKeyColsSequence = new int[primaryKeyCols.length];

        ArrayUtil.fillSequence(primaryKeyColsSequence);

        HsqlName name = indexName;

        if (name == null) {
            name = database.nameManager.newAutoName("IDX", getSchemaName(),
                    getName(), SchemaObject.INDEX);
        }

        createPrimaryIndex(primaryKeyCols, primaryKeyTypes, name);
        setBestRowIdentifiers();
    }

    public void createPrimaryKeyConstraint(HsqlName indexName, int[] columns,
                                           boolean columnsNotNull) {

        createPrimaryKey(indexName, columns, columnsNotNull);

        Constraint c =
            new Constraint(indexName, this, getPrimaryIndex(),
                           SchemaObject.ConstraintTypes.PRIMARY_KEY);

        this.addConstraint(c);
    }

    void setColumnStructures() {

        colTypes         = new Type[columnCount];
        colDefaults      = new Expression[columnCount];
        colNotNull       = new boolean[columnCount];
        colGenerated     = new boolean[columnCount];
        defaultColumnMap = new int[columnCount];
        hasDomainColumns = false;

        for (int i = 0; i < columnCount; i++) {
            setColumnTypeVars(i);
        }

        resetDefaultsFlag();

        defaultRanges = new RangeVariable[]{ new RangeVariable(this, 1) };
    }

    void setColumnTypeVars(int i) {

        ColumnSchema column   = getColumn(i);
        Type         dataType = column.getDataType();

        if (dataType.isDomainType()) {
            hasDomainColumns = true;
        }

        colTypes[i]         = dataType;
        colNotNull[i]       = column.isPrimaryKey() || !column.isNullable();
        defaultColumnMap[i] = i;

        if (column.isIdentity()) {
            identitySequence = column.getIdentitySequence();
            identityColumn   = i;
        } else if (identityColumn == i) {
            identityColumn = -1;
        }

        colDefaults[i= column.getDefaultExpression();
        colGenerated[i] = column.isGenerated();

        resetDefaultsFlag();
    }

    /**
     * Returns direct mapping array.
     */
    int[] getColumnMap() {
        return defaultColumnMap;
    }

    /**
     * Returns empty mapping array.
     */
    int[] getNewColumnMap() {
        return new int[columnCount];
    }

    boolean[] getColumnCheckList(int[] columnIndexes) {

        boolean[] columnCheckList = new boolean[columnCount];

        for (int i = 0; i < columnIndexes.length; i++) {
            int index = columnIndexes[i];

            if (index > -1) {
                columnCheckList[index] = true;
            }
        }

        return columnCheckList;
    }

    int[] getColumnIndexes(OrderedHashSet set) {

        int[] cols = new int[set.size()];

        for (int i = 0; i < cols.length; i++) {
            cols[i] = getColumnIndex((String) set.get(i));
        }

        return cols;
    }

    int[] getColumnIndexes(HashMappedList list) {

        int[] cols = new int[list.size()];

        for (int i = 0; i < cols.length; i++) {
            cols[i] = ((Integer) list.get(i)).intValue();
        }

        return cols;
    }

    /**
     *  Returns the Column object at the given index
     */
    public ColumnSchema getColumn(int i) {
        return (ColumnSchema) columnList.get(i);
    }

    public OrderedHashSet getColumnNameSet(int[] columnIndexes) {

        OrderedHashSet set = new OrderedHashSet();

        for (int i = 0; i < columnIndexes.length; i++) {
            set.add(((ColumnSchema) columnList.get(i)).getName());
        }

        return set;
    }

    public OrderedHashSet getColumnNameSet(boolean[] columnCheckList) {

        OrderedHashSet set = new OrderedHashSet();

        for (int i = 0; i < columnCheckList.length; i++) {
            if (columnCheckList[i]) {
                set.add(columnList.get(i));
            }
        }

        return set;
    }

    public void getColumnNames(boolean[] columnCheckList, Set set) {

        for (int i = 0; i < columnCheckList.length; i++) {
            if (columnCheckList[i]) {
                set.add(((ColumnSchema) columnList.get(i)).getName());
            }
        }
    }

    public OrderedHashSet getColumnNameSet() {

        OrderedHashSet set = new OrderedHashSet();

        for (int i = 0; i < columnCount; i++) {
            set.add(((ColumnSchema) columnList.get(i)).getName());
        }

        return set;
    }

    /**
     * Returns array for a new row with SQL DEFAULT value for each column n
     * where exists[n] is false. This provides default values only where
     * required and avoids evaluating these values where they will be
     * overwritten.
     */
    Object[] getNewRowData(Session session) {

        Object[] data = new Object[columnCount];
        int      i;

        if (hasDefaultValues) {
            for (i = 0; i < columnCount; i++) {
                Expression def = colDefaults[i];

                if (def != null) {
                    data[i] = def.getValue(session, colTypes[i]);
                }
            }
        }

        return data;
    }

    boolean hasTrigger(int trigVecIndex) {
        return triggerLists[trigVecIndex].length != 0;
    }

    /**
     * Adds a trigger.
     */
    void addTrigger(TriggerDef td, HsqlName otherName) {

        int index = triggerList.length;

        if (otherName != null) {
            int pos = getTriggerIndex(otherName.name);

            if (pos != -1) {
                index = pos + 1;
            }
        }

        triggerList = (TriggerDef[]) ArrayUtil.toAdjustedArray(triggerList,
                td, index, 1);

        TriggerDef[] list = triggerLists[td.triggerType];

        index = list.length;

        if (otherName != null) {
            for (int i = 0; i < list.length; i++) {
                TriggerDef trigger = list[i];

                if (trigger.getName().name.equals(otherName.name)) {
                    index = i + 1;

                    break;
                }
            }
        }

        list = (TriggerDef[]) ArrayUtil.toAdjustedArray(list, td, index, 1);
        triggerLists[td.triggerType] = list;
    }

    /**
     * Returns a trigger.
     */
    TriggerDef getTrigger(String name) {

        for (int i = triggerList.length - 1; i >= 0; i--) {
            if (triggerList[i].getName().name.equals(name)) {
                return triggerList[i];
            }
        }

        return null;
    }

    public int getTriggerIndex(String name) {

        for (int i = 0; i < triggerList.length; i++) {
            if (triggerList[i].getName().name.equals(name)) {
                return i;
            }
        }

        return -1;
    }

    /**
     * Drops a trigger.
     */
    void removeTrigger(TriggerDef trigger) {

        TriggerDef td = null;

        for (int i = 0; i < triggerList.length; i++) {
            td = triggerList[i];

            if (td.getName().name.equals(trigger.getName().name)) {
                td.terminate();

                triggerList =
                    (TriggerDef[]) ArrayUtil.toAdjustedArray(triggerList,
                        null, i, -1);

                break;
            }
        }

        if (td == null) {
            return;
        }

        int index = td.triggerType;

        // look in each trigger in list
        for (int j = 0; j < triggerLists[index].length; j++) {
            td = triggerLists[index][j];

            if (td.getName().name.equals(trigger.getName().name)) {
                td.terminate();

                triggerLists[index] = (TriggerDef[]) ArrayUtil.toAdjustedArray(
                    triggerLists[index], null, j, -1);

                break;
            }
        }
    }

    /**
     * Drops all triggers.
     */
    void releaseTriggers() {

        // look in each trigger list of each type of trigger
        for (int i = 0; i < TriggerDef.NUM_TRIGS; i++) {
            for (int j = 0; j < triggerLists[i].length; j++) {
                triggerLists[i][j].terminate();
            }

            triggerLists[i] = TriggerDef.emptyArray;
        }
    }

    /**
     * Returns the index of the Index object of the given name or -1 if not found.
     */
    int getIndexIndex(String indexName) {

        Index[] indexes = indexList;

        for (int i = 0; i < indexes.length; i++) {
            if (indexName.equals(indexes[i].getName().name)) {
                return i;
            }
        }

        // no such index
        return -1;
    }

    /**
     * Returns the Index object of the given name or null if not found.
     */
    Index getIndex(String indexName) {

        Index[] indexes = indexList;
        int     i       = getIndexIndex(indexName);

        return i == -1 ? null
                       : indexes[i];
    }

    /**
     *  Return the position of the constraint within the list
     */
    int getConstraintIndex(String constraintName) {

        for (int i = 0, size = constraintList.length; i < size; i++) {
            if (constraintList[i].getName().name.equals(constraintName)) {
                return i;
            }
        }

        return -1;
    }

    /**
     *  return the named constriant
     */
    public Constraint getConstraint(String constraintName) {

        int i = getConstraintIndex(constraintName);

        return (i < 0) ? null
                       : constraintList[i];
    }

    /**
     *  Returns any unique Constraint using this index
     *
     * @param  index
     * @return
     */
    public Constraint getUniqueConstraintForIndex(Index index) {

        for (int i = 0, size = constraintList.length; i < size; i++) {
            Constraint c = constraintList[i];

            if (c.getMainIndex() == index) {
                if (c.getConstraintType() == SchemaObject.ConstraintTypes
                        .PRIMARY_KEY || c.getConstraintType() == SchemaObject
                        .ConstraintTypes.UNIQUE) {
                    return c;
                }
            }
        }

        return null;
    }

    /**
     * remove a named constraint
     */
    void removeConstraint(String name) {

        int index = getConstraintIndex(name);

        if (index != -1) {
            removeConstraint(index);
        }
    }

    void removeConstraint(int index) {

        constraintList =
            (Constraint[]) ArrayUtil.toAdjustedArray(constraintList, null,
                index, -1);

        updateConstraintLists();
    }

    void renameColumn(ColumnSchema column, String newName, boolean isquoted) {

        String oldname = column.getName().name;
        int    i       = getColumnIndex(oldname);

        columnList.setKey(i, newName);
        column.getName().rename(newName, isquoted);
    }

    void renameColumn(ColumnSchema column, HsqlName newName) {

        String oldname = column.getName().name;
        int    i       = getColumnIndex(oldname);

        if (findColumn(newName.name) != -1) {
            throw Error.error(ErrorCode.X_42504);
        }

        columnList.setKey(i, newName.name);
        column.getName().rename(newName);
    }

    public TriggerDef[] getTriggers() {
        return triggerList;
    }

    public boolean isWritable() {
        return !isReadOnly && !database.databaseReadOnly
               && !(database.isFilesReadOnly() && (isCached || isText));
    }

    public boolean isInsertable() {
        return isWritable();
    }

    public boolean isUpdatable() {
        return isWritable();
    }

    public boolean isTriggerInsertable() {
        return false;
    }

    public boolean isTriggerUpdatable() {
        return false;
    }

    public boolean isTriggerDeletable() {
        return false;
    }

    public int[] getUpdatableColumns() {
        return defaultColumnMap;
    }

    public Table getBaseTable() {
        return this;
    }

    public int[] getBaseTableColumnMap() {
        return defaultColumnMap;
    }

//

    /**
     *  Used to create an index automatically for system and temp tables.
     */
    Index createIndexForColumns(Session session, int[] columns) {

        Index index = null;
        HsqlName indexName = database.nameManager.newAutoName("IDX_T",
            getSchemaName(), getName(), SchemaObject.INDEX);

        try {
            index = createAndAddIndexStructure(session, indexName, columns,
                                               null, null, false, false,
                                               false);
        } catch (Throwable t) {
            return null;
        }

        switch (tableType) {

            case TableBase.TEMP_TABLE : {
                Session sessions[] = database.sessionManager.getAllSessions();

                for (int i = 0; i < sessions.length; i++) {
                    sessions[i].sessionData.persistentStoreCollection
                        .registerIndex((Table) this);
                }

                break;
            }
            case TableBase.SYSTEM_TABLE : {
                session.sessionData.persistentStoreCollection.registerIndex(
                    (Table) this);

                break;
            }
        }

        return index;
    }

    void fireTriggers(Session session, int trigVecIndex,
                      RowSetNavigator rowSet) {

        if (!database.isReferentialIntegrity()) {
            return;
        }

        TriggerDef[] trigVec = triggerLists[trigVecIndex];

        for (int i = 0, size = trigVec.length; i < size; i++) {
            TriggerDef td         = trigVec[i];
            boolean    sqlTrigger = td instanceof TriggerDefSQL;

            if (td.hasOldTable()) {

                //
            }

            td.pushPair(session, null, null);
        }
    }

    /**
     *  Fires all row-level triggers of the given set (trigger type)
     *
     */
    void fireTriggers(Session session, int trigVecIndex, Object[] oldData,
                      Object[] newData, int[] cols) {

        if (!database.isReferentialIntegrity()) {
            return;
        }

        TriggerDef[] trigVec = triggerLists[trigVecIndex];

        for (int i = 0, size = trigVec.length; i < size; i++) {
            TriggerDef td         = trigVec[i];
            boolean    sqlTrigger = td instanceof TriggerDefSQL;

            if (cols != null && td.getUpdateColumnIndexes() != null
                    && !ArrayUtil.haveCommonElement(
                        td.getUpdateColumnIndexes(), cols)) {
                continue;
            }

            if (td.isForEachRow()) {
                switch (td.triggerType) {

                    case Trigger.INSERT_BEFORE_ROW :
                        break;

                    case Trigger.INSERT_AFTER_ROW :
                        if (!sqlTrigger) {
                            newData =
                                (Object[]) ArrayUtil.duplicateArray(newData);
                        }
                        break;

                    case Trigger.UPDATE_AFTER_ROW :
                        if (!sqlTrigger) {
                            oldData =
                                (Object[]) ArrayUtil.duplicateArray(oldData);
                            newData =
                                (Object[]) ArrayUtil.duplicateArray(newData);
                        }
                        break;

                    case Trigger.UPDATE_BEFORE_ROW :
                    case Trigger.DELETE_BEFORE_ROW :
                    case Trigger.DELETE_AFTER_ROW :
                        if (!sqlTrigger) {
                            oldData =
                                (Object[]) ArrayUtil.duplicateArray(oldData);
                        }
                        break;
                }

                td.pushPair(session, oldData, newData);
            } else {
                td.pushPair(session, null, null);
            }
        }
    }

    /**
     *  Enforce max field sizes according to SQL column definition.
     *  SQL92 13.8
     */
    public void enforceRowConstraints(Session session, Object[] data) {

        for (int i = 0; i < columnCount; i++) {
            Type type = colTypes[i];

            if (hasDomainColumns && type.isDomainType()) {
                Constraint[] constraints =
                    type.userTypeModifier.getConstraints();

                for (int j = 0; j < constraints.length; j++) {
                    constraints[j].checkCheckConstraint(session, this,
                                                        (Object) data[i]);
                }
            }

            if (colNotNull[i] && data[i] == null) {
                Constraint c = getNotNullConstraintForColumn(i);

                if (c == null) {
                    c = this.getPrimaryConstraint();
                }

                String[] info = new String[] {
                    c.getName().name, tableName.name
                };

                throw Error.error(null, ErrorCode.X_23502,
                                  ErrorCode.CONSTRAINT, info);
            }
        }
    }

    public void enforceTypeLimits(Session session, Object[] data) {

        for (int i = 0; i < columnCount; i++) {
            data[i] = colTypes[i].convertToTypeLimits(session, data[i]);
        }
    }

    boolean canGetIndexForColumn(Session session, int col) {

        int i = bestIndexForColumn[col];

        if (i > -1) {
            return true;
        }

        switch (tableType) {

//            case TableBase.MEMORY_TABLE :
            case TableBase.SYSTEM_SUBQUERY :
            case TableBase.SYSTEM_TABLE :
            case TableBase.VIEW_TABLE :
            case TableBase.TEMP_TABLE : {
                return true;
            }
        }

        return false;
    }

    /**
     *  Finds an existing index for a column group
     */
    Index getIndexForColumns(Session session, int[] cols) {

        int i = bestIndexForColumn[cols[0]];

        if (i > -1) {
            return indexList[i];
        }

        switch (tableType) {

//            case TableBase.MEMORY_TABLE :
            case TableBase.SYSTEM_SUBQUERY :
            case TableBase.SYSTEM_TABLE :
            case TableBase.VIEW_TABLE :
            case TableBase.TEMP_TABLE : {
                Index index = createIndexForColumns(session, cols);

                return index;
            }
        }

        return null;
    }

    /**
     *  Finds an existing index for a full column group
     */
    Index getFullIndexForColumns(int[] cols) {

        for (int i = 0; i < indexList.length; i++) {
            if (ArrayUtil.haveEqualArrays(indexList[i].getColumns(), cols,
                                          cols.length)) {
                return indexList[i];
            }
        }

        return null;
    }

    /**
     * Finds an existing index for a column set or create one for temporary
     * tables
     */
    Index getIndexForColumns(Session session, OrderedIntHashSet set,
                             boolean ordered) {

        int   maxMatchCount = 0;
        Index selected      = null;

        if (set.isEmpty()) {
            return null;
        }

        for (int i = 0, count = indexList.length; i < count; i++) {
            Index currentindex = getIndex(i);
            int[] indexcols    = currentindex.getColumns();
            int matchCount = ordered ? set.getOrderedStartMatchCount(indexcols)
                                     : set.getStartMatchCount(indexcols);

            if (matchCount == 0) {
                continue;
            }

            if (matchCount == indexcols.length) {
                return currentindex;
            }

            if (matchCount > maxMatchCount) {
                maxMatchCount = matchCount;
                selected      = currentindex;
            }
        }

        if (selected != null) {
            return selected;
        }

        switch (tableType) {

//            case TableBase.MEMORY_TABLE :
            case TableBase.SYSTEM_SUBQUERY :
            case TableBase.SYSTEM_TABLE :
            case TableBase.VIEW_TABLE :
            case TableBase.TEMP_TABLE : {
                selected = createIndexForColumns(session, set.toArray());
            }
        }

        return selected;
    }

    /**
     *  Return the list of file pointers to root nodes for this table's
     *  indexes.
     */
    public final int[] getIndexRootsArray() {

        PersistentStore store =
            database.persistentStoreCollection.getStore(this);
        int[] roots = new int[indexList.length * 2 + 1];
        int   i     = 0;

        for (int index = 0; index < indexList.length; index++) {
            CachedObject accessor = store.getAccessor(indexList[index]);

            roots[i++] = accessor == null ? -1
                                          : accessor.getPos();
        }

        for (int index = 0; index < indexList.length; index++) {
            roots[i++] = indexList[index].sizeUnique(store);
        }

        roots[i] = indexList[0].size(null, store);

        return roots;
    }

    /**
     *  Sets the index roots of a cached/text table to specified file
     *  pointers. If a
     *  file pointer is -1 then the particular index root is null. A null index
     *  root signifies an empty table. Accordingly, all index roots should be
     *  null or all should be a valid file pointer/reference.
     */
    public void setIndexRoots(int[] roots) {

        if (!isCached) {
            throw Error.error(ErrorCode.X_42501, tableName.name);
        }

        PersistentStore store =
            database.persistentStoreCollection.getStore(this);
        int i = 0;

        for (int index = 0; index < indexList.length; index++) {
            store.setAccessor(indexList[index], roots[i++]);
        }

        int size = roots[indexList.length * 2];

        for (int index = 0; index < indexList.length; index++) {
            store.setElementCount(indexList[index], size, roots[i++]);
        }
    }

    /**
     *  Sets the index roots.
     */
    void setIndexRoots(Session session, String s) {

        if (!isCached) {
            throw Error.error(ErrorCode.X_42501, tableName.name);
        }

        ParserDQL p     = new ParserDQL(session, new Scanner(s));
        int[]     roots = new int[getIndexCount() * 2 + 1];

        p.read();

        int i = 0;

        for (int index = 0; index < getIndexCount(); index++) {
            int v = p.readInteger();

            roots[i++] = v;
        }

        try {
            for (int index = 0; index < getIndexCount() + 1; index++) {
                int v = p.readInteger();

                roots[i++] = v;
            }
        } catch (Exception e) {
            for (i = getIndexCount(); i < roots.length; i++) {
                roots[i] = -1;
            }
        }

        setIndexRoots(roots);
    }

    /**
     *  Mid level method for inserting single rows. Performs constraint checks and
     *  fires row level triggers.
     */
    Row insertSingleRow(Session session, PersistentStore store, Object[] data,
                        int[] changedCols) {

        if (identityColumn != -1) {
            setIdentityColumn(session, data);
        }

        if (hasGeneratedValues) {
            setGeneratedColumns(session, data);
        }

        if (hasDomainColumns || hasNotNullColumns) {
            enforceRowConstraints(session, data);
        }

        if (isView) {

            // may have domain column
            return null;
        }

        Row row = (Row) store.getNewCachedObject(session, data);

        store.indexRow(session, row);
        session.addInsertAction(this, row);

        return row;
    }

    /**
     * Multi-row insert method. Used for CREATE TABLE AS ... queries.
     */
    void insertIntoTable(Session session, Result result) {

        PersistentStore store = session.sessionData.getRowStore(this);
        RowSetNavigator nav   = result.initialiseNavigator();

        while (nav.hasNext()) {
            Object[] data = (Object[]) nav.getNext();
            Object[] newData =
                (Object[]) ArrayUtil.resizeArrayIfDifferent(data, columnCount);

            insertData(session, store, newData);
        }
    }

    /**
     *
     */
    public void insertNoCheckFromLog(Session session, Object[] data) {

        systemUpdateIdentityValue(data);

        PersistentStore store = session.sessionData.getRowStore(this);
        Row             row   = (Row) store.getNewCachedObject(session, data);

        store.indexRow(session, row);
        session.addInsertAction(this, row);
    }

    /**
     * Used for system table inserts. No checks. No identity
     * columns.
     */
    public int insertSys(PersistentStore store, Result ins) {

        RowSetNavigator nav   = ins.getNavigator();
        int             count = 0;

        while (nav.hasNext()) {
            insertSys(store, (Object[]) nav.getNext());

            count++;
        }

        return count;
    }

    /**
     * Used for subquery inserts. No checks. No identity
     * columns.
     */
    void insertResult(Session session, PersistentStore store, Result ins) {

        RowSetNavigator nav = ins.initialiseNavigator();

        while (nav.hasNext()) {
            Object[] data = (Object[]) nav.getNext();
            Object[] newData =
                (Object[]) ArrayUtil.resizeArrayIfDifferent(data, columnCount);

            insertData(session, store, newData);
        }
    }

    /**
     * Not for general use.
     * Used by ScriptReader to unconditionally insert a row into
     * the table when the .script file is read.
     */
    public void insertFromScript(Session session, PersistentStore store,
                                 Object[] data) {
        systemUpdateIdentityValue(data);
        insertData(session, store, data);
    }

    /**
     * For system operations outside transaction constrol
     */
    public void insertData(Session session, PersistentStore store,
                           Object[] data) {

        Row row = (Row) store.getNewCachedObject(null, data);

        store.indexRow(session, row);
    }

    /**
     * Used by the system tables only
     */
    public void insertSys(PersistentStore store, Object[] data) {

        Row row = (Row) store.getNewCachedObject(null, data);

        store.indexRow(null, row);
    }

    /**
     * If there is an identity or generated column in the table, sets
     * the value and/or adjusts the identiy value for the table.
     */
    protected void setIdentityColumn(Session session, Object[] data) {

        if (identityColumn != -1) {
            Number id = (Number) data[identityColumn];

            if (id == null) {
                id = (Number) identitySequence.getValueObject();
                data[identityColumn] = id;
            } else {
                identitySequence.userUpdate(id.longValue());
            }

            if (session != null) {
                session.setLastIdentity(id);
            }
        }
    }

    protected void setGeneratedColumns(Session session, Object[] data) {

        if (hasGeneratedValues) {
            for (int i = 0; i < colGenerated.length; i++) {
                if (colGenerated[i]) {
                    Expression e = getColumn(i).getGeneratingExpression();
                    RangeIteratorBase range =
                        session.sessionContext.getCheckIterator(
                            defaultRanges[0]);

                    range.currentData = data;
                    data[i]           = e.getValue(session, colTypes[i]);
                }
            }
        }
    }

    public void systemSetIdentityColumn(Session session, Object[] data) {

        if (identityColumn != -1) {
            Number id = (Number) data[identityColumn];

            if (id == null) {
                id = (Number) identitySequence.getValueObject();
                data[identityColumn] = id;
            } else {
                identitySequence.userUpdate(id.longValue());
            }
        }
    }

    /**
     * If there is an identity column in the table, sets
     * the max identity value.
     */
    protected void systemUpdateIdentityValue(Object[] data) {

        if (identityColumn != -1) {
            Number id = (Number) data[identityColumn];

            if (id != null) {
                identitySequence.systemUpdate(id.longValue());
            }
        }
    }

    /**
     * For log statements. Delete a single row.
     */
    public void deleteNoCheckFromLog(Session session, Object[] data) {

        Row             row   = null;
        PersistentStore store = session.sessionData.getRowStore(this);

        if (hasPrimaryKey()) {
            RowIterator it = getPrimaryIndex().findFirstRow(session, store,
                data, primaryKeyColsSequence);

            row = it.getNextRow();

            it.release();
        } else if (bestIndex == null) {
            RowIterator it = rowIterator(session);

            while (true) {
                row = it.getNextRow();

                if (row == null) {
                    break;
                }

                if (Table.compareRows(
                        session, row.getData(), data, defaultColumnMap,
                        colTypes) == 0) {
                    break;
                }
            }

            it.release();
        } else {
            RowIterator it = bestIndex.findFirstRow(session, store, data);

            while (true) {
                row = it.getNextRow();

                if (row == null) {
                    break;
                }

                Object[] rowdata = row.getData();

                // reached end of range
                if (bestIndex.compareRowNonUnique(
                        session, rowdata, data, bestIndex.getColumns()) != 0) {
                    row = null;

                    break;
                }

                if (Table.compareRows(
                        session, rowdata, data, defaultColumnMap,
                        colTypes) == 0) {
                    break;
                }
            }

            it.release();
        }

        if (row == null) {
            return;
        }

        session.addDeleteAction(this, row, null);
    }

    public void clearAllData(Session session) {

        super.clearAllData(session);

        if (identitySequence != null) {
            identitySequence.reset();
        }
    }

    public void clearAllData(PersistentStore store) {

        super.clearAllData(store);

        if (identitySequence != null) {
            identitySequence.reset();
        }
    }

    /**
     * Path used for INFORMATION_SCHEMA tables
     */
    public PersistentStore getRowStore(Session session) {

        if (tableType == TableBase.SYSTEM_TABLE) {
            database.dbInfo.getSystemTable(session, this.getName().name);

            return session.sessionData.getRowStore(this);
        }

        return store == null ? session.sessionData.getRowStore(this)
                             : store;
    }

    public SubQuery getSubQuery() {
        return null;
    }

    public QueryExpression getQueryExpression() {
        return null;
    }
}
TOP

Related Classes of org.hsqldb.Table

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.