Package org.hsqldb

Source Code of org.hsqldb.ParserDDL

/* 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.
*/


package org.hsqldb;

import java.lang.reflect.Method;

import org.hsqldb.HsqlNameManager.HsqlName;
import org.hsqldb.error.Error;
import org.hsqldb.error.ErrorCode;
import org.hsqldb.index.Index;
import org.hsqldb.lib.HsqlArrayList;
import org.hsqldb.lib.HsqlList;
import org.hsqldb.lib.Iterator;
import org.hsqldb.lib.OrderedHashSet;
import org.hsqldb.lib.OrderedIntHashSet;
import org.hsqldb.rights.Grantee;
import org.hsqldb.rights.GranteeManager;
import org.hsqldb.rights.Right;
import org.hsqldb.rights.User;
import org.hsqldb.store.ValuePool;
import org.hsqldb.types.Charset;
import org.hsqldb.types.Type;
import org.hsqldb.types.UserTypeModifier;

/**
* Parser for DDL statements
*
* @author Fred Toussi (fredt@users dot sourceforge.net)
* @version 1.9.0
* @since 1.9.0
*/
public class ParserDDL extends ParserRoutine {

    final static int[]   schemaCommands             = new int[] {
        Tokens.CREATE, Tokens.GRANT
    };
    final static short[] startStatementTokens       = new short[] {
        Tokens.CREATE, Tokens.GRANT, Tokens.ALTER, Tokens.DROP
    };
    final static short[] startStatementTokensSchema = new short[] {
        Tokens.CREATE, Tokens.GRANT,
    };

    ParserDDL(Session session, Scanner scanner) {
        super(session, scanner);
    }

    void reset(String sql) {
        super.reset(sql);
    }

    StatementSchema compileCreate() {

        int     tableType = TableBase.MEMORY_TABLE;
        boolean isTable   = false;

        read();

        switch (token.tokenType) {

            case Tokens.GLOBAL :
                read();
                readThis(Tokens.TEMPORARY);
                readIfThis(Tokens.MEMORY);
                readThis(Tokens.TABLE);

                isTable   = true;
                tableType = TableBase.TEMP_TABLE;
                break;

            case Tokens.TEMP :
                read();
                readThis(Tokens.TABLE);

                isTable   = true;
                tableType = TableBase.TEMP_TABLE;
                break;

            case Tokens.TEMPORARY :
                read();
                readThis(Tokens.TABLE);

                isTable   = true;
                tableType = TableBase.TEMP_TABLE;
                break;

            case Tokens.MEMORY :
                read();
                readThis(Tokens.TABLE);

                isTable = true;
                break;

            case Tokens.CACHED :
                read();
                readThis(Tokens.TABLE);

                isTable   = true;
                tableType = TableBase.CACHED_TABLE;
                break;

            case Tokens.TEXT :
                read();
                readThis(Tokens.TABLE);

                isTable   = true;
                tableType = TableBase.TEXT_TABLE;
                break;

            case Tokens.TABLE :
                read();

                isTable   = true;
                tableType = database.schemaManager.getDefaultTableType();
                break;

            default :
        }

        if (isTable) {
            return compileCreateTable(tableType);
        }

        switch (token.tokenType) {

            // other objects
            case Tokens.ALIAS :
                return compileCreateAlias();

            case Tokens.SEQUENCE :
                return compileCreateSequence();

            case Tokens.SCHEMA :
                return compileCreateSchema();

            case Tokens.TRIGGER :
                return compileCreateTrigger();

            case Tokens.USER :
                return compileCreateUser();

            case Tokens.ROLE :
                return compileCreateRole();

            case Tokens.VIEW :
                return compileCreateView(false);

            case Tokens.DOMAIN :
                return compileCreateDomain();

            case Tokens.TYPE :
                return compileCreateType();

            case Tokens.CHARACTER :
                return compileCreateCharacterSet();

            // index
            case Tokens.UNIQUE :
                read();
                checkIsThis(Tokens.INDEX);

                return compileCreateIndex(true);

            case Tokens.INDEX :
                return compileCreateIndex(false);

            case Tokens.AGGREGATE :
            case Tokens.FUNCTION :
            case Tokens.PROCEDURE :
                return compileCreateProcedureOrFunction();

            default : {
                throw unexpectedToken();
            }
        }
    }

    void processAlter() {

        session.setScripting(true);
        readThis(Tokens.ALTER);

        switch (token.tokenType) {

            case Tokens.TABLE : {
                read();
                processAlterTable();

                break;
            }
            case Tokens.DOMAIN : {
                read();
                processAlterDomain();

                break;
            }
            default : {
                throw unexpectedToken();
            }
        }
    }

    Statement compileAlter() {

        read();

        switch (token.tokenType) {

            case Tokens.INDEX : {
                read();

                HsqlName name = readNewSchemaObjectName(SchemaObject.INDEX,
                    true);

                readThis(Tokens.RENAME);
                readThis(Tokens.TO);

                return compileRenameObject(name, SchemaObject.INDEX);
            }
            case Tokens.SCHEMA : {
                read();

                HsqlName name = readSchemaName();

                readThis(Tokens.RENAME);
                readThis(Tokens.TO);

                return compileRenameObject(name, SchemaObject.SCHEMA);
            }
            case Tokens.CATALOG : {
                read();
                checkIsSimpleName();

                String name = token.tokenString;

                checkValidCatalogName(name);
                read();
                readThis(Tokens.RENAME);
                readThis(Tokens.TO);

                return compileRenameObject(database.getCatalogName(),
                                           SchemaObject.CATALOG);
            }
            case Tokens.SEQUENCE : {
                return compileAlterSequence();
            }
            case Tokens.TABLE : {
                return compileAlterTable();
            }
            case Tokens.USER : {
                return compileAlterUser();
            }
            case Tokens.DOMAIN : {
                return compileAlterDomain();
            }
            case Tokens.VIEW : {
                return compileCreateView(true);
            }
            default : {
                throw unexpectedToken();
            }
        }
    }

/*
    CompiledStatementInterface compileAlter() {

        CompiledStatementInterface cs = null;
        read();
        String sql = getStatement(getParsePosition(), endStatementTokensAlter);

        cs = new CompiledStatementSchema(sql, StatementCodes.ALTER_TYPE, null);

        return cs;
    }
*/
    Statement compileDrop() {

        int      objectTokenType;
        int      objectType;
        int      statementType;
        boolean  canCascade  = false;
        boolean  cascade     = false;
        boolean  useIfExists = false;
        boolean  ifExists    = false;
        HsqlName writeName   = null;
        HsqlName catalogName = database.getCatalogName();

        read();

        objectTokenType = this.token.tokenType;

        switch (objectTokenType) {

            case Tokens.INDEX : {
                read();

                statementType = StatementTypes.DROP_INDEX;
                objectType    = SchemaObject.INDEX;
                useIfExists   = true;
                writeName     = catalogName;

                break;
            }
            case Tokens.ASSERTION : {
                read();

                statementType = StatementTypes.DROP_ASSERTION;
                objectType    = SchemaObject.ASSERTION;
                canCascade    = true;

                break;
            }
            case Tokens.SPECIFIC : {
                read();

                switch (token.tokenType) {

                    case Tokens.ROUTINE :
                    case Tokens.PROCEDURE :
                    case Tokens.FUNCTION :
                        read();
                        break;

                    default :
                        throw unexpectedToken();
                }

                statementType = StatementTypes.DROP_ROUTINE;
                objectType    = SchemaObject.SPECIFIC_ROUTINE;
                writeName     = catalogName;
                canCascade    = true;
                useIfExists   = true;

                break;
            }
            case Tokens.PROCEDURE : {
                read();

                statementType = StatementTypes.DROP_ROUTINE;
                objectType    = SchemaObject.PROCEDURE;
                writeName     = catalogName;
                canCascade    = true;
                useIfExists   = true;

                break;
            }
            case Tokens.FUNCTION : {
                read();

                statementType = StatementTypes.DROP_ROUTINE;
                objectType    = SchemaObject.FUNCTION;
                writeName     = catalogName;
                canCascade    = true;
                useIfExists   = true;

                break;
            }
            case Tokens.SCHEMA : {
                read();

                statementType = StatementTypes.DROP_SCHEMA;
                objectType    = SchemaObject.SCHEMA;
                writeName     = catalogName;
                canCascade    = true;
                useIfExists   = true;

                break;
            }
            case Tokens.SEQUENCE : {
                read();

                statementType = StatementTypes.DROP_SEQUENCE;
                objectType    = SchemaObject.SEQUENCE;
                writeName     = catalogName;
                canCascade    = true;
                useIfExists   = true;

                break;
            }
            case Tokens.TRIGGER : {
                read();

                statementType = StatementTypes.DROP_TRIGGER;
                objectType    = SchemaObject.TRIGGER;
                writeName     = catalogName;
                canCascade    = false;
                useIfExists   = true;

                break;
            }
            case Tokens.USER : {
                read();

                statementType = StatementTypes.DROP_USER;
                objectType    = SchemaObject.GRANTEE;
                writeName     = catalogName;
                canCascade    = true;

                break;
            }
            case Tokens.ROLE : {
                read();

                statementType = StatementTypes.DROP_ROLE;
                objectType    = SchemaObject.GRANTEE;
                writeName     = catalogName;
                canCascade    = true;

                break;
            }
            case Tokens.DOMAIN :
                read();

                statementType = StatementTypes.DROP_DOMAIN;
                objectType    = SchemaObject.DOMAIN;
                writeName     = catalogName;
                canCascade    = true;
                useIfExists   = true;
                break;

            case Tokens.TYPE :
                read();

                statementType = StatementTypes.DROP_TYPE;
                objectType    = SchemaObject.TYPE;
                writeName     = catalogName;
                canCascade    = true;
                useIfExists   = true;
                break;

            case Tokens.CHARACTER :
                read();
                readThis(Tokens.SET);

                statementType = StatementTypes.DROP_CHARACTER_SET;
                objectType    = SchemaObject.CHARSET;
                writeName     = catalogName;
                canCascade    = false;
                useIfExists   = true;
                break;

            case Tokens.VIEW :
                read();

                statementType = StatementTypes.DROP_VIEW;
                objectType    = SchemaObject.VIEW;
                writeName     = catalogName;
                canCascade    = true;
                useIfExists   = true;
                break;

            case Tokens.TABLE :
                read();

                statementType = StatementTypes.DROP_TABLE;
                objectType    = SchemaObject.TABLE;
                writeName     = catalogName;
                canCascade    = true;
                useIfExists   = true;
                break;

            default :
                throw unexpectedToken();
        }

        if (useIfExists && token.tokenType == Tokens.IF) {
            int position = getPosition();

            read();

            if (token.tokenType == Tokens.EXISTS) {
                read();

                ifExists = true;
            } else {
                rewind(position);
            }
        }

        checkIsIdentifier();

        HsqlName name = null;

        switch (objectTokenType) {

            case Tokens.USER : {
                checkIsSimpleName();
                checkDatabaseUpdateAuthorisation();

                Grantee grantee =
                    database.getUserManager().get(token.tokenString);

                name = grantee.getName();

                read();

                break;
            }
            case Tokens.ROLE : {
                checkIsSimpleName();
                checkDatabaseUpdateAuthorisation();

                Grantee role =
                    database.getGranteeManager().getRole(token.tokenString);

                name = role.getName();

                read();

                break;
            }
            case Tokens.SCHEMA : {
                name      = readNewSchemaName();
                writeName = catalogName;

                break;
            }
            case Tokens.TABLE : {
                boolean isModule = token.namePrePrefix == null
                                   && Tokens.T_MODULE.equals(token.namePrefix);

                name = readNewSchemaObjectName(objectType, false);

                if (isModule) {
                    Object[] args = new Object[] {
                        name, Boolean.valueOf(ifExists)
                    };

                    return new StatementSession(StatementTypes.DROP_TABLE,
                                                args);
                }

                break;
            }
            default :
                name = readNewSchemaObjectName(objectType, false);
        }

        if (!ifExists && useIfExists && token.tokenType == Tokens.IF) {
            read();
            readThis(Tokens.EXISTS);

            ifExists = true;
        }

        if (canCascade) {
            if (token.tokenType == Tokens.CASCADE) {
                cascade = true;

                read();
            } else if (token.tokenType == Tokens.RESTRICT) {
                read();
            }
        }

        Object[] args = new Object[] {
            name, new Integer(objectType), Boolean.valueOf(cascade),
            Boolean.valueOf(ifExists)
        };
        String sql = getLastPart();
        Statement cs = new StatementSchema(sql, statementType, args, null,
                                           writeName);

        return cs;
    }

    private void processAlterTable() {

        String   tableName = token.tokenString;
        HsqlName schema    = session.getSchemaHsqlName(token.namePrefix);

        checkSchemaUpdateAuthorisation(schema);

        Table t = database.schemaManager.getUserTable(session, tableName,
            schema.name);

        if (t.isView()) {
            throw Error.error(ErrorCode.X_42501, tableName);
        }

        read();

        switch (token.tokenType) {

            case Tokens.RENAME : {
                read();
                readThis(Tokens.TO);
                processAlterTableRename(t);

                return;
            }
            case Tokens.ADD : {
                read();

                HsqlName cname = null;

                if (token.tokenType == Tokens.CONSTRAINT) {
                    read();

                    cname = readNewDependentSchemaObjectName(t.getName(),
                            SchemaObject.CONSTRAINT);

                    database.schemaManager.checkSchemaObjectNotExists(cname);
                }

                switch (token.tokenType) {

                    case Tokens.FOREIGN :
                        read();
                        readThis(Tokens.KEY);
                        processAlterTableAddForeignKeyConstraint(t, cname);

                        return;

                    case Tokens.UNIQUE :
                        read();
                        processAlterTableAddUniqueConstraint(t, cname);

                        return;

                    case Tokens.CHECK :
                        read();
                        processAlterTableAddCheckConstraint(t, cname);

                        return;

                    case Tokens.PRIMARY :
                        read();
                        readThis(Tokens.KEY);
                        processAlterTableAddPrimaryKey(t, cname);

                        return;

                    case Tokens.COLUMN :
                        if (cname != null) {
                            throw unexpectedToken();
                        }

                        read();
                        checkIsSimpleName();
                        processAlterTableAddColumn(t);

                        return;

                    default :
                        if (cname != null) {
                            throw unexpectedToken();
                        }

                        checkIsSimpleName();
                        processAlterTableAddColumn(t);

                        return;
                }
            }
            case Tokens.DROP : {
                read();

                switch (token.tokenType) {

                    case Tokens.PRIMARY : {
                        boolean cascade = false;

                        read();
                        readThis(Tokens.KEY);

                        if (token.tokenType == Tokens.CASCADE) {
                            read();

                            cascade = true;
                        }

                        if (t.hasPrimaryKey()) {
                            processAlterTableDropConstraint(
                                t, t.getPrimaryConstraint().getName().name,
                                cascade);
                        } else {
                            throw Error.error(ErrorCode.X_42501);
                        }

                        return;
                    }
                    case Tokens.CONSTRAINT : {
                        boolean cascade = false;

                        read();

                        SchemaObject object = readSchemaObjectName(t.getName(),
                            SchemaObject.CONSTRAINT);

                        if (token.tokenType == Tokens.RESTRICT) {
                            read();
                        } else if (token.tokenType == Tokens.CASCADE) {
                            read();

                            cascade = true;
                        }

                        processAlterTableDropConstraint(
                            t, object.getName().name, cascade);

//                        read();
                        return;
                    }
                    case Tokens.COLUMN :
                        read();

                    // fall through
                    default : {
                        checkIsSimpleName();

                        String  name    = token.tokenString;
                        boolean cascade = false;

                        read();

                        if (token.tokenType == Tokens.RESTRICT) {
                            read();
                        } else if (token.tokenType == Tokens.CASCADE) {
                            read();

                            cascade = true;
                        }

                        processAlterTableDropColumn(t, name, cascade);

                        return;
                    }
                }
            }
            case Tokens.ALTER : {
                read();

                if (token.tokenType == Tokens.COLUMN) {
                    read();
                }

                int          columnIndex = t.getColumnIndex(token.tokenString);
                ColumnSchema column      = t.getColumn(columnIndex);

                read();
                processAlterColumn(t, column, columnIndex);

                return;
            }
            default : {
                throw unexpectedToken();
            }
        }
    }

    Statement compileAlterTable() {

        read();

        String   tableName = token.tokenString;
        HsqlName schema    = session.getSchemaHsqlName(token.namePrefix);
        Table t = database.schemaManager.getUserTable(session, tableName,
            schema.name);

        read();

        switch (token.tokenType) {

            case Tokens.RENAME : {
                read();
                readThis(Tokens.TO);

                return compileRenameObject(t.getName(), SchemaObject.TABLE);
            }
            case Tokens.ADD : {
                read();

                HsqlName cname = null;

                if (token.tokenType == Tokens.CONSTRAINT) {
                    read();

                    cname = readNewDependentSchemaObjectName(t.getName(),
                            SchemaObject.CONSTRAINT);
                }

                switch (token.tokenType) {

                    case Tokens.FOREIGN :
                        read();
                        readThis(Tokens.KEY);

                        return compileAlterTableAddForeignKeyConstraint(t,
                                cname);

                    case Tokens.UNIQUE :
                        read();

                        return compileAlterTableAddUniqueConstraint(t, cname);

                    case Tokens.CHECK :
                        read();

                        return compileAlterTableAddCheckConstraint(t, cname);

                    case Tokens.PRIMARY :
                        read();
                        readThis(Tokens.KEY);

                        return compileAlterTableAddPrimaryKey(t, cname);

                    case Tokens.COLUMN :
                        if (cname != null) {
                            throw unexpectedToken();
                        }

                        read();
                        checkIsSimpleName();

                        return compileAlterTableAddColumn(t);

                    default :
                        if (cname != null) {
                            throw unexpectedToken();
                        }

                        checkIsSimpleName();

                        return compileAlterTableAddColumn(t);
                }
            }
            case Tokens.DROP : {
                read();

                switch (token.tokenType) {

                    case Tokens.PRIMARY : {
                        boolean cascade = false;

                        read();
                        readThis(Tokens.KEY);

                        return compileAlterTableDropPrimaryKey(t);
                    }
                    case Tokens.CONSTRAINT : {
                        read();

                        return compileAlterTableDropConstraint(t);
                    }
                    case Tokens.COLUMN :
                        read();

                    // fall through
                    default : {
                        checkIsSimpleName();

                        String  name    = token.tokenString;
                        boolean cascade = false;

                        read();

                        if (token.tokenType == Tokens.RESTRICT) {
                            read();
                        } else if (token.tokenType == Tokens.CASCADE) {
                            read();

                            cascade = true;
                        }

                        return compileAlterTableDropColumn(t, name, cascade);
                    }
                }
            }
            case Tokens.ALTER : {
                read();

                if (token.tokenType == Tokens.COLUMN) {
                    read();
                }

                int          columnIndex = t.getColumnIndex(token.tokenString);
                ColumnSchema column      = t.getColumn(columnIndex);

                read();

                return compileAlterColumn(t, column, columnIndex);
            }
            default : {
                throw unexpectedToken();
            }
        }
    }

    private Statement compileAlterTableDropConstraint(Table t) {

        boolean cascade = false;
        SchemaObject object = readSchemaObjectName(t.getSchemaName(),
            SchemaObject.CONSTRAINT);

        if (token.tokenType == Tokens.RESTRICT) {
            read();
        } else if (token.tokenType == Tokens.CASCADE) {
            read();

            cascade = true;
        }

        Object[] args = new Object[] {
            object.getName(), ValuePool.getInt(SchemaObject.CONSTRAINT),
            Boolean.valueOf(cascade), Boolean.valueOf(false)
        };
        String sql = getLastPart();
        Statement cs = new StatementSchema(sql,
                                           StatementTypes.DROP_CONSTRAINT,
                                           args);

        cs.writeTableNames = getReferenceArray(t.getName(), cascade);

        return cs;
    }

    private Statement compileAlterTableDropPrimaryKey(Table t) {

        boolean cascade = false;

        if (token.tokenType == Tokens.RESTRICT) {
            read();
        } else if (token.tokenType == Tokens.CASCADE) {
            read();

            cascade = true;
        }

        if (!t.hasPrimaryKey()) {
            throw Error.error(ErrorCode.X_42501);
        }

        SchemaObject object = t.getPrimaryConstraint();
        Object[]     args   = new Object[] {
            object.getName(), ValuePool.getInt(SchemaObject.CONSTRAINT),
            Boolean.valueOf(cascade), Boolean.valueOf(false)
        };
        String sql = getLastPart();
        Statement cs = new StatementSchema(sql,
                                           StatementTypes.DROP_CONSTRAINT,
                                           args);

        cs.writeTableNames = getReferenceArray(t.getName(), cascade);

        return cs;
    }

    HsqlName[] getReferenceArray(HsqlName objectName, boolean cascade) {

        if (cascade) {
            OrderedHashSet names = new OrderedHashSet();

            database.schemaManager.getCascadingReferencingObjectNames(
                objectName, names);

            Iterator it = names.iterator();

            while (it.hasNext()) {
                HsqlName name = (HsqlName) it.next();

                if (name.type != SchemaObject.TABLE) {
                    it.remove();
                }
            }

            names.add(objectName);

            HsqlName[] array = new HsqlName[names.size()];

            names.toArray(array);

            return array;
        } else {
            return new HsqlName[]{ objectName };
        }
    }

    StatementSession compileDeclareLocalTableOrNull() {

        int position = super.getPosition();

        try {
            readThis(Tokens.DECLARE);
            readThis(Tokens.LOCAL);
            readThis(Tokens.TEMPORARY);
            readThis(Tokens.TABLE);
        } catch (Exception e) {

            // may be cursor
            rewind(position);

            return null;
        }

        if (token.namePrePrefix != null) {
            throw unexpectedToken();
        }

        if (token.namePrePrefix == null
                && (token.namePrefix == null
                    || Tokens.T_MODULE.equals(token.namePrefix))) {

            // valid name
        } else {
            throw unexpectedToken();
        }

        HsqlName name = readNewSchemaObjectName(SchemaObject.TABLE, false);

        name.schema = SqlInvariants.MODULE_HSQLNAME;

        Table table = TableUtil.newTable(database, TableBase.TEMP_TABLE, name);
        StatementSchema cs          = compileCreateTableBody(table);
        HsqlArrayList   constraints = (HsqlArrayList) cs.arguments[1];

        for (int i = 0; i < constraints.size(); i++) {
            Constraint c = (Constraint) constraints.get(i);

            if (c.getConstraintType()
                    == SchemaObject.ConstraintTypes.FOREIGN_KEY) {
                throw unexpectedToken(Tokens.T_FOREIGN);
            }
        }

        StatementSession ss =
            new StatementSession(StatementTypes.DECLARE_SESSION_TABLE,
                                 cs.arguments);

        return ss;
    }

    StatementSchema compileCreateTable(int type) {

        HsqlName name = readNewSchemaObjectName(SchemaObject.TABLE, false);

        name.setSchemaIfNull(session.getCurrentSchemaHsqlName());

        Table table = TableUtil.newTable(database, type, name);

        return compileCreateTableBody(table);
    }

    StatementSchema compileCreateTableBody(Table table) {

        HsqlArrayList tempConstraints = new HsqlArrayList();

        if (token.tokenType == Tokens.AS) {
            return readTableAsSubqueryDefinition(table);
        }

        int position = getPosition();

        readThis(Tokens.OPENBRACKET);

        {
            Constraint c = new Constraint(null, null,
                                          SchemaObject.ConstraintTypes.TEMP);

            tempConstraints.add(c);
        }

        boolean start     = true;
        boolean startPart = true;
        boolean end       = false;

        while (!end) {
            switch (token.tokenType) {

                case Tokens.LIKE : {
                    ColumnSchema[] likeColumns = readLikeTable(table);

                    for (int i = 0; i < likeColumns.length; i++) {
                        table.addColumn(likeColumns[i]);
                    }

                    start     = false;
                    startPart = false;

                    break;
                }
                case Tokens.CONSTRAINT :
                case Tokens.PRIMARY :
                case Tokens.FOREIGN :
                case Tokens.UNIQUE :
                case Tokens.CHECK :
                    if (!startPart) {
                        throw unexpectedToken();
                    }

                    readConstraint(table, tempConstraints);

                    start     = false;
                    startPart = false;
                    break;

                case Tokens.COMMA :
                    if (startPart) {
                        throw unexpectedToken();
                    }

                    read();

                    startPart = true;
                    break;

                case Tokens.CLOSEBRACKET :
                    read();

                    end = true;
                    break;

                default :
                    if (!startPart) {
                        throw unexpectedToken();
                    }

                    checkIsSchemaObjectName();

                    HsqlName hsqlName =
                        database.nameManager.newColumnHsqlName(table.getName(),
                            token.tokenString, isDelimitedIdentifier());

                    read();

                    ColumnSchema newcolumn = readColumnDefinitionOrNull(table,
                        hsqlName, tempConstraints);

                    if (newcolumn == null) {
                        if (start) {
                            rewind(position);

                            return readTableAsSubqueryDefinition(table);
                        } else {
                            throw Error.error(ErrorCode.X_42000);
                        }
                    }

                    table.addColumn(newcolumn);

                    start     = false;
                    startPart = false;
            }
        }

        if (token.tokenType == Tokens.ON) {
            if (!table.isTemp()) {
                throw unexpectedToken();
            }

            read();
            readThis(Tokens.COMMIT);

            if (token.tokenType == Tokens.DELETE) {}
            else if (token.tokenType == Tokens.PRESERVE) {
                table.persistenceScope = TableBase.SCOPE_SESSION;
            }

            read();
            readThis(Tokens.ROWS);
        }

        Object[] args = new Object[] {
            table, tempConstraints, null
        };
        String   sql  = getLastPart();

        return new StatementSchema(sql, StatementTypes.CREATE_TABLE, args);
    }

    private ColumnSchema[] readLikeTable(Table table) {

        read();

        boolean           generated = false;
        boolean           identity  = false;
        boolean           defaults  = false;
        Table             likeTable = readTableName();
        OrderedIntHashSet set       = new OrderedIntHashSet();

        while (true) {
            boolean including = token.tokenType == Tokens.INCLUDING;

            if (!including && token.tokenType != Tokens.EXCLUDING) {
                break;
            }

            read();

            switch (token.tokenType) {

                case Tokens.GENERATED :
                    if (!set.add(token.tokenType)) {
                        throw unexpectedToken();
                    }

                    generated = including;
                    break;

                case Tokens.IDENTITY :
                    if (!set.add(token.tokenType)) {
                        throw unexpectedToken();
                    }

                    identity = including;
                    break;

                case Tokens.DEFAULTS :
                    if (!set.add(token.tokenType)) {
                        throw unexpectedToken();
                    }

                    defaults = including;
                    break;

                default :
                    throw unexpectedToken();
            }

            read();
        }

        ColumnSchema[] columnList =
            new ColumnSchema[likeTable.getColumnCount()];

        for (int i = 0; i < columnList.length; i++) {
            ColumnSchema column = likeTable.getColumn(i).duplicate();
            HsqlName name =
                database.nameManager.newColumnSchemaHsqlName(table.getName(),
                    column.getName());

            column.setName(name);
            column.setNullable(true);
            column.setPrimaryKey(false);

            if (identity) {
                if (column.isIdentity()) {
                    column.setIdentity(
                        column.getIdentitySequence().duplicate());
                }
            } else {
                column.setIdentity(null);
            }

            if (!defaults) {
                column.setDefaultExpression(null);
            }

            if (!generated) {
                column.setGeneratingExpression(null);
            }

            columnList[i] = column;
        }

        return columnList;
    }

    StatementSchema readTableAsSubqueryDefinition(Table table) {

        HsqlName   readName    = null;
        boolean    withData    = true;
        HsqlName[] columnNames = null;
        Statement  statement   = null;

        if (token.tokenType == Tokens.OPENBRACKET) {
            columnNames = readColumnNames(table.getName());
        }

        readThis(Tokens.AS);
        readThis(Tokens.OPENBRACKET);

        QueryExpression queryExpression = XreadQueryExpression();

        queryExpression.setReturningResult();
        queryExpression.resolve(session);
        readThis(Tokens.CLOSEBRACKET);
        readThis(Tokens.WITH);

        if (token.tokenType == Tokens.NO) {
            read();

            withData = false;
        } else if (table.getTableType() == TableBase.TEXT_TABLE) {
            throw unexpectedTokenRequire(Tokens.T_NO);
        }

        readThis(Tokens.DATA);

        if (token.tokenType == Tokens.ON) {
            if (!table.isTemp()) {
                throw unexpectedToken();
            }

            read();
            readThis(Tokens.COMMIT);

            if (token.tokenType == Tokens.DELETE) {}
            else if (token.tokenType == Tokens.PRESERVE) {
                table.persistenceScope = TableBase.SCOPE_SESSION;
            }

            read();
            readThis(Tokens.ROWS);
        }

        if (columnNames == null) {
            columnNames = queryExpression.getResultColumnNames();
        } else {
            if (columnNames.length != queryExpression.getColumnCount()) {
                throw Error.error(ErrorCode.X_42593);
            }
        }

        TableUtil.setColumnsInSchemaTable(table, columnNames,
                                          queryExpression.getColumnTypes());
        table.createPrimaryKey();

        if (withData) {
            statement = new StatementQuery(session, queryExpression,
                                           compileContext);
            readName = statement.getTableNamesForRead()[0];
        }

        Object[] args = new Object[] {
            table, null, statement
        };
        String   sql  = getLastPart();
        StatementSchema st = new StatementSchema(sql,
            StatementTypes.CREATE_TABLE, args, readName, null);

        return st;
    }

    /**
     * Adds a list of temp constraints to a new table
     */
    static Table addTableConstraintDefinitions(Session session, Table table,
            HsqlArrayList tempConstraints, HsqlArrayList constraintList,
            boolean addToSchema) {

        Constraint c        = (Constraint) tempConstraints.get(0);
        String     namePart = c.getName() == null ? null
                                                  : c.getName().name;
        HsqlName indexName = session.database.nameManager.newAutoName("IDX",
            namePart, table.getSchemaName(), table.getName(),
            SchemaObject.INDEX);

        c.setColumnsIndexes(table);
        table.createPrimaryKey(indexName, c.core.mainCols, true);

        if (c.core.mainCols != null) {
            Constraint newconstraint = new Constraint(c.getName(), table,
                table.getPrimaryIndex(),
                SchemaObject.ConstraintTypes.PRIMARY_KEY);

            table.addConstraint(newconstraint);

            if (addToSchema) {
                session.database.schemaManager.addSchemaObject(newconstraint);
            }
        }

        for (int i = 1; i < tempConstraints.size(); i++) {
            c = (Constraint) tempConstraints.get(i);

            switch (c.constType) {

                case SchemaObject.ConstraintTypes.UNIQUE : {
                    c.setColumnsIndexes(table);

                    if (table.getUniqueConstraintForColumns(c.core.mainCols)
                            != null) {
                        throw Error.error(ErrorCode.X_42522);
                    }

                    // create an autonamed index
                    indexName = session.database.nameManager.newAutoName("IDX",
                            c.getName().name, table.getSchemaName(),
                            table.getName(), SchemaObject.INDEX);

                    Index index = table.createAndAddIndexStructure(session,
                        indexName, c.core.mainCols, null, null, true, true,
                        false);
                    Constraint newconstraint = new Constraint(c.getName(),
                        table, index, SchemaObject.ConstraintTypes.UNIQUE);

                    table.addConstraint(newconstraint);

                    if (addToSchema) {
                        session.database.schemaManager.addSchemaObject(
                            newconstraint);
                    }

                    break;
                }
                case SchemaObject.ConstraintTypes.FOREIGN_KEY : {
                    addForeignKey(session, table, c, constraintList);

                    break;
                }
                case SchemaObject.ConstraintTypes.CHECK : {
                    try {
                        c.prepareCheckConstraint(session, table, false);
                    } catch (HsqlException e) {
                        if (session.isProcessingScript()) {
                            break;
                        }

                        throw e;
                    }

                    table.addConstraint(c);

                    if (c.isNotNull()) {
                        ColumnSchema column =
                            table.getColumn(c.notNullColumnIndex);

                        column.setNullable(false);
                        table.setColumnTypeVars(c.notNullColumnIndex);
                    }

                    if (addToSchema) {
                        session.database.schemaManager.addSchemaObject(c);
                    }

                    break;
                }
            }
        }

        return table;
    }

    static void addForeignKey(Session session, Table table, Constraint c,
                              HsqlArrayList constraintList) {

        HsqlName mainTableName = c.getMainTableName();

        if (mainTableName == table.getName()) {
            c.core.mainTable = table;
        } else {
            Table mainTable =
                session.database.schemaManager.findUserTable(session,
                    mainTableName.name, mainTableName.schema.name);

            if (mainTable == null) {
                if (constraintList == null) {
                    throw Error.error(ErrorCode.X_42501, mainTableName.name);
                }

                constraintList.add(c);

                return;
            }

            c.core.mainTable = mainTable;
        }

        c.setColumnsIndexes(table);

        Constraint uniqueConstraint =
            c.core.mainTable.getUniqueConstraintForColumns(c.core.mainCols,
                c.core.refCols);

        if (uniqueConstraint == null) {
            throw Error.error(ErrorCode.X_42523);
        }

        Index      mainIndex  = uniqueConstraint.getMainIndex();
        TableWorks tableWorks = new TableWorks(session, table);

        tableWorks.checkCreateForeignKey(c);

        boolean isForward = c.core.mainTable.getSchemaName()
                            != table.getSchemaName();
        int offset = session.database.schemaManager.getTableIndex(table);

        if (offset != -1
                && offset
                   < session.database.schemaManager.getTableIndex(
                       c.core.mainTable)) {
            isForward = true;
        }

        HsqlName refIndexName = session.database.nameManager.newAutoName("IDX",
            table.getSchemaName(), table.getName(), SchemaObject.INDEX);
        Index index = table.createAndAddIndexStructure(session, refIndexName,
            c.core.refCols, null, null, false, true, isForward);
        HsqlName mainName = session.database.nameManager.newAutoName("REF",
            c.getName().name, table.getSchemaName(), table.getName(),
            SchemaObject.INDEX);

        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   = index;
        c.isForward       = isForward;

        table.addConstraint(c);
        c.core.mainTable.addConstraint(new Constraint(mainName, c));
        session.database.schemaManager.addSchemaObject(c);
    }

    private Constraint readFKReferences(Table refTable,
                                        HsqlName constraintName,
                                        OrderedHashSet refColSet) {

        HsqlName       mainTableName;
        OrderedHashSet mainColSet = null;

        readThis(Tokens.REFERENCES);

        HsqlName schema;

        if (token.namePrefix == null) {
            schema = refTable.getSchemaName();
        } else {
            schema =
                database.schemaManager.getSchemaHsqlName(token.namePrefix);
        }

        if (refTable.getSchemaName() == schema
                && refTable.getName().name.equals(token.tokenString)) {
            mainTableName = refTable.getName();

            read();
        } else {
            mainTableName = readFKTableName(schema);
        }

        if (token.tokenType == Tokens.OPENBRACKET) {
            mainColSet = readColumnNames(false);
        } else {

            // columns are resolved in the calling method
            if (mainTableName == refTable.getName()) {

                // fredt - FK statement is part of CREATE TABLE and is self-referencing
                // reference must be to same table being created
            } else {
/*
                if (!mainTable.hasPrimaryKey()) {
                    throw Trace.error(Trace.CONSTRAINT_NOT_FOUND,
                                      Trace.TABLE_HAS_NO_PRIMARY_KEY);

                }
*/
            }
        }

        int matchType = OpTypes.MATCH_SIMPLE;

        if (token.tokenType == Tokens.MATCH) {
            read();

            switch (token.tokenType) {

                case Tokens.SIMPLE :
                    read();
                    break;

                case Tokens.PARTIAL :
                    throw super.unsupportedFeature();
                case Tokens.FULL :
                    read();

                    matchType = OpTypes.MATCH_FULL;
                    break;

                default :
                    throw unexpectedToken();
            }
        }

        // -- In a while loop we parse a maximium of two
        // -- "ON" statements following the foreign key
        // -- definition this can be
        // -- ON [UPDATE|DELETE] [NO ACTION|RESTRICT|CASCADE|SET [NULL|DEFAULT]]
        int deleteAction      = SchemaObject.ReferentialAction.NO_ACTION;
        int updateAction      = SchemaObject.ReferentialAction.NO_ACTION;
        OrderedIntHashSet set = new OrderedIntHashSet();

        while (token.tokenType == Tokens.ON) {
            read();

            if (!set.add(token.tokenType)) {
                throw unexpectedToken();
            }

            if (token.tokenType == Tokens.DELETE) {
                read();

                if (token.tokenType == Tokens.SET) {
                    read();

                    switch (token.tokenType) {

                        case Tokens.DEFAULT : {
                            read();

                            deleteAction =
                                SchemaObject.ReferentialAction.SET_DEFAULT;

                            break;
                        }
                        case Tokens.NULL :
                            read();

                            deleteAction =
                                SchemaObject.ReferentialAction.SET_NULL;
                            break;

                        default :
                            throw unexpectedToken();
                    }
                } else if (token.tokenType == Tokens.CASCADE) {
                    read();

                    deleteAction = SchemaObject.ReferentialAction.CASCADE;
                } else if (token.tokenType == Tokens.RESTRICT) {
                    read();
                } else {
                    readThis(Tokens.NO);
                    readThis(Tokens.ACTION);
                }
            } else if (token.tokenType == Tokens.UPDATE) {
                read();

                if (token.tokenType == Tokens.SET) {
                    read();

                    switch (token.tokenType) {

                        case Tokens.DEFAULT : {
                            read();

                            deleteAction =
                                SchemaObject.ReferentialAction.SET_DEFAULT;

                            break;
                        }
                        case Tokens.NULL :
                            read();

                            deleteAction =
                                SchemaObject.ReferentialAction.SET_NULL;
                            break;

                        default :
                            throw unexpectedToken();
                    }
                } else if (token.tokenType == Tokens.CASCADE) {
                    read();

                    updateAction = SchemaObject.ReferentialAction.CASCADE;
                } else if (token.tokenType == Tokens.RESTRICT) {
                    read();
                } else {
                    readThis(Tokens.NO);
                    readThis(Tokens.ACTION);
                }
            } else {
                throw unexpectedToken();
            }
        }

        if (constraintName == null) {
            constraintName = database.nameManager.newAutoName("FK",
                    refTable.getSchemaName(), refTable.getName(),
                    SchemaObject.CONSTRAINT);
        }

        return new Constraint(constraintName, refTable.getName(), refColSet,
                              mainTableName, mainColSet,
                              SchemaObject.ConstraintTypes.FOREIGN_KEY,
                              deleteAction, updateAction, matchType);
    }

    private HsqlName readFKTableName(HsqlName schema) {

        HsqlName name;

        checkIsSchemaObjectName();

        Table table = database.schemaManager.findUserTable(session,
            token.tokenString, schema.name);

        if (table == null) {
            name = database.nameManager.newHsqlName(schema, token.tokenString,
                    isDelimitedIdentifier(), SchemaObject.TABLE);
        } else {
            name = table.getName();
        }

        read();

        return name;
    }

    StatementSchema compileCreateView(boolean alter) {

        read();

        HsqlName name = readNewSchemaObjectName(SchemaObject.VIEW, true);

        name.setSchemaIfNull(session.getCurrentSchemaHsqlName());
        checkSchemaUpdateAuthorisation(name.schema);

        HsqlName[] colList = null;

        if (token.tokenType == Tokens.OPENBRACKET) {
            colList = readColumnNames(name);
        }

        readThis(Tokens.AS);
        startRecording();

        int             position = getPosition();
        QueryExpression queryExpression;

        try {
            queryExpression = XreadQueryExpression();
        } catch (HsqlException e) {
            queryExpression = XreadJoinedTable();
        }

        Token[] tokenisedStatement = getRecordedStatement();
        int     check              = SchemaObject.ViewCheckModes.CHECK_NONE;

        if (token.tokenType == Tokens.WITH) {
            read();

            check = SchemaObject.ViewCheckModes.CHECK_CASCADE;

            if (readIfThis(Tokens.LOCAL)) {
                check = SchemaObject.ViewCheckModes.CHECK_LOCAL;
            } else {
                readIfThis(Tokens.CASCADED);
            }

            readThis(Tokens.CHECK);
            readThis(Tokens.OPTION);
        }

        View view = new View(database, name, colList, check);

        queryExpression.setView(view);
        queryExpression.resolve(session);
        view.setStatement(Token.getSQL(tokenisedStatement));

        String          fullSQL = getLastPart();
        Object[]        args    = new Object[]{ view };
        int             type    = alter ? StatementTypes.ALTER_VIEW
                                        : StatementTypes.CREATE_VIEW;
        StatementSchema cs      = new StatementSchema(fullSQL, type, args);
        StatementQuery s = new StatementQuery(session, queryExpression,
                                              compileContext);

        cs.readTableNames = s.readTableNames;

        return cs;
    }

    StatementSchema compileCreateSequence() {

        read();

        /*
                CREATE SEQUENCE <name>
                [AS {INTEGER | BIGINT}]
                [START WITH <value>]
                [INCREMENT BY <value>]
        */
        HsqlName name = readNewSchemaObjectName(SchemaObject.SEQUENCE, false);
        NumberSequence sequence = new NumberSequence(name, Type.SQL_INTEGER);

        readSequenceOptions(sequence, true, false);

        String   sql  = getLastPart();
        Object[] args = new Object[]{ sequence };

        return new StatementSchema(sql, StatementTypes.CREATE_SEQUENCE, args);
    }

    StatementSchema compileCreateDomain() {

        UserTypeModifier userTypeModifier = null;
        HsqlName         name;

        read();

        name = readNewSchemaObjectName(SchemaObject.DOMAIN, false);

        readIfThis(Tokens.AS);

        Type       type          = readTypeDefinition(false).duplicate();
        Expression defaultClause = null;

        if (readIfThis(Tokens.DEFAULT)) {
            defaultClause = readDefaultClause(type);
        }

        userTypeModifier = new UserTypeModifier(name, SchemaObject.DOMAIN,
                type);

        userTypeModifier.setDefaultClause(defaultClause);

        type.userTypeModifier = userTypeModifier;

        HsqlArrayList tempConstraints = new HsqlArrayList();

        compileContext.currentDomain = type;

        while (true) {
            boolean end = false;

            switch (token.tokenType) {

                case Tokens.CONSTRAINT :
                case Tokens.CHECK :
                    readConstraint(type, tempConstraints);
                    break;

                default :
                    end = true;
                    break;
            }

            if (end) {
                break;
            }
        }

        compileContext.currentDomain = null;

        for (int i = 0; i < tempConstraints.size(); i++) {
            Constraint c = (Constraint) tempConstraints.get(i);

            c.prepareCheckConstraint(session, null, false);
            userTypeModifier.addConstraint(c);
        }

        String   sql  = getLastPart();
        Object[] args = new Object[]{ type };

        return new StatementSchema(sql, StatementTypes.CREATE_DOMAIN, args);
    }

    StatementSchema compileCreateType() {

        read();

        HsqlName name = readNewSchemaObjectName(SchemaObject.TYPE, false);

        readThis(Tokens.AS);

        Type type = readTypeDefinition(false).duplicate();

        readIfThis(Tokens.FINAL);

        UserTypeModifier userTypeModifier = new UserTypeModifier(name,
            SchemaObject.TYPE, type);

        type.userTypeModifier = userTypeModifier;

        String   sql  = getLastPart();
        Object[] args = new Object[]{ type };

        return new StatementSchema(sql, StatementTypes.CREATE_TYPE, args);
    }

    StatementSchema compileCreateCharacterSet() {

        read();
        readThis(Tokens.SET);

        HsqlName name = readNewSchemaObjectName(SchemaObject.CHARSET, false);

        readIfThis(Tokens.AS);
        readThis(Tokens.GET);

        String schema = session.getSchemaName(token.namePrefix);
        Charset source =
            (Charset) database.schemaManager.getSchemaObject(token.tokenString,
                schema, SchemaObject.CHARSET);

        read();

        if (token.tokenType == Tokens.COLLATION) {
            read();
            readThis(Tokens.FROM);
            readThis(Tokens.DEFAULT);
        }

        Charset charset = new Charset(name);

        charset.base = source.getName();

        String   sql  = getLastPart();
        Object[] args = new Object[]{ charset };

        return new StatementSchema(sql, StatementTypes.CREATE_CHARACTER_SET,
                                   args);
    }

    StatementSchema compileCreateAlias() {

        HsqlName  name     = null;
        Routine[] routines = null;
        String    alias;
        String    methodFQN = null;

        if (!session.isProcessingScript()) {
            throw super.unsupportedFeature();
        }

        read();

        try {
            alias = token.tokenString;

            read();
            readThis(Tokens.FOR);

            methodFQN = token.tokenString;

            read();
        } catch (HsqlException e) {
            alias = null;
        }

        if (alias != null) {
            HsqlName schema =
                database.schemaManager.getDefaultSchemaHsqlName();

            name = database.nameManager.newHsqlName(schema, alias,
                    SchemaObject.FUNCTION);

            Method[] methods = Routine.getMethods(methodFQN);

            routines = Routine.newRoutines(session, methods);
        }

        String   sql  = getLastPart();
        Object[] args = new Object[] {
            name, routines
        };

        return new StatementSchema(sql, StatementTypes.CREATE_ALIAS, args);
    }

    StatementSchema compileCreateTrigger() {

        Table          table;
        Boolean        isForEachRow = null;
        boolean        isNowait     = false;
        boolean        hasQueueSize = false;
        int            queueSize    = 0;
        int            beforeOrAfterType;
        int            operationType;
        String         className;
        TriggerDef     td;
        HsqlName       name;
        HsqlName       otherName           = null;
        OrderedHashSet columns             = null;
        int[]          updateColumnIndexes = null;

        read();

        name = readNewSchemaObjectName(SchemaObject.TRIGGER, true);

        switch (token.tokenType) {

            case Tokens.INSTEAD :
                beforeOrAfterType = TriggerDef.getTiming(Tokens.INSTEAD);

                read();
                readThis(Tokens.OF);
                break;

            case Tokens.BEFORE :
            case Tokens.AFTER :
                beforeOrAfterType = TriggerDef.getTiming(token.tokenType);

                read();
                break;

            default :
                throw unexpectedToken();
        }

        switch (token.tokenType) {

            case Tokens.INSERT :
            case Tokens.DELETE :
                operationType = TriggerDef.getOperationType(token.tokenType);

                read();
                break;

            case Tokens.UPDATE :
                operationType = TriggerDef.getOperationType(token.tokenType);

                read();

                if (token.tokenType == Tokens.OF
                        && beforeOrAfterType != TriggerDef.INSTEAD) {
                    read();

                    columns = new OrderedHashSet();

                    readColumnNameList(columns, null, false);
                }
                break;

            default :
                throw unexpectedToken();
        }

        readThis(Tokens.ON);

        table = readTableName();

        if (token.tokenType == Tokens.BEFORE) {
            read();
            checkIsSimpleName();

            otherName = readNewSchemaObjectName(SchemaObject.TRIGGER, true);
        }

        name.setSchemaIfNull(table.getSchemaName());
        checkSchemaUpdateAuthorisation(name.schema);

        if (beforeOrAfterType == TriggerDef.INSTEAD) {
            if (!table.isView()
                    || ((View) table).getCheckOption()
                       == SchemaObject.ViewCheckModes.CHECK_CASCADE) {
                throw Error.error(ErrorCode.X_42538, name.schema.name);
            }
        } else {
            if (table.isView()) {
                throw Error.error(ErrorCode.X_42538, name.schema.name);
            }
        }

        if (name.schema != table.getSchemaName()) {
            throw Error.error(ErrorCode.X_42505, name.schema.name);
        }

        name.parent = table.getName();

        database.schemaManager.checkSchemaObjectNotExists(name);

        if (columns != null) {
            updateColumnIndexes = table.getColumnIndexes(columns);

            for (int i = 0; i < updateColumnIndexes.length; i++) {
                if (updateColumnIndexes[i] == -1) {
                    throw Error.error(ErrorCode.X_42544,
                                      (String) columns.get(i));
                }
            }
        }

        Expression      condition    = null;
        String          oldTableName = null;
        String          newTableName = null;
        String          oldRowName   = null;
        String          newRowName   = null;
        Table[]         transitions  = new Table[4];
        RangeVariable[] rangeVars    = new RangeVariable[4];
        String          conditionSQL = null;

        if (token.tokenType == Tokens.REFERENCING) {
            read();

            if (token.tokenType != Tokens.OLD
                    && token.tokenType != Tokens.NEW) {
                throw unexpectedToken();
            }

            while (true) {
                if (token.tokenType == Tokens.OLD) {
                    if (operationType == StatementTypes.INSERT) {
                        throw unexpectedToken();
                    }

                    read();

                    if (token.tokenType == Tokens.TABLE) {
                        if (Boolean.TRUE.equals(isForEachRow)
                                || oldTableName != null
                                || beforeOrAfterType == TriggerDef.BEFORE) {
                            throw unexpectedToken();
                        }

                        read();
                        readIfThis(Tokens.AS);
                        checkIsSimpleName();
                        read();

                        oldTableName = token.tokenString;

                        String n = oldTableName;

                        if (n.equals(newTableName) || n.equals(oldRowName)
                                || n.equals(newRowName)) {
                            throw unexpectedToken();
                        }

                        isForEachRow = Boolean.FALSE;

                        HsqlName hsqlName = database.nameManager.newHsqlName(
                            table.getSchemaName(), n, isDelimitedIdentifier(),
                            SchemaObject.TRANSITION);
                        Table transition = new Table(table, hsqlName);
                        RangeVariable range = new RangeVariable(transition,
                            null, null, null, compileContext);

                        transitions[TriggerDef.OLD_TABLE] = transition;
                        rangeVars[TriggerDef.OLD_TABLE]   = range;
                    } else {
                        if (Boolean.FALSE.equals(isForEachRow)
                                || oldRowName != null) {
                            throw unexpectedToken();
                        }

                        readIfThis(Tokens.ROW);
                        readIfThis(Tokens.AS);
                        checkIsSimpleName();

                        oldRowName = token.tokenString;

                        read();

                        String n = oldRowName;

                        if (n.equals(newTableName) || n.equals(oldTableName)
                                || n.equals(newRowName)) {
                            throw unexpectedToken();
                        }

                        isForEachRow = Boolean.TRUE;

                        HsqlName hsqlName = database.nameManager.newHsqlName(
                            table.getSchemaName(), n, isDelimitedIdentifier(),
                            SchemaObject.TRANSITION);
                        Table transition = new Table(table, hsqlName);
                        RangeVariable range = new RangeVariable(transition,
                            null, null, null, compileContext);

                        transitions[TriggerDef.OLD_ROW] = transition;
                        rangeVars[TriggerDef.OLD_ROW]   = range;
                    }
                } else if (token.tokenType == Tokens.NEW) {
                    if (operationType == StatementTypes.DELETE_WHERE) {
                        throw unexpectedToken();
                    }

                    read();

                    if (token.tokenType == Tokens.TABLE) {
                        if (Boolean.TRUE.equals(isForEachRow)
                                || newTableName != null
                                || beforeOrAfterType == TriggerDef.BEFORE) {
                            throw unexpectedToken();
                        }

                        read();
                        readIfThis(Tokens.AS);
                        checkIsSimpleName();

                        newTableName = token.tokenString;

                        read();

                        isForEachRow = Boolean.FALSE;

                        String n = newTableName;

                        if (n.equals(oldTableName) || n.equals(oldRowName)
                                || n.equals(newRowName)) {
                            throw unexpectedToken();
                        }

                        HsqlName hsqlName = database.nameManager.newHsqlName(
                            table.getSchemaName(), n, isDelimitedIdentifier(),
                            SchemaObject.TRANSITION);
                        Table transition = new Table(table, hsqlName);
                        RangeVariable range = new RangeVariable(transition,
                            null, null, null, compileContext);

                        transitions[TriggerDef.NEW_TABLE] = transition;
                        rangeVars[TriggerDef.NEW_TABLE]   = range;
                    } else {
                        if (Boolean.FALSE.equals(isForEachRow)
                                || newRowName != null) {
                            throw unexpectedToken();
                        }

                        readIfThis(Tokens.ROW);
                        readIfThis(Tokens.AS);
                        checkIsSimpleName();

                        newRowName = token.tokenString;

                        read();

                        isForEachRow = Boolean.TRUE;

                        String n = newRowName;

                        if (n.equals(oldTableName) || n.equals(newTableName)
                                || n.equals(oldRowName)) {
                            throw unexpectedToken();
                        }

                        HsqlName hsqlName = database.nameManager.newHsqlName(
                            table.getSchemaName(), n, isDelimitedIdentifier(),
                            SchemaObject.TRANSITION);
                        Table transition = new Table(table, hsqlName);
                        RangeVariable range = new RangeVariable(transition,
                            null, null, null, compileContext);

                        transitions[TriggerDef.NEW_ROW] = transition;
                        rangeVars[TriggerDef.NEW_ROW]   = range;
                    }
                } else {
                    break;
                }
            }
        }

        if (Boolean.TRUE.equals(isForEachRow)
                && token.tokenType != Tokens.FOR) {
            throw unexpectedTokenRequire(Tokens.T_FOR);
        }

        if (token.tokenType == Tokens.FOR) {
            read();
            readThis(Tokens.EACH);

            if (token.tokenType == Tokens.ROW) {
                if (Boolean.FALSE.equals(isForEachRow)) {
                    throw unexpectedToken();
                }

                isForEachRow = Boolean.TRUE;
            } else if (token.tokenType == Tokens.STATEMENT) {
                if (Boolean.TRUE.equals(isForEachRow)
                        || beforeOrAfterType == TriggerDef.BEFORE) {
                    throw unexpectedToken();
                }

                isForEachRow = Boolean.FALSE;
            } else {
                throw unexpectedToken();
            }

            read();
        }

        //
        if (rangeVars[TriggerDef.OLD_TABLE] != null) {}

        if (rangeVars[TriggerDef.NEW_TABLE] != null) {}

        //
        if (Tokens.T_QUEUE.equals(token.tokenString)) {
            read();

            queueSize    = readInteger();
            hasQueueSize = true;
        }

        if (Tokens.T_NOWAIT.equals(token.tokenString)) {
            read();

            isNowait = true;
        }

        if (token.tokenType == Tokens.WHEN
                && beforeOrAfterType != TriggerDef.INSTEAD) {
            read();
            readThis(Tokens.OPENBRACKET);

            int position = getPosition();

            isCheckOrTriggerCondition = true;
            condition                 = XreadBooleanValueExpression();
            conditionSQL              = getLastPart(position);
            isCheckOrTriggerCondition = false;

            readThis(Tokens.CLOSEBRACKET);

            HsqlList unresolved = condition.resolveColumnReferences(rangeVars,
                null);

            ExpressionColumn.checkColumnsResolved(unresolved);
            condition.resolveTypes(session, null);

            if (condition.getDataType() != Type.SQL_BOOLEAN) {
                throw Error.error(ErrorCode.X_42568);
            }
        }

        if (isForEachRow == null) {
            isForEachRow = Boolean.FALSE;
        }

        if (token.tokenType == Tokens.CALL) {
            int position = getPosition();

            try {
                read();
                checkIsSimpleName();
                checkIsDelimitedIdentifier();

                className = token.tokenString;

                read();

                td = new TriggerDef(name, beforeOrAfterType, operationType,
                                    isForEachRow.booleanValue(), table,
                                    transitions, rangeVars, condition,
                                    conditionSQL, updateColumnIndexes,
                                    className, isNowait, queueSize);

                String   sql  = getLastPart();
                Object[] args = new Object[] {
                    td, otherName
                };

                return new StatementSchema(sql, StatementTypes.CREATE_TRIGGER,
                                           args, null, table.getName());
            } catch (HsqlException e) {
                rewind(position);
            }
        }

        //
        if (hasQueueSize) {
            throw unexpectedToken(Tokens.T_QUEUE);
        }

        if (isNowait) {
            throw unexpectedToken(Tokens.T_NOWAIT);
        }

        Routine routine = compileTriggerRoutine(table, rangeVars,
            beforeOrAfterType, operationType);

        td = new TriggerDefSQL(name, beforeOrAfterType, operationType,
                               isForEachRow.booleanValue(), table,
                               transitions, rangeVars, condition,
                               conditionSQL, updateColumnIndexes, routine);

        String   sql  = getLastPart();
        Object[] args = new Object[] {
            td, otherName
        };

        return new StatementSchema(sql, StatementTypes.CREATE_TRIGGER, args,
                                   null, table.getName());
    }

    Routine compileTriggerRoutine(Table table, RangeVariable[] ranges,
                                  int beforeOrAfter, int operation) {

        int impact = (beforeOrAfter == TriggerDef.BEFORE) ? Routine.READS_SQL
                                                          : Routine
                                                              .MODIFIES_SQL;
        Routine routine = new Routine(table, ranges, impact, beforeOrAfter,
                                      operation);

        startRecording();

        Statement statement = compileSQLProcedureStatementOrNull(routine,
            null);

        if (statement == null) {
            throw unexpectedToken();
        }

        Token[] tokenisedStatement = getRecordedStatement();
        String  sql                = Token.getSQL(tokenisedStatement);

        statement.setSQL(sql);
        routine.setProcedure(statement);
        routine.resolve(session);

        return routine;
    }

    /**
     * Responsible for handling the creation of table columns during the process
     * of executing CREATE TABLE or ADD COLUMN etc. statements.
     *
     * @param table this table
     * @param hsqlName column name
     * @param constraintList list of constraints
     * @return a Column object with indicated attributes
     */
    ColumnSchema readColumnDefinitionOrNull(Table table, HsqlName hsqlName,
            HsqlArrayList constraintList) {

        boolean        isGenerated     = false;
        boolean        isIdentity      = false;
        boolean        isPKIdentity    = false;
        boolean        generatedAlways = false;
        Expression     generateExpr    = null;
        boolean        isNullable      = true;
        Expression     defaultExpr     = null;
        Type           typeObject      = null;
        NumberSequence sequence        = null;

        if (token.tokenType == Tokens.GENERATED) {
            read();
            readThis(Tokens.ALWAYS);

            isGenerated     = true;
            generatedAlways = true;

            // not yet
            throw unexpectedToken(Tokens.T_GENERATED);
        } else if (token.tokenType == Tokens.IDENTITY) {
            read();

            isIdentity   = true;
            isPKIdentity = true;
            typeObject   = Type.SQL_INTEGER;
            sequence     = new NumberSequence(null, 0, 1, typeObject);
        } else if (token.tokenType == Tokens.COMMA) {
            return null;
        } else if (token.tokenType == Tokens.CLOSEBRACKET) {
            return null;
        } else {
            typeObject = readTypeDefinition(true);
        }

        if (isGenerated || isIdentity) {}
        else if (token.tokenType == Tokens.DEFAULT) {
            read();

            defaultExpr = readDefaultClause(typeObject);
        } else if (token.tokenType == Tokens.GENERATED && !isIdentity) {
            read();

            if (token.tokenType == Tokens.BY) {
                read();
                readThis(Tokens.DEFAULT);
            } else {
                readThis(Tokens.ALWAYS);

                generatedAlways = true;
            }

            readThis(Tokens.AS);

            if (token.tokenType == Tokens.IDENTITY) {
                read();

                sequence = new NumberSequence(null, typeObject);

                sequence.setAlways(generatedAlways);

                if (token.tokenType == Tokens.OPENBRACKET) {
                    read();
                    readSequenceOptions(sequence, false, false);
                    readThis(Tokens.CLOSEBRACKET);
                }

                isIdentity = true;
            } else if (token.tokenType == Tokens.OPENBRACKET) {
                if (!generatedAlways) {
                    throw super.unexpectedTokenRequire(Tokens.T_ALWAYS);
                }

                isGenerated = true;
            }
        } else if (token.tokenType == Tokens.IDENTITY && !isIdentity) {
            read();

            isIdentity   = true;
            isPKIdentity = true;
            sequence     = new NumberSequence(null, 0, 1, typeObject);
        }

        if (isGenerated) {
            readThis(Tokens.OPENBRACKET);

            generateExpr = XreadValueExpression();

            readThis(Tokens.CLOSEBRACKET);
        }

        ColumnSchema column = new ColumnSchema(hsqlName, typeObject,
                                               isNullable, false, defaultExpr);

        column.setGeneratingExpression(generateExpr);
        readColumnConstraints(table, column, constraintList);

        if (token.tokenType == Tokens.IDENTITY && !isIdentity) {
            read();

            isIdentity   = true;
            isPKIdentity = true;
            sequence     = new NumberSequence(null, 0, 1, typeObject);
        }

        if (isIdentity) {
            column.setIdentity(sequence);
        }

        if (isPKIdentity && !column.isPrimaryKey()) {
            OrderedHashSet set = new OrderedHashSet();

            set.add(column.getName().name);

            HsqlName constName = database.nameManager.newAutoName("PK",
                table.getSchemaName(), table.getName(),
                SchemaObject.CONSTRAINT);
            Constraint c =
                new Constraint(constName, set,
                               SchemaObject.ConstraintTypes.PRIMARY_KEY);

            constraintList.set(0, c);
            column.setPrimaryKey(true);
        }

        return column;
    }

    private void readSequenceOptions(NumberSequence sequence,
                                     boolean withType, boolean isAlter) {

        OrderedIntHashSet set = new OrderedIntHashSet();

        while (true) {
            boolean end = false;

            if (set.contains(token.tokenType)) {
                throw unexpectedToken();
            }

            switch (token.tokenType) {

                case Tokens.AS : {
                    if (withType) {
                        read();

                        Type type = readTypeDefinition(true);

                        sequence.setDefaults(sequence.getName(), type);

                        break;
                    }

                    throw unexpectedToken();
                }
                case Tokens.START : {
                    set.add(token.tokenType);
                    read();
                    readThis(Tokens.WITH);

                    long value = readBigint();

                    sequence.setStartValueNoCheck(value);

                    break;
                }
                case Tokens.RESTART : {
                    if (!isAlter) {
                        end = true;

                        break;
                    }

                    set.add(token.tokenType);
                    read();

                    if (readIfThis(Tokens.WITH)) {
                        long value = readBigint();

                        sequence.setCurrentValueNoCheck(value);
                    } else {
                        sequence.setStartValueDefault();
                    }

                    break;
                }
                case Tokens.INCREMENT : {
                    set.add(token.tokenType);
                    read();
                    readThis(Tokens.BY);

                    long value = readBigint();

                    sequence.setIncrement(value);

                    break;
                }
                case Tokens.NO :
                    read();

                    if (token.tokenType == Tokens.MAXVALUE) {
                        sequence.setDefaultMaxValue();
                    } else if (token.tokenType == Tokens.MINVALUE) {
                        sequence.setDefaultMinValue();
                    } else if (token.tokenType == Tokens.CYCLE) {
                        sequence.setCycle(false);
                    } else {
                        throw unexpectedToken();
                    }

                    set.add(token.tokenType);
                    read();
                    break;

                case Tokens.MAXVALUE : {
                    set.add(token.tokenType);
                    read();

                    long value = readBigint();

                    sequence.setMaxValueNoCheck(value);

                    break;
                }
                case Tokens.MINVALUE : {
                    set.add(token.tokenType);
                    read();

                    long value = readBigint();

                    sequence.setMinValueNoCheck(value);

                    break;
                }
                case Tokens.CYCLE :
                    set.add(token.tokenType);
                    read();
                    sequence.setCycle(true);
                    break;

                default :
                    end = true;
                    break;
            }

            if (end) {
                break;
            }
        }

        sequence.checkValues();
    }

    /**
     * Reads and adds a table constraint definition to the list
     *
     * @param schemaObject table or domain
     * @param constraintList list of constraints
     */
    private void readConstraint(SchemaObject schemaObject,
                                HsqlArrayList constraintList) {

        HsqlName constName = null;

        if (token.tokenType == Tokens.CONSTRAINT) {
            read();

            constName =
                readNewDependentSchemaObjectName(schemaObject.getName(),
                                                 SchemaObject.CONSTRAINT);
        }

        switch (token.tokenType) {

            case Tokens.PRIMARY : {
                if (schemaObject.getName().type != SchemaObject.TABLE) {
                    throw this.unexpectedTokenRequire(Tokens.T_CHECK);
                }

                read();
                readThis(Tokens.KEY);

                Constraint mainConst;

                mainConst = (Constraint) constraintList.get(0);

                if (mainConst.constType
                        == SchemaObject.ConstraintTypes.PRIMARY_KEY) {
                    throw Error.error(ErrorCode.X_42532);
                }

                if (constName == null) {
                    constName = database.nameManager.newAutoName("PK",
                            schemaObject.getSchemaName(),
                            schemaObject.getName(), SchemaObject.CONSTRAINT);
                }

                OrderedHashSet set = readColumnNames(false);
                Constraint c =
                    new Constraint(constName, set,
                                   SchemaObject.ConstraintTypes.PRIMARY_KEY);

                constraintList.set(0, c);

                break;
            }
            case Tokens.UNIQUE : {
                if (schemaObject.getName().type != SchemaObject.TABLE) {
                    throw this.unexpectedTokenRequire(Tokens.T_CHECK);
                }

                read();

                OrderedHashSet set = readColumnNames(false);

                if (constName == null) {
                    constName = database.nameManager.newAutoName("CT",
                            schemaObject.getSchemaName(),
                            schemaObject.getName(), SchemaObject.CONSTRAINT);
                }

                Constraint c =
                    new Constraint(constName, set,
                                   SchemaObject.ConstraintTypes.UNIQUE);

                constraintList.add(c);

                break;
            }
            case Tokens.FOREIGN : {
                if (schemaObject.getName().type != SchemaObject.TABLE) {
                    throw this.unexpectedTokenRequire(Tokens.T_CHECK);
                }

                read();
                readThis(Tokens.KEY);

                OrderedHashSet set = readColumnNames(false);
                Constraint c = readFKReferences((Table) schemaObject,
                                                constName, set);

                constraintList.add(c);

                break;
            }
            case Tokens.CHECK : {
                read();

                if (constName == null) {
                    constName = database.nameManager.newAutoName("CT",
                            schemaObject.getSchemaName(),
                            schemaObject.getName(), SchemaObject.CONSTRAINT);
                }

                Constraint c =
                    new Constraint(constName, null,
                                   SchemaObject.ConstraintTypes.CHECK);

                readCheckConstraintCondition(c);
                constraintList.add(c);

                break;
            }
            default : {
                if (constName != null) {
                    throw super.unexpectedToken();
                }
            }
        }
    }

    /**
     * Reads column constraints
     */
    void readColumnConstraints(Table table, ColumnSchema column,
                               HsqlArrayList constraintList) {

        boolean end                  = false;
        boolean hasNotNullConstraint = false;
        boolean hasNullNoiseWord     = false;
        boolean hasPrimaryKey        = false;

        while (true) {
            HsqlName constName = null;

            if (token.tokenType == Tokens.CONSTRAINT) {
                read();

                constName = readNewDependentSchemaObjectName(table.getName(),
                        SchemaObject.CONSTRAINT);
            }

            switch (token.tokenType) {

                case Tokens.PRIMARY : {
                    if (hasNullNoiseWord || hasPrimaryKey) {
                        throw unexpectedToken();
                    }

                    read();
                    readThis(Tokens.KEY);

                    Constraint existingConst =
                        (Constraint) constraintList.get(0);

                    if (existingConst.constType
                            == SchemaObject.ConstraintTypes.PRIMARY_KEY) {
                        throw Error.error(ErrorCode.X_42532);
                    }

                    OrderedHashSet set = new OrderedHashSet();

                    set.add(column.getName().name);

                    if (constName == null) {
                        constName = database.nameManager.newAutoName("PK",
                                table.getSchemaName(), table.getName(),
                                SchemaObject.CONSTRAINT);
                    }

                    Constraint c = new Constraint(
                        constName, set,
                        SchemaObject.ConstraintTypes.PRIMARY_KEY);

                    constraintList.set(0, c);
                    column.setPrimaryKey(true);

                    hasPrimaryKey = true;

                    break;
                }
                case Tokens.UNIQUE : {
                    read();

                    OrderedHashSet set = new OrderedHashSet();

                    set.add(column.getName().name);

                    if (constName == null) {
                        constName = database.nameManager.newAutoName("CT",
                                table.getSchemaName(), table.getName(),
                                SchemaObject.CONSTRAINT);
                    }

                    Constraint c =
                        new Constraint(constName, set,
                                       SchemaObject.ConstraintTypes.UNIQUE);

                    constraintList.add(c);

                    break;
                }
                case Tokens.FOREIGN : {
                    read();
                    readThis(Tokens.KEY);
                }

                // fall through
                case Tokens.REFERENCES : {
                    OrderedHashSet set = new OrderedHashSet();

                    set.add(column.getName().name);

                    Constraint c = readFKReferences(table, constName, set);

                    constraintList.add(c);

                    break;
                }
                case Tokens.CHECK : {
                    read();

                    if (constName == null) {
                        constName = database.nameManager.newAutoName("CT",
                                table.getSchemaName(), table.getName(),
                                SchemaObject.CONSTRAINT);
                    }

                    Constraint c =
                        new Constraint(constName, null,
                                       SchemaObject.ConstraintTypes.CHECK);

                    readCheckConstraintCondition(c);

                    OrderedHashSet set = c.getCheckColumnExpressions();

                    for (int i = 0; i < set.size(); i++) {
                        ExpressionColumn e = (ExpressionColumn) set.get(i);

                        if (column.getName().name.equals(e.getColumnName())) {
                            if (e.getSchemaName() != null
                                    && e.getSchemaName()
                                       != table.getSchemaName().name) {
                                throw Error.error(ErrorCode.X_42505);
                            }
                        } else {
                            throw Error.error(ErrorCode.X_42501);
                        }
                    }

                    constraintList.add(c);

                    break;
                }
                case Tokens.NOT : {
                    if (hasNotNullConstraint || hasNullNoiseWord) {
                        throw unexpectedToken();
                    }

                    read();
                    readThis(Tokens.NULL);

                    if (constName == null) {
                        constName = database.nameManager.newAutoName("CT",
                                table.getSchemaName(), table.getName(),
                                SchemaObject.CONSTRAINT);
                    }

                    Constraint c =
                        new Constraint(constName, null,
                                       SchemaObject.ConstraintTypes.CHECK);

                    c.check = new ExpressionLogical(column);

                    constraintList.add(c);

                    hasNotNullConstraint = true;

                    break;
                }
                case Tokens.NULL : {
                    if (hasNotNullConstraint || hasNullNoiseWord
                            || hasPrimaryKey) {
                        throw unexpectedToken();
                    }

                    if (constName != null) {
                        throw unexpectedToken();
                    }

                    read();

                    hasNullNoiseWord = true;

                    break;
                }
                default :
                    end = true;
                    break;
            }

            if (end) {
                break;
            }
        }
    }

    /**
     * Responsible for handling check constraints section of CREATE TABLE ...
     *
     * @param c check constraint
     */
    void readCheckConstraintCondition(Constraint c) {

        readThis(Tokens.OPENBRACKET);
        startRecording();

        isCheckOrTriggerCondition = true;

        Expression condition = XreadBooleanValueExpression();

        isCheckOrTriggerCondition = false;

        Token[] tokens = getRecordedStatement();

        readThis(Tokens.CLOSEBRACKET);

        c.check = condition;
    }

    /**
     * Process a bracketed column list as used in the declaration of SQL
     * CONSTRAINTS and return an array containing the indexes of the columns
     * within the table.
     *
     * @param table table that contains the columns
     * @param ascOrDesc boolean
     * @return array of column indexes
     */
    private int[] readColumnList(Table table, boolean ascOrDesc) {

        OrderedHashSet set = readColumnNames(ascOrDesc);

        return table.getColumnIndexes(set);
    }

    StatementSchema compileCreateIndex(boolean unique) {

        Table    table;
        HsqlName indexHsqlName;

        read();

        indexHsqlName = readNewSchemaObjectName(SchemaObject.INDEX, true);

        readThis(Tokens.ON);

        table = readTableName();

        HsqlName tableSchema = table.getSchemaName();

        indexHsqlName.setSchemaIfNull(tableSchema);

        indexHsqlName.parent = table.getName();

        if (indexHsqlName.schema != tableSchema) {
            throw Error.error(ErrorCode.X_42505);
        }

        indexHsqlName.schema = table.getSchemaName();

        int[]    indexColumns = readColumnList(table, true);
        String   sql          = getLastPart();
        Object[] args         = new Object[] {
            table, indexColumns, indexHsqlName, Boolean.valueOf(unique)
        };

        return new StatementSchema(sql, StatementTypes.CREATE_INDEX, args,
                                   null, table.getName());
    }

    StatementSchema compileCreateSchema() {

        HsqlName schemaName    = null;
        String   authorisation = null;

        read();

        if (token.tokenType != Tokens.AUTHORIZATION) {
            schemaName = readNewSchemaName();
        }

        if (token.tokenType == Tokens.AUTHORIZATION) {
            read();
            checkIsSimpleName();

            authorisation = token.tokenString;

            read();

            if (schemaName == null) {
                Grantee owner =
                    database.getGranteeManager().get(authorisation);

                if (owner == null) {
                    throw Error.error(ErrorCode.X_28501, authorisation);
                }

                schemaName =
                    database.nameManager.newHsqlName(owner.getName().name,
                                                     isDelimitedIdentifier(),
                                                     SchemaObject.SCHEMA);

                SqlInvariants.checkSchemaNameNotSystem(token.tokenString);
            }
        }

        if (SqlInvariants.PUBLIC_ROLE_NAME.equals(authorisation)) {
            throw Error.error(ErrorCode.X_28502, authorisation);
        }

        Grantee owner = authorisation == null ? session.getGrantee()
                                              : database.getGranteeManager()
                                                  .get(authorisation);

        if (owner == null) {
            throw Error.error(ErrorCode.X_28501, authorisation);
        }

        if (!session.getGrantee().isSchemaCreator()) {
            throw Error.error(ErrorCode.X_0L000,
                              session.getGrantee().getNameString());
        }

        if (database.schemaManager.schemaExists(schemaName.name)) {
            throw Error.error(ErrorCode.X_42504, schemaName.name);
        }

        if (schemaName.name.equals(SqlInvariants.LOBS_SCHEMA)) {
            schemaName = SqlInvariants.LOBS_SCHEMA_HSQLNAME;
            owner      = schemaName.owner;
        }

        String        sql  = getLastPart();
        Object[]      args = new Object[] {
            schemaName, owner
        };
        HsqlArrayList list = new HsqlArrayList();
        StatementSchema cs = new StatementSchema(sql,
            StatementTypes.CREATE_SCHEMA, args, null, null);

        cs.setSchemaHsqlName(schemaName);
        list.add(cs);
        getCompiledStatementBody(list);

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

        list.toArray(array);

        boolean swapped;

        do {
            swapped = false;

            for (int i = 0; i < array.length - 1; i++) {
                if (array[i].order > array[i + 1].order) {
                    StatementSchema temp = array[i + 1];

                    array[i + 1] = array[i];
                    array[i]     = temp;
                    swapped      = true;
                }
            }
        } while (swapped);

        return new StatementSchemaDefinition(array);
    }

    void getCompiledStatementBody(HsqlList list) {

        int    position;
        String sql;
        int    statementType;

        for (boolean end = false; !end; ) {
            StatementSchema cs = null;

            position = getPosition();

            switch (token.tokenType) {

                case Tokens.CREATE :
                    read();

                    switch (token.tokenType) {

                        // not in schema definition
                        case Tokens.SCHEMA :
                        case Tokens.USER :
                        case Tokens.UNIQUE :
                            throw unexpectedToken();
                        case Tokens.INDEX :
                            statementType = StatementTypes.CREATE_INDEX;
                            sql = getStatement(position,
                                               startStatementTokensSchema);
                            cs = new StatementSchema(sql, statementType, null);
                            break;

                        case Tokens.SEQUENCE :
                            cs     = compileCreateSequence();
                            cs.sql = getLastPart(position);
                            break;

                        case Tokens.ROLE :
                            cs     = compileCreateRole();
                            cs.sql = getLastPart(position);
                            break;

                        case Tokens.DOMAIN :
                            statementType = StatementTypes.CREATE_DOMAIN;
                            sql = getStatement(position,
                                               startStatementTokensSchema);
                            cs = new StatementSchema(sql, statementType, null);
                            break;

                        case Tokens.TYPE :
                            cs     = compileCreateType();
                            cs.sql = getLastPart(position);
                            break;

                        case Tokens.CHARACTER :
                            cs     = compileCreateCharacterSet();
                            cs.sql = getLastPart(position);
                            break;

                        // no supported
                        case Tokens.ASSERTION :
                            throw unexpectedToken();
                        case Tokens.TABLE :
                        case Tokens.MEMORY :
                        case Tokens.CACHED :
                        case Tokens.TEMP :
                        case Tokens.GLOBAL :
                        case Tokens.TEMPORARY :
                        case Tokens.TEXT :
                            statementType = StatementTypes.CREATE_TABLE;
                            sql = getStatement(position,
                                               startStatementTokensSchema);
                            cs = new StatementSchema(sql, statementType, null);
                            break;

                        case Tokens.TRIGGER :
                            statementType = StatementTypes.CREATE_TRIGGER;
                            sql = getStatement(position,
                                               startStatementTokensSchema);
                            cs = new StatementSchema(sql, statementType, null);
                            break;

                        case Tokens.VIEW :
                            statementType = StatementTypes.CREATE_VIEW;
                            sql = getStatement(position,
                                               startStatementTokensSchema);
                            cs = new StatementSchema(sql, statementType, null);
                            break;

                        case Tokens.FUNCTION :
                            statementType = StatementTypes.CREATE_ROUTINE;
                            sql = getStatementForRoutine(
                                position, startStatementTokensSchema);
                            cs = new StatementSchema(sql, statementType, null);
                            break;

                        case Tokens.PROCEDURE :
                            statementType = StatementTypes.CREATE_ROUTINE;
                            sql = getStatementForRoutine(
                                position, startStatementTokensSchema);
                            cs = new StatementSchema(sql, statementType, null);
                            break;

                        default :
                            throw unexpectedToken();
                    }
                    break;

                case Tokens.GRANT :
                    cs     = compileGrantOrRevoke();
                    cs.sql = getLastPart(position);
                    break;

                case Tokens.SEMICOLON :
                    read();

                    end = true;
                    break;

                case Tokens.X_ENDPARSE :
                    end = true;
                    break;

                default :
                    throw unexpectedToken();
            }

            if (cs != null) {
                cs.isSchemaDefinition = true;

                list.add(cs);
            }
        }
    }

    StatementSchema compileCreateRole() {

        read();

        HsqlName name = readNewUserIdentifier();
        String   sql  = getLastPart();
        Object[] args = new Object[]{ name };

        return new StatementSchema(sql, StatementTypes.CREATE_ROLE, args);
    }

    StatementSchema compileCreateUser() {

        HsqlName name;
        String   password;
        boolean  admin   = false;
        Grantee  grantor = session.getGrantee();

        read();

        name = readNewUserIdentifier();

        readThis(Tokens.PASSWORD);

        password = readPassword();

        if (token.tokenType == Tokens.ADMIN) {
            read();

            admin = true;
        }

        checkDatabaseUpdateAuthorisation();

        String   sql  = getLastPart();
        Object[] args = new Object[] {
            name, password, grantor, Boolean.valueOf(admin)
        };

        return new StatementSchema(sql, StatementTypes.CREATE_USER, args);
    }

    HsqlName readNewUserIdentifier() {

        checkIsSimpleName();

        String  tokenS   = token.tokenString;
        boolean isQuoted = isDelimitedIdentifier();

        if (tokenS.equalsIgnoreCase("SA")) {
            tokenS   = "SA";
            isQuoted = false;
        }

        HsqlName name = database.nameManager.newHsqlName(tokenS, isQuoted,
            SchemaObject.GRANTEE);

        read();

        return name;
    }

    String readPassword() {

        String tokenS = token.tokenString;

        read();

        return tokenS;
    }

    Statement compileRenameObject(HsqlName name, int objectType) {

        HsqlName newName = readNewSchemaObjectName(objectType, true);
        String   sql     = getLastPart();
        Object[] args    = new Object[] {
            name, newName
        };

        return new StatementSchema(sql, StatementTypes.RENAME_OBJECT, args);
    }

    /**
     * Responsible for handling tail of ALTER TABLE ... RENAME ...
     * @param table table
     */
    void processAlterTableRename(Table table) {

        HsqlName name = readNewSchemaObjectName(SchemaObject.TABLE, true);

        name.setSchemaIfNull(table.getSchemaName());

        if (table.getSchemaName() != name.schema) {
            throw Error.error(ErrorCode.X_42505);
        }

        database.schemaManager.renameSchemaObject(table.getName(), name);
    }

    void processAlterTableAddUniqueConstraint(Table table, HsqlName name) {

        if (name == null) {
            name = database.nameManager.newAutoName("CT",
                    table.getSchemaName(), table.getName(),
                    SchemaObject.CONSTRAINT);
        }

        int[] cols = this.readColumnList(table, false);

        session.commit(false);

        TableWorks tableWorks = new TableWorks(session, table);

        tableWorks.addUniqueConstraint(cols, name);
    }

    Statement compileAlterTableAddUniqueConstraint(Table table,
            HsqlName name) {

        if (name == null) {
            name = database.nameManager.newAutoName("CT",
                    table.getSchemaName(), table.getName(),
                    SchemaObject.CONSTRAINT);
        }

        int[]    cols = this.readColumnList(table, false);
        String   sql  = getLastPart();
        Object[] args = new Object[] {
            cols, name
        };

        return new StatementSchema(sql, StatementTypes.ALTER_TABLE, args,
                                   null, table.getName());
    }

    void processAlterTableAddForeignKeyConstraint(Table table, HsqlName name) {

        if (name == null) {
            name = database.nameManager.newAutoName("FK",
                    table.getSchemaName(), table.getName(),
                    SchemaObject.CONSTRAINT);
        }

        OrderedHashSet set           = readColumnNames(false);
        Constraint     c             = readFKReferences(table, name, set);
        HsqlName       mainTableName = c.getMainTableName();

        c.core.mainTable = database.schemaManager.getTable(session,
                mainTableName.name, mainTableName.schema.name);

        c.setColumnsIndexes(table);
        session.commit(false);

        TableWorks tableWorks = new TableWorks(session, table);

        tableWorks.addForeignKey(c);
    }

    Statement compileAlterTableAddForeignKeyConstraint(Table table,
            HsqlName name) {

        if (name == null) {
            name = database.nameManager.newAutoName("FK",
                    table.getSchemaName(), table.getName(),
                    SchemaObject.CONSTRAINT);
        }

        OrderedHashSet set           = readColumnNames(false);
        Constraint     c             = readFKReferences(table, name, set);
        HsqlName       mainTableName = c.getMainTableName();

        c.core.mainTable = database.schemaManager.getTable(session,
                mainTableName.name, mainTableName.schema.name);

        c.setColumnsIndexes(table);

        String   sql  = getLastPart();
        Object[] args = new Object[]{ c };

        return new StatementSchema(sql, StatementTypes.ALTER_TABLE, args,
                                   c.core.mainTableName, table.getName());
    }

    void processAlterTableAddCheckConstraint(Table table, HsqlName name) {

        Constraint check;

        if (name == null) {
            name = database.nameManager.newAutoName("CT",
                    table.getSchemaName(), table.getName(),
                    SchemaObject.CONSTRAINT);
        }

        check = new Constraint(name, null, SchemaObject.ConstraintTypes.CHECK);

        readCheckConstraintCondition(check);
        session.commit(false);

        TableWorks tableWorks = new TableWorks(session, table);

        tableWorks.addCheckConstraint(check);
    }

    Statement compileAlterTableAddCheckConstraint(Table table, HsqlName name) {

        Constraint check;

        if (name == null) {
            name = database.nameManager.newAutoName("CT",
                    table.getSchemaName(), table.getName(),
                    SchemaObject.CONSTRAINT);
        }

        check = new Constraint(name, null, SchemaObject.ConstraintTypes.CHECK);

        readCheckConstraintCondition(check);

        String   sql  = getLastPart();
        Object[] args = new Object[]{ check };

        return new StatementSchema(sql, StatementTypes.ALTER_TABLE, args,
                                   null, table.getName());
    }

    void processAlterTableAddColumn(Table table) {

        int           colIndex = table.getColumnCount();
        HsqlArrayList list     = new HsqlArrayList();
        Constraint constraint =
            new Constraint(null, null, SchemaObject.ConstraintTypes.TEMP);

        list.add(constraint);
        checkIsSchemaObjectName();

        HsqlName hsqlName =
            database.nameManager.newColumnHsqlName(table.getName(),
                token.tokenString, isDelimitedIdentifier());

        read();

        ColumnSchema column = readColumnDefinitionOrNull(table, hsqlName,
            list);

        if (column == null) {
            throw Error.error(ErrorCode.X_42000);
        }

        if (token.tokenType == Tokens.BEFORE) {
            read();

            colIndex = table.getColumnIndex(token.tokenString);

            read();
        }

        TableWorks tableWorks = new TableWorks(session, table);

        session.commit(false);
        tableWorks.addColumn(column, colIndex, list);

        return;
    }

    Statement compileAlterTableAddColumn(Table table) {

        int           colIndex = table.getColumnCount();
        HsqlArrayList list     = new HsqlArrayList();
        Constraint constraint =
            new Constraint(null, null, SchemaObject.ConstraintTypes.TEMP);

        list.add(constraint);
        checkIsSchemaObjectName();

        HsqlName hsqlName =
            database.nameManager.newColumnHsqlName(table.getName(),
                token.tokenString, isDelimitedIdentifier());

        read();

        ColumnSchema column = readColumnDefinitionOrNull(table, hsqlName,
            list);

        if (column == null) {
            throw Error.error(ErrorCode.X_42000);
        }

        if (token.tokenType == Tokens.BEFORE) {
            read();

            colIndex = table.getColumnIndex(token.tokenString);

            read();
        }

        String   sql  = getLastPart();
        Object[] args = new Object[] {
            column, new Integer(colIndex), list
        };

        return new StatementSchema(sql, StatementTypes.ALTER_TABLE, args,
                                   null, table.getName());
    }

    void processAlterTableAddPrimaryKey(Table table, HsqlName name) {

        if (name == null) {
            name = session.database.nameManager.newAutoName("PK",
                    table.getSchemaName(), table.getName(),
                    SchemaObject.CONSTRAINT);
        }

        OrderedHashSet set = readColumnNames(false);
        Constraint constraint =
            new Constraint(name, set,
                           SchemaObject.ConstraintTypes.PRIMARY_KEY);

        constraint.setColumnsIndexes(table);
        session.commit(false);

        TableWorks tableWorks = new TableWorks(session, table);

        tableWorks.addPrimaryKey(constraint, name);
    }

    Statement compileAlterTableAddPrimaryKey(Table table, HsqlName name) {

        if (name == null) {
            name = session.database.nameManager.newAutoName("PK",
                    table.getSchemaName(), table.getName(),
                    SchemaObject.CONSTRAINT);
        }

        OrderedHashSet set = readColumnNames(false);
        Constraint constraint =
            new Constraint(name, set,
                           SchemaObject.ConstraintTypes.PRIMARY_KEY);

        constraint.setColumnsIndexes(table);

        String   sql  = getLastPart();
        Object[] args = new Object[]{ constraint };

        return new StatementSchema(sql, StatementTypes.ALTER_TABLE, args,
                                   null, table.getName());
    }

    /**
     * Responsible for handling tail of ALTER TABLE ... DROP COLUMN ...
     */
    void processAlterTableDropColumn(Table table, String colName,
                                     boolean cascade) {

        int colindex = table.getColumnIndex(colName);

        if (table.getColumnCount() == 1) {
            throw Error.error(ErrorCode.X_42591);
        }

        session.commit(false);

        TableWorks tableWorks = new TableWorks(session, table);

        tableWorks.dropColumn(colindex, cascade);
    }

    Statement compileAlterTableDropColumn(Table table, String colName,
                                          boolean cascade) {

        HsqlName writeName = null;
        int      colindex  = table.getColumnIndex(colName);

        if (table.getColumnCount() == 1) {
            throw Error.error(ErrorCode.X_42591);
        }

        Object[] args = new Object[] {
            table.getColumn(colindex).getName(),
            ValuePool.getInt(SchemaObject.CONSTRAINT),
            Boolean.valueOf(cascade), Boolean.valueOf(false)
        };

        if (!table.isTemp()) {
            writeName = table.getName();
        }

        return new StatementSchema(null, StatementTypes.DROP_COLUMN, args,
                                   null, writeName);
    }

    /**
     * Responsible for handling tail of ALTER TABLE ... DROP CONSTRAINT ...
     */
    void processAlterTableDropConstraint(Table table, String name,
                                         boolean cascade) {

        session.commit(false);

        TableWorks tableWorks = new TableWorks(session, table);

        tableWorks.dropConstraint(name, cascade);

        return;
    }

    void processAlterColumn(Table table, ColumnSchema column,
                            int columnIndex) {

        int position = getPosition();

        switch (token.tokenType) {

            case Tokens.RENAME : {
                read();
                readThis(Tokens.TO);
                processAlterColumnRename(table, column);

                return;
            }
            case Tokens.DROP : {
                read();

                if (token.tokenType == Tokens.DEFAULT) {
                    read();

                    TableWorks tw = new TableWorks(session, table);

                    tw.setColDefaultExpression(columnIndex, null);

                    return;
                } else if (token.tokenType == Tokens.GENERATED) {
                    read();
                    column.setIdentity(null);
                    table.setColumnTypeVars(columnIndex);

                    return;
                } else {
                    throw unexpectedToken();
                }
            }
            case Tokens.SET : {
                read();

                switch (token.tokenType) {

                    case Tokens.DATA : {
                        read();
                        readThis(Tokens.TYPE);
                        processAlterColumnDataType(table, column);

                        return;
                    }
                    case Tokens.DEFAULT : {
                        read();

                        //ALTER TABLE .. ALTER COLUMN .. SET DEFAULT
                        TableWorks tw   = new TableWorks(session, table);
                        Type       type = column.getDataType();
                        Expression expr = this.readDefaultClause(type);

                        tw.setColDefaultExpression(columnIndex, expr);

                        return;
                    }
                    case Tokens.NOT : {

                        //ALTER TABLE .. ALTER COLUMN .. SET NOT NULL
                        read();
                        readThis(Tokens.NULL);
                        session.commit(false);

                        TableWorks tw = new TableWorks(session, table);

                        tw.setColNullability(column, false);

                        return;
                    }
                    case Tokens.NULL : {
                        read();

                        //ALTER TABLE .. ALTER COLUMN .. SET NULL
                        session.commit(false);

                        TableWorks tw = new TableWorks(session, table);

                        tw.setColNullability(column, true);

                        return;
                    }
                    default :
                        rewind(position);
                        read();
                        break;
                }
            }
            default :
        }

        if (token.tokenType == Tokens.SET
                || token.tokenType == Tokens.RESTART) {
            if (!column.isIdentity()) {
                throw Error.error(ErrorCode.X_42535);
            }

            processAlterColumnSequenceOptions(column);

            return;
        } else {
            processAlterColumnType(table, column, true);

            return;
        }
    }

    Statement compileAlterColumn(Table table, ColumnSchema column,
                                 int columnIndex) {

        int position = getPosition();

        switch (token.tokenType) {

            case Tokens.RENAME : {
                read();
                readThis(Tokens.TO);

                return compileAlterColumnRename(table, column);
            }
            case Tokens.DROP : {
                read();

                if (token.tokenType == Tokens.DEFAULT) {
                    read();

                    return compileAlterColumnDropDefault(table, column,
                                                         columnIndex);
                } else if (token.tokenType == Tokens.GENERATED) {
                    read();

                    return compileAlterColumnDropGenerated(table, column,
                                                           columnIndex);
                } else {
                    throw unexpectedToken();
                }
            }
            case Tokens.SET : {
                read();

                switch (token.tokenType) {

                    case Tokens.DATA : {
                        read();
                        readThis(Tokens.TYPE);

                        return compileAlterColumnDataType(table, column);
                    }
                    case Tokens.DEFAULT : {
                        read();

                        //ALTER TABLE .. ALTER COLUMN .. SET DEFAULT
                        Type       type = column.getDataType();
                        Expression expr = this.readDefaultClause(type);

                        return compileAlterColumnSetDefault(table, column,
                                                            expr);
                    }
                    case Tokens.NOT : {

                        //ALTER TABLE .. ALTER COLUMN .. SET NOT NULL
                        read();
                        readThis(Tokens.NULL);

                        return compileAlterColumnSetNullability(table, column,
                                false);
                    }
                    case Tokens.NULL : {
                        read();

                        return compileAlterColumnSetNullability(table, column,
                                true);
                    }
                    default :
                        rewind(position);
                        read();
                        break;
                }
            }

            // fall through
            default :
        }

        if (token.tokenType == Tokens.SET
                || token.tokenType == Tokens.RESTART) {
            if (!column.isIdentity()) {
                throw Error.error(ErrorCode.X_42535);
            }

            return compileAlterColumnSequenceOptions(table, column);
        } else {
            return compileAlterColumnType(table, column);
        }
    }

    private Statement compileAlterColumnDataType(Table table,
            ColumnSchema column) {

        HsqlName writeName  = null;
        Type     typeObject = readTypeDefinition(false);
        String   sql        = getLastPart();
        Object[] args       = new Object[] {
            table, column, typeObject
        };

        return new StatementSchema(sql, StatementTypes.ALTER_TABLE, null,
                                   null, table.getName());
    }

    private Statement compileAlterColumnType(Table table,
            ColumnSchema column) {

        String sql = super.getStatement(getParsePosition(),
                                        startStatementTokens);

        return new StatementSchema(sql, StatementTypes.ALTER_TABLE, null,
                                   table.getName());
    }

    private Statement compileAlterColumnSequenceOptions(Table table,
            ColumnSchema column) {

        String sql = super.getStatement(getParsePosition(),
                                        startStatementTokens);

        return new StatementSchema(sql, StatementTypes.ALTER_TABLE, null,
                                   table.getName());
    }

    private Statement compileAlterColumnSetNullability(Table table,
            ColumnSchema column, boolean b) {

        String sql = super.getStatement(getParsePosition(),
                                        startStatementTokens);

        return new StatementSchema(sql, StatementTypes.ALTER_TABLE, null,
                                   table.getName());
    }

    private Statement compileAlterColumnSetDefault(Table table,
            ColumnSchema column, Expression expr) {

        String sql = super.getStatement(getParsePosition(),
                                        startStatementTokens);

        return new StatementSchema(sql, StatementTypes.ALTER_TABLE, null,
                                   table.getName());
    }

    private Statement compileAlterColumnDropGenerated(Table table,
            ColumnSchema column, int columnIndex) {

        String sql = super.getStatement(getParsePosition(),
                                        startStatementTokens);

        return new StatementSchema(sql, StatementTypes.ALTER_TABLE, null,
                                   table.getName());
    }

    private Statement compileAlterColumnDropDefault(Table table,
            ColumnSchema column, int columnIndex) {

        String sql = super.getStatement(getParsePosition(),
                                        startStatementTokens);

        return new StatementSchema(sql, StatementTypes.ALTER_TABLE, null,
                                   table.getName());
    }

    Statement compileAlterSequence() {

        read();

        HsqlName schema = session.getSchemaHsqlName(token.namePrefix);
        NumberSequence sequence =
            database.schemaManager.getSequence(token.tokenString, schema.name,
                                               true);

        read();

        if (token.tokenType == Tokens.RENAME) {
            read();
            readThis(Tokens.TO);

            return compileRenameObject(sequence.getName(),
                                       SchemaObject.SEQUENCE);
        }

        NumberSequence copy = sequence.duplicate();

        readSequenceOptions(copy, false, true);

        String   sql  = getLastPart();
        Object[] args = new Object[] {
            sequence, copy
        };

        return new StatementSchema(sql, StatementTypes.ALTER_SEQUENCE, args);
    }

    void processAlterColumnSequenceOptions(ColumnSchema column) {

        OrderedIntHashSet set      = new OrderedIntHashSet();
        NumberSequence    sequence = column.getIdentitySequence().duplicate();

        while (true) {
            boolean end = false;

            switch (token.tokenType) {

                case Tokens.RESTART : {
                    if (!set.add(token.tokenType)) {
                        throw unexpectedToken();
                    }

                    read();
                    readThis(Tokens.WITH);

                    long value = readBigint();

                    sequence.setStartValue(value);

                    break;
                }
                case Tokens.SET :
                    read();

                    switch (token.tokenType) {

                        case Tokens.INCREMENT : {
                            if (!set.add(token.tokenType)) {
                                throw unexpectedToken();
                            }

                            read();
                            readThis(Tokens.BY);

                            long value = readBigint();

                            sequence.setIncrement(value);

                            break;
                        }
                        case Tokens.NO :
                            read();

                            if (token.tokenType == Tokens.MAXVALUE) {
                                sequence.setDefaultMaxValue();
                            } else if (token.tokenType == Tokens.MINVALUE) {
                                sequence.setDefaultMinValue();
                            } else if (token.tokenType == Tokens.CYCLE) {
                                sequence.setCycle(false);
                            } else {
                                throw unexpectedToken();
                            }

                            if (!set.add(token.tokenType)) {
                                throw unexpectedToken();
                            }

                            read();
                            break;

                        case Tokens.MAXVALUE : {
                            if (!set.add(token.tokenType)) {
                                throw unexpectedToken();
                            }

                            read();

                            long value = readBigint();

                            sequence.setMaxValueNoCheck(value);

                            break;
                        }
                        case Tokens.MINVALUE : {
                            if (!set.add(token.tokenType)) {
                                throw unexpectedToken();
                            }

                            read();

                            long value = readBigint();

                            sequence.setMinValueNoCheck(value);

                            break;
                        }
                        case Tokens.CYCLE :
                            if (!set.add(token.tokenType)) {
                                throw unexpectedToken();
                            }

                            read();
                            sequence.setCycle(true);
                            break;

                        default :
                            throw super.unexpectedToken();
                    }
                    break;

                default :
                    end = true;
                    break;
            }

            if (end) {
                break;
            }
        }

        sequence.checkValues();
        column.getIdentitySequence().reset(sequence);
    }

    /**
     * Should allow only limited changes to column type
     */
    private void processAlterColumnDataType(Table table, ColumnSchema oldCol) {
        processAlterColumnType(table, oldCol, false);
    }

    /**
     * Allows changes to type of column or addition of an IDENTITY generator.
     * IDENTITY is not removed if it does not appear in new column definition
     * Constraint definitions are not allowed
     */
    private void processAlterColumnType(Table table, ColumnSchema oldCol,
                                        boolean fullDefinition) {

        ColumnSchema newCol;

        if (oldCol.isGenerated()) {
            throw Error.error(ErrorCode.X_42561);
        }

        if (fullDefinition) {
            HsqlArrayList list = new HsqlArrayList();
            Constraint    c    = table.getPrimaryConstraint();

            if (c == null) {
                c = new Constraint(null, null,
                                   SchemaObject.ConstraintTypes.TEMP);
            }

            list.add(c);

            newCol = readColumnDefinitionOrNull(table, oldCol.getName(), list);

            if (newCol == null) {
                throw Error.error(ErrorCode.X_42000);
            }

            if (oldCol.isIdentity() && newCol.isIdentity()) {
                throw Error.error(ErrorCode.X_42525);
            }

            if (list.size() > 1) {
                throw Error.error(ErrorCode.X_42524);
            }
        } else {
            Type type = readTypeDefinition(true);

            if (oldCol.isIdentity()) {
                if (!type.isIntegralType()) {
                    throw Error.error(ErrorCode.X_42561);
                }
            }

            newCol = oldCol.duplicate();

            newCol.setType(type);
        }

        TableWorks tw = new TableWorks(session, table);

        tw.retypeColumn(oldCol, newCol);
    }

    /**
     * Responsible for handling tail of ALTER COLUMN ... RENAME ...
     */
    private void processAlterColumnRename(Table table, ColumnSchema column) {

        checkIsSimpleName();

        if (table.findColumn(token.tokenString) > -1) {
            throw Error.error(ErrorCode.X_42504, token.tokenString);
        }

        database.schemaManager.checkColumnIsReferenced(table.getName(),
                column.getName());
        session.commit(false);
        table.renameColumn(column, token.tokenString, isDelimitedIdentifier());
        read();
    }

    private Statement compileAlterColumnRename(Table table,
            ColumnSchema column) {

        checkIsSimpleName();

        HsqlName name = readNewSchemaObjectName(SchemaObject.COLUMN, true);

        if (table.findColumn(name.name) > -1) {
            throw Error.error(ErrorCode.X_42504, name.name);
        }

        database.schemaManager.checkColumnIsReferenced(table.getName(),
                column.getName());

        String   sql  = getLastPart();
        Object[] args = new Object[] {
            column.getName(), name
        };

        return new StatementSchema(sql, StatementTypes.RENAME_OBJECT, args);
    }

    Statement compileAlterSchemaRename() {

        HsqlName name = readSchemaName();

        checkSchemaUpdateAuthorisation(name);
        readThis(Tokens.RENAME);
        readThis(Tokens.TO);

        HsqlName newName = readNewSchemaName();
        String   sql     = getLastPart();
        Object[] args    = new Object[] {
            name, newName
        };

        return new StatementSchema(sql, StatementTypes.RENAME_OBJECT, args);
    }

    Statement compileAlterUser() {

        read();

        String   password;
        User     userObject;
        HsqlName userName = readNewUserIdentifier();

        userObject = database.getUserManager().get(userName.name);

        if (userName.name.equals(Tokens.T_PUBLIC)) {
            throw Error.error(ErrorCode.X_42503);
        }

        readThis(Tokens.SET);

        if (token.tokenType == Tokens.PASSWORD) {
            read();

            password = readPassword();

            Object[] args = new Object[] {
                userObject, password
            };

            return new StatementCommand(StatementTypes.SET_USER_PASSWORD,
                                        args);
        } else if (token.tokenType == Tokens.INITIAL) {
            read();
            readThis(Tokens.SCHEMA);

            HsqlName schemaName;

            if (token.tokenType == Tokens.DEFAULT) {
                schemaName = null;
            } else {
                schemaName = database.schemaManager.getSchemaHsqlName(
                    token.tokenString);
            }

            read();

            Object[] args = new Object[] {
                userObject, schemaName
            };

            return new StatementCommand(StatementTypes.SET_USER_INITIAL_SCHEMA,
                                        args);
        } else {
            throw unexpectedToken();
        }
    }

    void processAlterDomain() {

        HsqlName schema = session.getSchemaHsqlName(token.namePrefix);

        checkSchemaUpdateAuthorisation(schema);

        Type domain = database.schemaManager.getDomain(token.tokenString,
            schema.name, true);

        read();

        switch (token.tokenType) {

            case Tokens.RENAME : {
                read();
                readThis(Tokens.TO);

                HsqlName newName = readNewSchemaObjectName(SchemaObject.DOMAIN,
                    true);

                newName.setSchemaIfNull(schema);

                if (domain.getSchemaName() != newName.schema) {
                    throw Error.error(ErrorCode.X_42505, newName.schema.name);
                }

                checkSchemaUpdateAuthorisation(schema);
                database.schemaManager.renameSchemaObject(domain.getName(),
                        newName);

                return;
            }
            case Tokens.DROP : {
                read();

                if (token.tokenType == Tokens.DEFAULT) {
                    read();
                    domain.userTypeModifier.removeDefaultClause();

                    return;
                } else if (token.tokenType == Tokens.CONSTRAINT) {
                    read();
                    checkIsSchemaObjectName();

                    HsqlName name = database.schemaManager.getSchemaObjectName(
                        domain.getSchemaName(), token.tokenString,
                        SchemaObject.CONSTRAINT, true);

                    read();
                    database.schemaManager.removeSchemaObject(name);

                    return;
                } else {
                    throw unexpectedToken();
                }
            }
            case Tokens.SET : {
                read();
                readThis(Tokens.DEFAULT);

                Expression e = readDefaultClause(domain);

                domain.userTypeModifier.setDefaultClause(e);

                return;
            }
            case Tokens.ADD : {
                read();

                if (token.tokenType == Tokens.CONSTRAINT
                        || token.tokenType == Tokens.CHECK) {
                    HsqlArrayList tempConstraints = new HsqlArrayList();

                    readConstraint(domain, tempConstraints);

                    Constraint c = (Constraint) tempConstraints.get(0);

                    domain.userTypeModifier.addConstraint(c);
                    database.schemaManager.addSchemaObject(c);

                    return;
                }
            }
        }

        throw unexpectedToken();
    }

    Statement compileAlterDomain() {

        read();

        HsqlName schema = session.getSchemaHsqlName(token.namePrefix);
        Type domain = database.schemaManager.getDomain(token.tokenString,
            schema.name, true);

        read();

        switch (token.tokenType) {

            case Tokens.RENAME : {
                read();
                readThis(Tokens.TO);

                return compileRenameObject(domain.getName(),
                                           SchemaObject.DOMAIN);
            }
            case Tokens.DROP : {
                read();

                if (token.tokenType == Tokens.DEFAULT) {
                    read();

                    return compileAlterDomainDropDefault(domain);
                } else if (token.tokenType == Tokens.CONSTRAINT) {
                    read();
                    checkIsSchemaObjectName();

                    HsqlName name = database.schemaManager.getSchemaObjectName(
                        domain.getSchemaName(), token.tokenString,
                        SchemaObject.CONSTRAINT, true);

                    read();

                    return compileAlterDomainDropConstraint(domain, name);
                } else {
                    throw unexpectedToken();
                }
            }
            case Tokens.SET : {
                read();
                readThis(Tokens.DEFAULT);

                Expression e = readDefaultClause(domain);

                return compileAlterDomainSetDefault(domain, e);
            }
            case Tokens.ADD : {
                read();

                if (token.tokenType == Tokens.CONSTRAINT
                        || token.tokenType == Tokens.CHECK) {
                    HsqlArrayList tempConstraints = new HsqlArrayList();

                    readConstraint(domain, tempConstraints);

                    Constraint c = (Constraint) tempConstraints.get(0);

                    return compileAlterDomainAddConstraint(domain, c);
                }
            }
        }

        throw unexpectedToken();
    }

    private Statement compileAlterDomainAddConstraint(Type domain,
            Constraint c) {

        String sql = super.getStatement(getParsePosition(),
                                        startStatementTokens);

        return new StatementSchema(sql, StatementTypes.ALTER_DOMAIN, null,
                                   null);
    }

    private Statement compileAlterDomainSetDefault(Type domain, Expression e) {

        String sql = super.getStatement(getParsePosition(),
                                        startStatementTokens);

        return new StatementSchema(sql, StatementTypes.ALTER_DOMAIN, null,
                                   null);
    }

    private Statement compileAlterDomainDropConstraint(Type domain,
            HsqlName name) {

        String sql = super.getStatement(getParsePosition(),
                                        startStatementTokens);

        return new StatementSchema(sql, StatementTypes.ALTER_DOMAIN, null,
                                   null);
    }

    private Statement compileAlterDomainDropDefault(Type domain) {

        String sql = getStatement(getParsePosition(), startStatementTokens);

        return new StatementSchema(sql, StatementTypes.ALTER_DOMAIN, null,
                                   null);
    }

    private boolean isGrantToken() {

        switch (token.tokenType) {

            case Tokens.ALL :
            case Tokens.INSERT :
            case Tokens.UPDATE :
            case Tokens.SELECT :
            case Tokens.DELETE :
            case Tokens.USAGE :
            case Tokens.EXECUTE :
            case Tokens.REFERENCES :
                return true;

            default :
                return false;
        }
    }

    StatementSchema compileGrantOrRevoke() {

        boolean grant = token.tokenType == Tokens.GRANT;

        read();

        if (isGrantToken()
                || (!grant
                    && (token.tokenType == Tokens.GRANT
                        || token.tokenType == Tokens.HIERARCHY))) {
            return compileRightGrantOrRevoke(grant);
        } else {
            return compileRoleGrantOrRevoke(grant);
        }
    }

    private StatementSchema compileRightGrantOrRevoke(boolean grant) {

        OrderedHashSet granteeList = new OrderedHashSet();
        Grantee        grantor     = null;
        Right          right       = null;

//        SchemaObject   schemaObject;
        HsqlName objectName    = null;
        boolean  isTable       = false;
        boolean  isUsage       = false;
        boolean  isExec        = false;
        boolean  isAll         = false;
        boolean  isGrantOption = false;
        boolean  cascade       = false;

        if (!grant) {
            if (token.tokenType == Tokens.GRANT) {
                read();
                readThis(Tokens.OPTION);
                readThis(Tokens.FOR);

                isGrantOption = true;

                // throw not suppoerted
            } else if (token.tokenType == Tokens.HIERARCHY) {
                throw unsupportedFeature();
/*
                read();
                readThis(Token.OPTION);
                readThis(Token.FOR);
*/
            }
        }

        // ALL means all the rights the grantor can grant
        if (token.tokenType == Tokens.ALL) {
            read();

            if (token.tokenType == Tokens.PRIVILEGES) {
                read();
            }

            right = Right.fullRights;
            isAll = true;
        } else {
            right = new Right();

            boolean loop = true;

            while (loop) {
                checkIsNotQuoted();

                int rightType =
                    GranteeManager.getCheckSingleRight(token.tokenString);
                int            grantType = token.tokenType;
                OrderedHashSet columnSet = null;

                read();

                switch (grantType) {

                    case Tokens.REFERENCES :
                    case Tokens.SELECT :
                    case Tokens.INSERT :
                    case Tokens.UPDATE :
                        if (token.tokenType == Tokens.OPENBRACKET) {
                            columnSet = readColumnNames(false);
                        }

                    // fall through
                    case Tokens.DELETE :
                    case Tokens.TRIGGER :
                        if (right == null) {
                            right = new Right();
                        }

                        right.set(rightType, columnSet);

                        isTable = true;
                        break;

                    case Tokens.USAGE :
                        if (isTable) {
                            throw unexpectedToken();
                        }

                        right   = Right.fullRights;
                        isUsage = true;
                        loop    = false;

                        continue;
                    case Tokens.EXECUTE :
                        if (isTable) {
                            throw unexpectedToken();
                        }

                        right  = Right.fullRights;
                        isExec = true;
                        loop   = false;

                        continue;
                }

                if (token.tokenType == Tokens.COMMA) {
                    read();

                    continue;
                }

                break;
            }
        }

        readThis(Tokens.ON);

        int objectType = 0;

        switch (token.tokenType) {

            case Tokens.CLASS :
                if (!isExec && !isAll) {
                    throw unexpectedToken();
                }

                read();

                if (!isSimpleName() || !isDelimitedIdentifier()) {
                    throw Error.error(ErrorCode.X_42569);
                }

                objectType = SchemaObject.FUNCTION;
                objectName = readNewSchemaObjectName(SchemaObject.FUNCTION,
                                                     false);
                break;

            case Tokens.SPECIFIC : {
                if (!isExec && !isAll) {
                    throw unexpectedToken();
                }

                read();

                switch (token.tokenType) {

                    case Tokens.ROUTINE :
                    case Tokens.PROCEDURE :
                    case Tokens.FUNCTION :
                        read();
                        break;

                    default :
                        throw unexpectedToken();
                }

                objectType = SchemaObject.SPECIFIC_ROUTINE;

                break;
            }
            case Tokens.FUNCTION :
                if (!isExec && !isAll) {
                    throw unexpectedToken();
                }

                read();

                objectType = SchemaObject.FUNCTION;
                break;

            case Tokens.PROCEDURE :
                if (!isExec && !isAll) {
                    throw unexpectedToken();
                }

                read();

                objectType = SchemaObject.PROCEDURE;
                break;

            case Tokens.ROUTINE :
                if (!isExec && !isAll) {
                    throw unexpectedToken();
                }

                read();

                objectType = SchemaObject.ROUTINE;
                break;

            case Tokens.TYPE :
                if (!isUsage && !isAll) {
                    throw unexpectedToken();
                }

                read();

                objectType = SchemaObject.TYPE;
                break;

            case Tokens.DOMAIN :
                if (!isUsage && !isAll) {
                    throw unexpectedToken();
                }

                read();

                objectType = SchemaObject.DOMAIN;
                break;

            case Tokens.SEQUENCE :
                if (!isUsage && !isAll) {
                    throw unexpectedToken();
                }

                read();

                objectType = SchemaObject.SEQUENCE;
                break;

            case Tokens.CHARACTER :
                if (!isUsage && !isAll) {
                    throw unexpectedToken();
                }

                read();
                readThis(Tokens.SET);

                objectType = SchemaObject.CHARSET;
                break;

            case Tokens.TABLE :
            default :
                if (!isTable && !isAll) {
                    throw unexpectedToken();
                }

                readIfThis(Tokens.TABLE);

                objectType = SchemaObject.TABLE;
        }

        objectName = readNewSchemaObjectName(objectType, false);

        if (grant) {
            readThis(Tokens.TO);
        } else {
            readThis(Tokens.FROM);
        }

        while (true) {
            checkIsSimpleName();
            granteeList.add(token.tokenString);
            read();

            if (token.tokenType == Tokens.COMMA) {
                read();
            } else {
                break;
            }
        }

        if (grant) {
            if (token.tokenType == Tokens.WITH) {
                read();
                readThis(Tokens.GRANT);
                readThis(Tokens.OPTION);

                isGrantOption = true;
            }

            /** @todo - implement */
            if (token.tokenType == Tokens.GRANTED) {
                read();
                readThis(Tokens.BY);

                if (token.tokenType == Tokens.CURRENT_USER) {
                    read();

                    //
                } else {
                    readThis(Tokens.CURRENT_ROLE);

                    if (session.getRole() == null) {
                        throw Error.error(ErrorCode.X_0P000);
                    }

                    grantor = session.getRole();
                }
            }
        } else {
            if (token.tokenType == Tokens.CASCADE) {
                cascade = true;

                read();
            } else {
                readThis(Tokens.RESTRICT);
            }
        }

        int      typee = grant ? StatementTypes.GRANT
                               : StatementTypes.REVOKE;
        Object[] args  = new Object[] {
            granteeList, objectName, right, grantor, Boolean.valueOf(cascade),
            Boolean.valueOf(isGrantOption)
        };
        String          sql = getLastPart();
        StatementSchema cs  = new StatementSchema(sql, typee, args);

        return cs;
    }

    private StatementSchema compileRoleGrantOrRevoke(boolean grant) {

        Grantee        grantor     = session.getGrantee();
        OrderedHashSet roleList    = new OrderedHashSet();
        OrderedHashSet granteeList = new OrderedHashSet();
        boolean        cascade     = false;

        if (!grant && token.tokenType == Tokens.ADMIN) {
            throw unsupportedFeature();
/*
            read();
            readThis(Token.OPTION);
            readThis(Token.FOR);
*/
        }

        while (true) {
            checkIsSimpleName();
            roleList.add(token.tokenString);
            read();

            if (token.tokenType == Tokens.COMMA) {
                read();

                continue;
            }

            break;
        }

        if (grant) {
            readThis(Tokens.TO);
        } else {
            readThis(Tokens.FROM);
        }

        while (true) {
            checkIsSimpleName();
            granteeList.add(token.tokenString);
            read();

            if (token.tokenType == Tokens.COMMA) {
                read();
            } else {
                break;
            }
        }

        if (grant) {
            if (token.tokenType == Tokens.WITH) {
                throw unsupportedFeature();
/*
                read();
                readThis(Token.ADMIN);
                readThis(Token.OPTION);
*/
            }
        }

        if (token.tokenType == Tokens.GRANTED) {
            read();
            readThis(Tokens.BY);

            if (token.tokenType == Tokens.CURRENT_USER) {
                read();

                //
            } else {
                readThis(Tokens.CURRENT_ROLE);

                if (session.getRole() == null) {
                    throw Error.error(ErrorCode.X_0P000);
                }

                grantor = session.getRole();
            }
        }

        if (!grant) {
            if (token.tokenType == Tokens.CASCADE) {
                cascade = true;

                read();
            } else {
                readThis(Tokens.RESTRICT);
            }
        }

        int             type = grant ? StatementTypes.GRANT_ROLE
                                     : StatementTypes.REVOKE_ROLE;
        Object[]        args = new Object[] {
            granteeList, roleList, grantor, Boolean.valueOf(cascade)
        };
        String          sql  = getLastPart();
        StatementSchema cs   = new StatementSchema(sql, type, args);

        return cs;
    }

    void checkSchemaUpdateAuthorisation(HsqlName schema) {

        if (session.isProcessingLog) {
            return;
        }

        SqlInvariants.checkSchemaNameNotSystem(schema.name);

        if (isSchemaDefinition) {
            if (schema != session.getCurrentSchemaHsqlName()) {
                throw Error.error(ErrorCode.X_42505);
            }
        } else {
            session.getGrantee().checkSchemaUpdateOrGrantRights(schema.name);
        }

        session.checkDDLWrite();
    }

    void checkDatabaseUpdateAuthorisation() {
        session.checkAdmin();
        session.checkDDLWrite();
    }

    StatementSchema compileComment() {

        HsqlName name;
        int      type;

        readThis(Tokens.COMMENT);
        readThis(Tokens.ON);

        switch (token.tokenType) {

            case Tokens.ROUTINE :
            case Tokens.TABLE : {
                type = token.tokenType == Tokens.ROUTINE ? SchemaObject.ROUTINE
                                                         : SchemaObject.TABLE;

                read();
                checkIsSchemaObjectName();

                name = database.nameManager.newHsqlName(token.tokenString,
                        token.isDelimitedIdentifier, type);

                if (token.namePrefix == null) {
                    name.schema = session.getCurrentSchemaHsqlName();
                } else {
                    name.schema = database.nameManager.newHsqlName(
                        token.namePrefix, token.isDelimitedPrefix,
                        SchemaObject.SCHEMA);
                }

                read();

                break;
            }
            case Tokens.COLUMN : {
                read();
                checkIsSchemaObjectName();

                name = database.nameManager.newHsqlName(token.tokenString,
                        token.isDelimitedIdentifier, SchemaObject.COLUMN);

                if (token.namePrefix == null) {
                    throw Error.error(ErrorCode.X_42501);
                }

                name.parent =
                    database.nameManager.newHsqlName(token.namePrefix,
                                                     token.isDelimitedPrefix,
                                                     SchemaObject.TABLE);

                if (token.namePrePrefix == null) {
                    name.parent.schema = session.getCurrentSchemaHsqlName();
                } else {
                    name.parent.schema = database.nameManager.newHsqlName(
                        token.namePrePrefix, token.isDelimitedPrePrefix,
                        SchemaObject.TABLE);
                }

                read();

                break;
            }
            default :
                throw unexpectedToken();
        }

        readThis(Tokens.IS);

        String   comment   = readQuotedString();
        Object[] arguments = new Object[] {
            name, comment
        };

        return new StatementSchema(null, StatementTypes.COMMENT, arguments);
    }
}
TOP

Related Classes of org.hsqldb.ParserDDL

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.