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

org.hsqldb.ParserDQL Maven / Gradle / Ivy

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


package org.hsqldb;

import org.hsqldb.HsqlNameManager.HsqlName;
import org.hsqldb.HsqlNameManager.SimpleName;
import org.hsqldb.error.Error;
import org.hsqldb.error.ErrorCode;
import org.hsqldb.lib.ArrayUtil;
import org.hsqldb.lib.HashMappedList;
import org.hsqldb.lib.HsqlArrayList;
import org.hsqldb.lib.HsqlList;
import org.hsqldb.lib.Iterator;
import org.hsqldb.lib.LongDeque;
import org.hsqldb.lib.OrderedHashSet;
import org.hsqldb.lib.OrderedIntKeyHashMap;
import org.hsqldb.map.BitMap;
import org.hsqldb.map.ValuePool;
import org.hsqldb.result.ResultConstants;
import org.hsqldb.result.ResultProperties;
import org.hsqldb.types.ArrayType;
import org.hsqldb.types.BinaryType;
import org.hsqldb.types.BlobType;
import org.hsqldb.types.CharacterType;
import org.hsqldb.types.Charset;
import org.hsqldb.types.ClobType;
import org.hsqldb.types.Collation;
import org.hsqldb.types.DTIType;
import org.hsqldb.types.IntervalType;
import org.hsqldb.types.NumberType;
import org.hsqldb.types.Type;
import org.hsqldb.types.Types;
import org.hsqldb.types.UserTypeModifier;

/**
 * Parser for DQL statements
 *
 * @author Fred Toussi (fredt@users dot sourceforge.net)
 * @version 2.5.0
 * @since 1.9.0
 */
public class ParserDQL extends ParserBase {

    protected Database             database;
    protected Session              session;
    protected final CompileContext compileContext;

    /**
     *  Constructs a new Parser object with the given context.
     *
     * @param  session the connected context
     * @param  scanner the token source from which to parse commands
     */
    ParserDQL(Session session, Scanner scanner, CompileContext baseContext) {

        super(scanner);

        this.session        = session;
        this.database       = session.getDatabase();
        this.compileContext = new CompileContext(session, this, baseContext);
    }

    /**
     *  Resets this parse context with the given SQL character sequence.
     *
     * @param sql a new SQL character sequence to replace the current one
     */
    void reset(Session session, String sql) {

        super.reset(session, sql);
        compileContext.reset();

        lastError = null;
    }

    void checkIsSchemaObjectName() {

        if (database.sqlEnforceNames) {
            checkIsNonReservedIdentifier();
        } else {
            checkIsNonCoreReservedIdentifier();
        }

        if (database.sqlRegularNames) {
            checkIsIrregularCharInIdentifier();
        }
    }

    Type readTypeDefinition(boolean allowCollation, boolean includeUserTypes) {

        int     typeNumber     = Integer.MIN_VALUE;
        boolean hasLength      = false;
        boolean hasScale       = false;
        boolean isCharacter    = false;
        boolean isIgnoreCase   = false;
        boolean readByteOrChar = false;
        boolean enforceSize    = database.sqlEnforceSize;

        checkIsIdentifier();

        if (token.namePrefix == null) {
            typeNumber = Type.getTypeNr(token.tokenString);
        }

        if (database.sqlSyntaxOra && !session.isProcessingScript()) {
            if (typeNumber == Types.SQL_DATE) {
                read();

                return Type.SQL_TIMESTAMP_NO_FRACTION;
            }
        }

        if (typeNumber == Integer.MIN_VALUE) {
            if (includeUserTypes) {
                checkIsSchemaObjectName();

                Type type = database.schemaManager.findDomainOrUDT(session,
                    token.tokenString, token.namePrefix, token.namePrePrefix,
                    token.namePrePrePrefix);

                if (type != null) {
                    getRecordedToken().setExpression(type);
                    compileContext.addSchemaObject(type);
                    read();

                    return type;
                }
            }

            if (token.namePrefix != null) {
                throw Error.error(ErrorCode.X_42509, token.tokenString);
            }

            if (database.sqlSyntaxOra) {
                switch (token.tokenType) {

                    case Tokens.BINARY_DOUBLE :
                    case Tokens.BINARY_FLOAT :
                        read();

                        return Type.SQL_DOUBLE;

                    case Tokens.LONG :
                        read();

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

                            return Type.getType(Types.SQL_VARBINARY, null,
                                                null,
                                                BlobType.defaultBlobSize, 0);
                        } else {
                            readIfThis(Tokens.VARCHAR);

                            return Type.getType(Types.SQL_VARCHAR, null,
                                                database.collation,
                                                ClobType.defaultClobSize, 0);
                        }
                    case Tokens.NUMBER :
                        read();

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

                            int precision = readInteger();
                            int scale     = 0;

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

                                scale = readInteger();
                            }

                            readThis(Tokens.CLOSEBRACKET);

                            return Type.getType(Types.SQL_DECIMAL, null, null,
                                                precision, scale);
                        } else {
                            return Type.SQL_DECIMAL_DEFAULT;
                        }
                    case Tokens.RAW :
                        typeNumber = Types.SQL_VARBINARY;
                        break;

                    case Tokens.VARCHAR2 :
                        readByteOrChar = true;
                        enforceSize    = false;
                        typeNumber     = Types.SQL_VARCHAR;
                        break;

                    case Tokens.NVARCHAR2 :
                        typeNumber = Types.SQL_VARCHAR;
                        break;

                    case Tokens.NCHAR :
                        typeNumber = Types.SQL_CHAR;
                        break;

                    default :
                }
            }

            if (database.sqlSyntaxMss) {
                if (Tokens.T_MONEY.equals(token.tokenString)) {
                    return Type.getType(Types.SQL_DECIMAL, null, null, 18, 4);
                } else if (Tokens.T_UNIQUEIDENTIFIER.equals(
                        token.tokenString)) {
                    return Type.SQL_GUID;
                } else if (Tokens.T_DATETIME2.equals(token.tokenString)) {
                    typeNumber = Types.SQL_TIMESTAMP;
                } else if (Tokens.T_IMAGE.equals(token.tokenString)) {
                    typeNumber = Types.LONGVARBINARY;
                } else if (Tokens.T_NTEXT.equals(token.tokenString)) {
                    typeNumber = Types.LONGVARCHAR;
                }

                switch (token.tokenType) {

                    case Tokens.DATETIMEOFFSET :
                        typeNumber = Types.SQL_TIMESTAMP_WITH_TIME_ZONE;
                        break;

                    case Tokens.TEXT :
                        typeNumber = Types.LONGVARCHAR;
                        break;
                }
            }

            if (database.sqlSyntaxPgs) {
                switch (token.tokenType) {

                    case Tokens.TEXT :
                        typeNumber     = Types.LONGVARCHAR;
                        readByteOrChar = true;
                        break;

                    case Tokens.CITEXT :
                        typeNumber = Types.VARCHAR_IGNORECASE;
                        break;
                }
            }

            if (database.sqlSyntaxMys) {
                switch (token.tokenType) {

                    case Tokens.ENUM :
                        return readMysEnum();

                    case Tokens.TINYTEXT :
                        typeNumber     = Types.VARCHAR;
                        readByteOrChar = true;
                        break;

                    case Tokens.TEXT :
                        typeNumber     = Types.LONGVARCHAR;
                        readByteOrChar = true;
                        break;

                    case Tokens.MEDIUMTEXT :
                    case Tokens.LONGTEXT :
                        typeNumber     = Types.LONGVARCHAR;
                        readByteOrChar = true;
                        break;

                    case Tokens.TINYBLOB :
                        typeNumber = Types.VARBINARY;
                        break;

                    case Tokens.MEDIUMBLOB :
                    case Tokens.LONGBLOB :
                        typeNumber = Types.LONGVARBINARY;
                        break;
                }
            }

            if (typeNumber == Integer.MIN_VALUE) {
                throw Error.error(ErrorCode.X_42509, token.tokenString);
            }
        }

        read();

        switch (typeNumber) {

            case Types.SQL_CHAR :
                if (token.tokenType == Tokens.VARYING) {
                    read();

                    typeNumber = Types.SQL_VARCHAR;
                } else if (token.tokenType == Tokens.LARGE) {
                    read();
                    readThis(Tokens.OBJECT);

                    typeNumber = Types.SQL_CLOB;
                } else {
                    if (database.sqlSyntaxOra) {
                        readByteOrChar = true;
                    }
                }
                break;

            case Types.SQL_DOUBLE :
                if (token.tokenType == Tokens.PRECISION) {
                    read();
                }
                break;

            case Types.SQL_BINARY :
                if (token.tokenType == Tokens.VARYING) {
                    read();

                    typeNumber = Types.SQL_VARBINARY;
                } else if (token.tokenType == Tokens.LARGE) {
                    read();
                    readThis(Tokens.OBJECT);

                    typeNumber = Types.SQL_BLOB;
                }
                break;

            case Types.SQL_BIT :
                if (token.tokenType == Tokens.VARYING) {
                    read();

                    typeNumber = Types.SQL_BIT_VARYING;
                }
                break;

            case Types.SQL_INTERVAL :
                return readIntervalType(session, false);

            default :
        }

        long length = typeNumber == Types.SQL_TIMESTAMP
                      ? DTIType.defaultTimestampFractionPrecision
                      : 0;
        int scale = 0;

        if (Types.requiresPrecision(typeNumber)
                && token.tokenType != Tokens.OPENBRACKET && enforceSize
                && !session.isProcessingScript()) {
            throw Error.error(ErrorCode.X_42599,
                              Type.getDefaultType(typeNumber).getNameString());
        }

        boolean acceptsPrecision = Types.acceptsPrecision(typeNumber);

        if (database.sqlSyntaxMys) {
            switch (typeNumber) {

                case Types.TINYINT :
                case Types.SQL_INTEGER :
                case Types.SQL_SMALLINT :
                case Types.SQL_BIGINT :
                    acceptsPrecision = true;
                    break;
            }
        }

        if (database.sqlSyntaxMss) {
            switch (typeNumber) {

                case Types.SQL_TIMESTAMP_WITH_TIME_ZONE :
                    acceptsPrecision = true;
                    break;
            }
        }

        if (acceptsPrecision) {
            if (token.tokenType == Tokens.OPENBRACKET) {
                int multiplier = 1;

                read();

                switch (token.tokenType) {

                    case Tokens.X_VALUE :
                        if (token.dataType.typeCode != Types.SQL_INTEGER
                                && token.dataType.typeCode
                                   != Types.SQL_BIGINT) {
                            throw unexpectedToken();
                        }
                        break;

                    case Tokens.X_LOB_SIZE :
                        if (typeNumber == Types.SQL_BLOB
                                || typeNumber == Types.SQL_CLOB
                                || typeNumber == Types.SQL_VARBINARY
                                || typeNumber == Types.SQL_VARCHAR) {
                            switch (token.lobMultiplierType) {

                                case Tokens.K :
                                    multiplier = 1024;
                                    break;

                                case Tokens.M :
                                    multiplier = 1024 * 1024;
                                    break;

                                case Tokens.G :
                                    multiplier = 1024 * 1024 * 1024;
                                    break;

                                case Tokens.P :
                                case Tokens.T :
                                default :
                                    throw unexpectedToken();
                            }

                            break;
                        } else {
                            throw unexpectedToken(token.getFullString());
                        }
                    case Tokens.MAX :
                        if (database.sqlSyntaxMss) {
                            token.tokenValue = Long.valueOf(Integer.MAX_VALUE);

                            break;
                        }

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

                hasLength = true;
                length    = ((Number) token.tokenValue).longValue();

                if (length < 0
                        || (length == 0
                            && !Types.acceptsZeroPrecision(typeNumber))) {
                    throw Error.error(ErrorCode.X_42592);
                }

                length *= multiplier;

                read();

                if (typeNumber == Types.SQL_CHAR
                        || typeNumber == Types.SQL_VARCHAR
                        || typeNumber == Types.SQL_CLOB) {
                    if (token.tokenType == Tokens.CHARACTERS) {
                        read();
                    } else if (token.tokenType == Tokens.OCTETS) {
                        read();
                    }
                }

                if (Types.acceptsScaleCreateParam(typeNumber)
                        && token.tokenType == Tokens.COMMA) {
                    read();

                    scale = readInteger();

                    if (scale < 0) {
                        if (typeNumber == Types.SQL_DECIMAL
                                && database.sqlSyntaxOra) {

                            //
                        } else {
                            throw Error.error(ErrorCode.X_42592);
                        }
                    }

                    if (typeNumber == Types.SQL_DECIMAL && scale > length) {
                        throw Error.error(ErrorCode.X_42592);
                    }

                    hasScale = true;
                }

                if (readByteOrChar) {
                    if (!readIfThis(Tokens.CHAR)) {
                        readIfThis(Tokens.BYTE);
                    }
                }

                readThis(Tokens.CLOSEBRACKET);
            } else if (typeNumber == Types.SQL_BIT) {
                length = 1;
            } else if (typeNumber == Types.SQL_BLOB
                       || typeNumber == Types.SQL_CLOB) {
                length = BlobType.defaultBlobSize;
            } else if (enforceSize) {

                // BIT is always BIT(1), regardless of sqlEnforceSize
                if (typeNumber == Types.SQL_CHAR
                        || typeNumber == Types.SQL_BINARY) {
                    length = 1;
                }
            }

            switch (typeNumber) {

                case Types.SQL_TIMESTAMP_WITH_TIME_ZONE :
                case Types.SQL_TIMESTAMP :
                case Types.SQL_TIME :
                    if (length > DTIType.maxFractionPrecision) {
                        throw Error.error(ErrorCode.X_42592);
                    }

                    scale  = (int) length;
                    length = 0;

                    if (typeNumber == Types.SQL_TIMESTAMP_WITH_TIME_ZONE) {
                        break;
                    }

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

                        if (typeNumber == Types.SQL_TIMESTAMP) {
                            typeNumber = Types.SQL_TIMESTAMP_WITH_TIME_ZONE;
                        } else {
                            typeNumber = Types.SQL_TIME_WITH_TIME_ZONE;
                        }
                    } else if (token.tokenType == Tokens.WITHOUT) {
                        read();
                        readThis(Tokens.TIME);
                        readThis(Tokens.ZONE);
                    }
            }
        }

        switch (typeNumber) {

            case Types.LONGVARCHAR : {
                if (database.sqlLongvarIsLob) {
                    typeNumber = Types.SQL_CLOB;
                    length     = ClobType.defaultClobSize;
                } else {
                    typeNumber = Types.SQL_VARCHAR;

                    if (!hasLength) {
                        length = ClobType.defaultShortClobSize;
                    }
                }

                break;
            }
            case Types.LONGVARBINARY : {
                if (database.sqlLongvarIsLob) {
                    typeNumber = Types.SQL_BLOB;
                    length     = BlobType.defaultBlobSize;
                } else {
                    typeNumber = Types.SQL_VARBINARY;

                    if (!hasLength) {
                        length = BlobType.defaultShortBlobSize;
                    }
                }

                break;
            }
            case Types.SQL_CHAR :
                if (database.sqlSyntaxDb2) {
                    if (readIfThis(Tokens.FOR)) {
                        readThis(Tokens.BIT);
                        readThis(Tokens.DATA);

                        typeNumber = Types.SQL_BINARY;

                        break;
                    }
                }

                isCharacter = true;
                break;

            case Types.SQL_CLOB :
                isCharacter = true;
                break;

            case Types.VARCHAR_IGNORECASE :
                typeNumber   = Types.SQL_VARCHAR;
                isIgnoreCase = true;

            // fall through
            case Types.SQL_VARCHAR :
                if (database.sqlSyntaxDb2) {
                    if (readIfThis(Tokens.FOR)) {
                        readThis(Tokens.BIT);
                        readThis(Tokens.DATA);

                        typeNumber = Types.SQL_VARBINARY;

                        if (!hasLength) {
                            length = 32 * 1024;
                        }

                        break;
                    }
                }

                isCharacter = true;

                if (!hasLength) {
                    length = 32 * 1024;
                }

                if (session.isIgnorecase()) {
                    if (!session.isProcessingScript()) {
                        isIgnoreCase = true;
                    }
                }

                if (length > CharacterType.maxCharPrecision) {
                    throw Error.error(ErrorCode.X_42592);
                }
                break;

            case Types.SQL_BINARY :
                break;

            case Types.SQL_VARBINARY :
                if (!hasLength) {
                    length = 32 * 1024;
                }

                if (length > BinaryType.maxBinaryPrecision) {
                    throw Error.error(ErrorCode.X_42592);
                }
                break;

            case Types.SQL_DECIMAL :
            case Types.SQL_NUMERIC :
                if (!hasLength && !hasScale && !enforceSize) {
                    length = NumberType.defaultNumericPrecision;
                    scale  = NumberType.defaultNumericScale;
                }
                break;
        }

        Collation collation = database.collation;
        Charset   charset   = null;

        if (isCharacter && allowCollation) {
            if (token.tokenType == Tokens.CHARACTER) {
                read();
                readThis(Tokens.SET);
                checkIsSchemaObjectName();

                charset =
                    (Charset) database.schemaManager.getCharacterSet(session,
                        token.tokenString, token.namePrefix);

                read();
            }

            if (token.tokenType == Tokens.COLLATE) {
                read();
                checkIsSchemaObjectName();

                collation = database.schemaManager.getCollation(session,
                        token.tokenString, token.namePrefix);

                read();
            } else if (isIgnoreCase) {
                collation = Collation.getUpperCaseCompareCollation(collation);
            }
        }

        Type typeObject = Type.getType(typeNumber, charset, collation, length,
                                       scale);

        if (token.tokenType == Tokens.ARRAY) {
            if (typeObject.isLobType()) {
                throw unexpectedToken();
            }

            read();

            int maxCardinality = ArrayType.defaultArrayCardinality;

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

                maxCardinality = readInteger();

                if (maxCardinality < 0) {
                    throw Error.error(ErrorCode.X_42592);
                }

                readThis(Tokens.RIGHTBRACKET);
            }

            typeObject = new ArrayType(typeObject, maxCardinality);
        }

        return typeObject;
    }

    Type readMysEnum() {

        read();
        checkIsThis(Tokens.OPENBRACKET);

        HsqlName name = database.nameManager.newHsqlName("ENUM", false,
            SchemaObject.DOMAIN);
        Type t = Type.getType(Types.SQL_VARCHAR, null, null, 32, 0);
        UserTypeModifier m = new UserTypeModifier(name, SchemaObject.DOMAIN,
            t);

        t.userTypeModifier = m;

        return t;
    }

    void readSimpleColumnNames(OrderedHashSet columns, RangeVariable rangeVar,
                               boolean withPrefix) {

        while (true) {
            ColumnSchema col = readSimpleColumnName(rangeVar, withPrefix);

            if (!columns.add(col.getName().name)) {
                throw Error.error(ErrorCode.X_42579, col.getName().name);
            }

            if (readIfThis(Tokens.COMMA)) {
                continue;
            }

            if (token.tokenType == Tokens.CLOSEBRACKET) {
                break;
            }

            throw unexpectedToken();
        }
    }

    void readTargetSpecificationList(OrderedHashSet targets,
                                     RangeVariable[] rangeVars,
                                     LongDeque colIndexList) {

        while (true) {
            Expression target = XreadTargetSpecification(rangeVars,
                colIndexList);

            if (!targets.add(target)) {
                ColumnSchema col = target.getColumn();

                throw Error.error(ErrorCode.X_42579, col.getName().name);
            }

            if (readIfThis(Tokens.COMMA)) {
                continue;
            }

            if (token.tokenType == Tokens.CLOSEBRACKET) {
                break;
            }

            if (token.tokenType == Tokens.FROM) {
                break;
            }

            throw unexpectedToken();
        }
    }

    /**
     * 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
     */
    int[] readColumnList(Table table, boolean ascOrDesc) {

        OrderedHashSet set = readColumnNames(ascOrDesc);

        return table.getColumnIndexes(set);
    }

    void readSimpleColumnNames(OrderedHashSet columns, Table table,
                               boolean withPrefix) {

        while (true) {
            ColumnSchema col = readSimpleColumnName(table, withPrefix);

            if (!columns.add(col.getName().name)) {
                throw Error.error(ErrorCode.X_42577, col.getName().name);
            }

            if (readIfThis(Tokens.COMMA)) {
                continue;
            }

            if (token.tokenType == Tokens.CLOSEBRACKET) {
                break;
            }

            throw unexpectedToken();
        }
    }

    HsqlName[] readColumnNames(HsqlName tableName) {

        BitMap         quotedFlags = new BitMap(0, true);
        OrderedHashSet set         = readColumnNames(quotedFlags, false);
        HsqlName[]     colList     = new HsqlName[set.size()];

        for (int i = 0; i < colList.length; i++) {
            String  name   = (String) set.get(i);
            boolean quoted = quotedFlags.isSet(i);

            colList[i] = database.nameManager.newHsqlName(tableName.schema,
                    name, quoted, SchemaObject.COLUMN, tableName);
        }

        return colList;
    }

    OrderedHashSet readColumnNames(boolean readAscDesc) {
        return readColumnNames(null, readAscDesc);
    }

    OrderedHashSet readColumnNames(BitMap quotedFlags, boolean readAscDesc) {

        readThis(Tokens.OPENBRACKET);

        OrderedHashSet set = new OrderedHashSet();

        readColumnNameList(set, quotedFlags, readAscDesc);
        readThis(Tokens.CLOSEBRACKET);

        return set;
    }

    void readColumnNameList(OrderedHashSet set, BitMap quotedFlags,
                            boolean readAscDesc) {

        int i = 0;

        while (true) {
            if (session.isProcessingScript()) {

                // for old scripts
                if (!isSimpleName()) {
                    token.isDelimitedIdentifier = true;
                }
            } else {
                checkIsSimpleName();
            }

            if (!set.add(token.tokenString)) {
                throw Error.error(ErrorCode.X_42577, token.tokenString);
            }

            if (quotedFlags != null) {
                quotedFlags.setValue(i, isDelimitedIdentifier());
            }

            read();

            i++;

            if (readAscDesc) {
                if (token.tokenType == Tokens.ASC
                        || token.tokenType == Tokens.DESC) {
                    read();
                }
            }

            if (readIfThis(Tokens.COMMA)) {
                continue;
            }

            break;
        }
    }

    SimpleName[] readColumnNameList(OrderedHashSet set) {

        BitMap columnNameQuoted = new BitMap(0, true);

        readThis(Tokens.OPENBRACKET);
        readColumnNameList(set, columnNameQuoted, false);
        readThis(Tokens.CLOSEBRACKET);

        SimpleName[] columnNameList = new SimpleName[set.size()];

        for (int i = 0; i < set.size(); i++) {
            SimpleName name =
                HsqlNameManager.getSimpleName((String) set.get(i),
                                              columnNameQuoted.isSet(i));

            columnNameList[i] = name;
        }

        return columnNameList;
    }

    int XreadUnionType() {

        int unionType = QueryExpression.NOUNION;

        switch (token.tokenType) {

            case Tokens.UNION :
                read();

                unionType = QueryExpression.UNION;

                if (token.tokenType == Tokens.ALL) {
                    unionType = QueryExpression.UNION_ALL;

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

            case Tokens.INTERSECT :
                read();

                unionType = QueryExpression.INTERSECT;

                if (token.tokenType == Tokens.ALL) {
                    unionType = QueryExpression.INTERSECT_ALL;

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

            case Tokens.EXCEPT :
            case Tokens.MINUS_EXCEPT :
                read();

                unionType = QueryExpression.EXCEPT;

                if (token.tokenType == Tokens.ALL) {
                    unionType = QueryExpression.EXCEPT_ALL;

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

            default :
                break;
        }

        return unionType;
    }

    void XreadUnionCorrespondingClause(QueryExpression queryExpression) {

        if (token.tokenType == Tokens.CORRESPONDING) {
            read();
            queryExpression.setUnionCorresoponding();

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

                OrderedHashSet names = readColumnNames(false);

                queryExpression.setUnionCorrespondingColumns(names);
            }
        }
    }

    QueryExpression XreadQueryExpression() {

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

            boolean recursive = readIfThis(Tokens.RECURSIVE);

            compileContext.unregisterSubqueries();

            while (true) {
                checkIsSimpleName();

                HsqlName[] nameList = null;
                HsqlName queryName =
                    database.nameManager.newHsqlName(token.tokenString,
                                                     isDelimitedIdentifier(),
                                                     SchemaObject.SUBQUERY);

                queryName.schema = SqlInvariants.SYSTEM_SCHEMA_HSQLNAME;

                read();
                compileContext.registerSubquery(queryName.name);

                if (token.tokenType == Tokens.OPENBRACKET) {
                    nameList = readColumnNames(queryName);
                } else if (recursive) {
                    throw unexpectedTokenRequire(Tokens.T_OPENBRACKET);
                }

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

                TableDerived td;

                td = XreadTableNamedSubqueryBody(queryName, nameList,
                                                 recursive
                                                 ? OpTypes.RECURSIVE_SUBQUERY
                                                 : OpTypes.TABLE_SUBQUERY);

                readThis(Tokens.CLOSEBRACKET);

                if (token.tokenType == Tokens.CYCLE) {
                    throw unsupportedFeature();
                }

                if (recursive && token.tokenType == Tokens.CYCLE) {
                    Table    table           = td;
                    int[]    cycleColumnList = readColumnList(table, false);
                    HsqlName name;

                    readThis(Tokens.SET);
                    checkIsSimpleName();

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

                    ColumnSchema cycleMarkColumn = new ColumnSchema(name,
                        null, true, false, null);

                    if (table.getColumnIndex(name.name) != -1) {
                        throw Error.error(ErrorCode.X_42578,
                                          token.tokenString);
                    }

                    read();
                    readThis(Tokens.TO);

                    String cycleMarkValue = readQuotedString();

                    if (cycleMarkValue.length() != 1) {
                        throw unexpectedToken(cycleMarkValue);
                    }

                    readThis(Tokens.DEFAULT);

                    String noncycleMarkValue = readQuotedString();

                    if (noncycleMarkValue.length() != 1) {
                        throw unexpectedToken(noncycleMarkValue);
                    }

                    if (cycleMarkValue.equals(noncycleMarkValue)) {
                        throw unexpectedToken(cycleMarkValue);
                    }

                    readThis(Tokens.USING);
                    checkIsSimpleName();
                    checkIsSimpleName();

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

                    if (table.getColumnIndex(name.name) != -1) {
                        throw Error.error(ErrorCode.X_42578,
                                          token.tokenString);
                    }

                    read();

                    ColumnSchema pathColumn = new ColumnSchema(name, null,
                        true, false, null);
                }

                compileContext.registerSubquery(queryName.name, td);

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

                    continue;
                }

                break;
            }
        }

        QueryExpression queryExpression = XreadQueryExpressionBody();
        SortAndSlice    sortAndSlice    = XreadOrderByExpression();

        if (queryExpression.sortAndSlice == SortAndSlice.noSort) {
            queryExpression.addSortAndSlice(sortAndSlice);
        } else {
            if (queryExpression.sortAndSlice.hasLimit()) {
                if (sortAndSlice.hasLimit()) {
                    throw Error.error(ErrorCode.X_42549);
                }

                for (int i = 0; i < sortAndSlice.exprList.size(); i++) {
                    Expression e = (Expression) sortAndSlice.exprList.get(i);

                    queryExpression.sortAndSlice.addOrderExpression(e);
                }
            } else {
                if (sortAndSlice != SortAndSlice.noSort) {
                    queryExpression.addSortAndSlice(sortAndSlice);
                }
            }
        }

        compileContext.unregisterSubqueries();

        return queryExpression;
    }

    QueryExpression XreadQueryExpressionBody() {

        QueryExpression queryExpression = XreadQueryTerm();

        while (true) {
            switch (token.tokenType) {

                case Tokens.UNION :
                case Tokens.EXCEPT :
                case Tokens.MINUS_EXCEPT : {
                    queryExpression = XreadSetOperation(queryExpression);

                    break;
                }
                default : {
                    return queryExpression;
                }
            }
        }
    }

    QueryExpression XreadQueryTerm() {

        QueryExpression queryExpression = XreadQueryPrimary();

        while (true) {
            if (token.tokenType == Tokens.INTERSECT) {
                queryExpression = XreadSetOperation(queryExpression);
            } else {
                return queryExpression;
            }
        }
    }

    private QueryExpression XreadSetOperation(
            QueryExpression queryExpression) {

        queryExpression = new QueryExpression(compileContext, queryExpression);

        int unionType = XreadUnionType();

        XreadUnionCorrespondingClause(queryExpression);

        QueryExpression rightQueryExpression = XreadQueryTerm();

        queryExpression.addUnion(rightQueryExpression, unionType);

        return queryExpression;
    }

    QueryExpression XreadQueryPrimary() {

        switch (token.tokenType) {

            case Tokens.TABLE :
            case Tokens.VALUES :
            case Tokens.SELECT : {
                QuerySpecification select = XreadSimpleTable();

                return select;
            }
            case Tokens.OPENBRACKET : {
                read();

                QueryExpression queryExpression = XreadQueryExpressionBody();
                SortAndSlice    sortAndSlice    = XreadOrderByExpression();

                readThis(Tokens.CLOSEBRACKET);

                if (queryExpression.sortAndSlice == SortAndSlice.noSort) {
                    queryExpression.addSortAndSlice(sortAndSlice);
                } else {
                    if (queryExpression.sortAndSlice.hasLimit()) {
                        if (sortAndSlice.hasLimit()) {
                            throw Error.error(ErrorCode.X_42549);
                        }

                        for (int i = 0; i < sortAndSlice.exprList.size();
                                i++) {
                            Expression e =
                                (Expression) sortAndSlice.exprList.get(i);

                            queryExpression.sortAndSlice.addOrderExpression(e);
                        }
                    } else {
                        if (sortAndSlice != SortAndSlice.noSort) {
                            queryExpression.addSortAndSlice(sortAndSlice);
                        }
                    }
                }

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

    QuerySpecification XreadSimpleTable() {

        QuerySpecification select;

        switch (token.tokenType) {

            case Tokens.TABLE : {
                read();

                Table table = readNamedSubqueryOrNull();

                if (table == null) {
                    table = readTableName(true);
                }

                if (table.isView()) {
                    table = ((View) table).newDerivedTable(session,
                                                           compileContext);
                }

                select = new QuerySpecification(session, table,
                                                compileContext, false);

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

                TableDerived td = XreadRowValueExpressionList();

                select = new QuerySpecification(session, td, compileContext,
                                                true);

                break;
            }
            case Tokens.SELECT : {
                select = XreadQuerySpecification();

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

        return select;
    }

    QuerySpecification XreadQuerySpecification() {

        QuerySpecification select = XreadSelect();

        if (!select.isValueList
                && select.getCurrentRangeVariableCount() == 0) {
            XreadTableExpression(select);
        }

        return select;
    }

    void XreadTableExpression(QuerySpecification select) {
        XreadFromClause(select);
        readWhereGroupHaving(select);
    }

    QuerySpecification XreadSelect() {

        QuerySpecification select = new QuerySpecification(compileContext);

        readThis(Tokens.SELECT);

        if (token.tokenType == Tokens.TOP || token.tokenType == Tokens.LIMIT) {
            SortAndSlice sortAndSlice = XreadTopOrLimit();

            if (sortAndSlice != null) {
                select.addSortAndSlice(sortAndSlice);
            }
        }

        if (token.tokenType == Tokens.DISTINCT) {
            select.setDistinctSelect();
            read();
        } else if (token.tokenType == Tokens.ALL) {
            read();
        }

        while (true) {
            Expression e = XreadValueExpression();

            if (token.tokenType == Tokens.AS) {
                if (e.getType() == OpTypes.MULTICOLUMN) {
                    throw unexpectedToken();
                }

                read();
                checkIsNonCoreReservedIdentifier();
            }

            if (isNonCoreReservedIdentifier()) {
                if (e.getType() == OpTypes.MULTICOLUMN) {
                    throw unexpectedToken();
                }

                e.setAlias(HsqlNameManager.getSimpleName(token.tokenString,
                        isDelimitedIdentifier()));
                read();
            }

            select.addSelectColumnExpression(e);

            if (token.tokenType == Tokens.FROM) {
                break;
            }

            if (token.tokenType == Tokens.INTO) {
                break;
            }

            if (readIfThis(Tokens.COMMA)) {
                continue;
            }

            if (token.tokenType == Tokens.CLOSEBRACKET
                    || token.tokenType == Tokens.X_ENDPARSE
                    || token.tokenType == Tokens.SEMICOLON) {
                if (database.sqlSyntaxMss || database.sqlSyntaxMys
                        || database.sqlSyntaxPgs) {
                    RangeVariable range =
                        new RangeVariable(database.schemaManager.dualTable,
                                          null, null, null, compileContext);

                    select.addRangeVariable(session, range);

                    return select;
                }
            }

            throw unexpectedToken();
        }

        return select;
    }

    void XreadFromClause(QuerySpecification select) {

        readThis(Tokens.FROM);

        while (true) {
            XreadTableReference(select);

            if (readIfThis(Tokens.COMMA)) {

                // set last range as boundary
                continue;
            }

            break;
        }
    }

    void XreadTableReference(QuerySpecification select) {

        boolean       natural   = false;
        RangeVariable leftRange = readTableOrSubquery();

        select.addRangeVariable(session, leftRange);

        while (true) {
            int     type;
            boolean left  = false;
            boolean right = false;
            boolean end   = false;

            type = token.tokenType;

            switch (token.tokenType) {

                case Tokens.NATURAL :
                    if (natural) {
                        throw unexpectedToken();
                    }

                    read();

                    natural = true;

                    continue;
                case Tokens.INNER :
                    read();
                    readThis(Tokens.JOIN);
                    break;

                case Tokens.CROSS :
                    if (natural) {
                        throw unexpectedToken();
                    }

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

                case Tokens.UNION :
                    if (natural) {
                        throw unexpectedToken();
                    }

                    int position = getPosition();

                    read();

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

                        left  = true;
                        right = true;

                        break;
                    } else {
                        rewind(position);

                        end = true;

                        break;
                    }
                case Tokens.LEFT :
                    read();
                    readIfThis(Tokens.OUTER);
                    readThis(Tokens.JOIN);

                    left = true;
                    break;

                case Tokens.RIGHT :
                    read();
                    readIfThis(Tokens.OUTER);
                    readThis(Tokens.JOIN);

                    right = true;
                    break;

                case Tokens.FULL :
                    read();
                    readIfThis(Tokens.OUTER);
                    readThis(Tokens.JOIN);

                    left  = true;
                    right = true;
                    break;

                case Tokens.JOIN :
                    read();

                    type = Tokens.INNER;
                    break;

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

                    end = true;
                    break;
            }

            if (end) {
                break;
            }

            RangeVariable rightRange = readTableOrSubquery();
            Expression    condition  = null;

            rightRange.setJoinType(left, right);

            switch (type) {

                case Tokens.CROSS :
                    select.addRangeVariable(session, rightRange);
                    break;

                case Tokens.UNION :
                    condition = Expression.EXPR_FALSE;

                    rightRange.addJoinCondition(condition);
                    select.addRangeVariable(session, rightRange);
                    break;

                case Tokens.LEFT :
                case Tokens.RIGHT :
                case Tokens.INNER :
                case Tokens.FULL : {
                    boolean using = token.tokenType == Tokens.USING;

                    if (natural || using) {
                        leftRange.resolveRangeTable(
                            session, RangeGroup.emptyGroup,
                            compileContext.getOuterRanges());
                        rightRange.resolveRangeTable(
                            session, RangeGroup.emptyGroup,
                            compileContext.getOuterRanges());
                    }

                    if (natural) {
                        OrderedHashSet columns =
                            rightRange.getUniqueColumnNameSet();

                        condition = select.getEquiJoinExpressions(columns,
                                rightRange, false);

                        rightRange.addJoinCondition(condition);
                        select.addRangeVariable(session, rightRange);
                    } else if (using) {
                        read();

                        OrderedHashSet columns = new OrderedHashSet();

                        readThis(Tokens.OPENBRACKET);
                        readSimpleColumnNames(columns, rightRange, false);
                        readThis(Tokens.CLOSEBRACKET);

                        condition = select.getEquiJoinExpressions(columns,
                                rightRange, true);

                        rightRange.addJoinCondition(condition);
                        select.addRangeVariable(session, rightRange);
                    } else if (token.tokenType == Tokens.ON) {
                        read();

                        condition = XreadBooleanValueExpression();

                        rightRange.addJoinCondition(condition);
                        select.addRangeVariable(session, rightRange);
                    } else {
                        throw unexpectedToken();
                    }

                    break;
                }
            }

            natural = false;
        }
    }

    Expression getRowExpression(OrderedHashSet columnNames) {

        Expression[] elements = new Expression[columnNames.size()];

        for (int i = 0; i < elements.length; i++) {
            String name = (String) columnNames.get(i);

            elements[i] = new ExpressionColumn(null, null, name);
        }

        return new Expression(OpTypes.ROW, elements);
    }

    void readWhereGroupHaving(QuerySpecification select) {

        // where
        if (token.tokenType == Tokens.WHERE) {
            read();

            Expression e = XreadBooleanValueExpression();

            select.addQueryCondition(e);
        }

        // group by
        if (token.tokenType == Tokens.GROUP) {
            read();
            readThis(Tokens.BY);

            while (true) {
                Expression e = XreadValueExpression();

                select.addGroupByColumnExpression(e);

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

                    continue;
                }

                break;
            }
        }

        // having
        if (token.tokenType == Tokens.HAVING) {
            read();

            Expression e = XreadBooleanValueExpression();

            select.addHavingExpression(e);
        }
    }

    SortAndSlice XreadOrderByExpression() {

        SortAndSlice sortAndSlice = null;

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

            sortAndSlice = XreadOrderBy();
        }

        if (token.tokenType == Tokens.LIMIT || token.tokenType == Tokens.FETCH
                || token.tokenType == Tokens.OFFSET) {
            if (sortAndSlice == null) {
                sortAndSlice = new SortAndSlice();
            }

            XreadLimit(sortAndSlice);
        }

        return sortAndSlice == null ? SortAndSlice.noSort
                                    : sortAndSlice;
    }

    private SortAndSlice XreadTopOrLimit() {

        Expression e1 = null;
        Expression e2 = null;

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

            read();

            e1 = XreadSimpleValueSpecificationOrNull();

            if (e1 == null) {
                rewind(position);

                return null;
            }

            // optional comma
            readIfThis(Tokens.COMMA);

            e2 = XreadSimpleValueSpecificationOrNull();

            if (e2 == null) {
                throw Error.error(ErrorCode.X_42563,
                                  ErrorCode.M_INVALID_LIMIT);
            }
        } else if (token.tokenType == Tokens.TOP) {
            int position = getPosition();

            read();

            e2 = XreadSimpleValueSpecificationOrNull();

            if (e2 == null) {
                rewind(position);

                return null;
            }

            e1 = new ExpressionValue(ValuePool.INTEGER_0, Type.SQL_INTEGER);
        } else {
            throw unexpectedToken();
        }

        boolean valid = true;

        if (e1.isUnresolvedParam()) {
            e1.setDataType(session, Type.SQL_INTEGER);
        } else if (e1.opType == OpTypes.VALUE) {
            valid = (e1.getDataType().typeCode == Types.SQL_INTEGER
                     && ((Integer) e1.getValue(null)).intValue() >= 0);
        } else {
            throw Error.error(ErrorCode.X_42563, ErrorCode.M_INVALID_LIMIT);
        }

        if (e2.isUnresolvedParam()) {
            e2.setDataType(session, Type.SQL_INTEGER);
        } else if (e2.opType == OpTypes.VALUE) {
            valid &= (e2.getDataType().typeCode == Types.SQL_INTEGER
                      && ((Integer) e2.getValue(null)).intValue() >= 0);
        } else {
            throw Error.error(ErrorCode.X_42563, ErrorCode.M_INVALID_LIMIT);
        }

        if (valid) {
            SortAndSlice sortAndSlice = new SortAndSlice();

            sortAndSlice.addLimitCondition(new ExpressionOp(OpTypes.LIMIT, e1,
                    e2));

            return sortAndSlice;
        }

        throw Error.error(ErrorCode.X_42563, ErrorCode.M_INVALID_LIMIT);
    }

    private void XreadLimit(SortAndSlice sortAndSlice) {

        Expression e1 = null;
        Expression e2 = null;

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

            e1 = XreadSimpleValueSpecificationOrNull();

            if (e1 == null) {
                throw Error.error(ErrorCode.X_42563,
                                  ErrorCode.M_INVALID_LIMIT);
            }

            if (token.tokenType == Tokens.ROW
                    || token.tokenType == Tokens.ROWS) {
                read();
            }
        }

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

            e2 = XreadSimpleValueSpecificationOrNull();

            if (e2 == null) {
                throw Error.error(ErrorCode.X_42563,
                                  ErrorCode.M_INVALID_LIMIT);
            }

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

                    e1 = e2;
                    e2 = XreadSimpleValueSpecificationOrNull();
                } else if (token.tokenType == Tokens.OFFSET) {
                    read();

                    e1 = XreadSimpleValueSpecificationOrNull();
                }
            }

            if (database.sqlSyntaxPgs || database.sqlSyntaxMys) {
                sortAndSlice.setZeroLimit();
            }
        } else if (token.tokenType == Tokens.FETCH) {
            read();

            if (token.tokenType == Tokens.FIRST
                    || token.tokenType == Tokens.NEXT) {
                read();
            }

            e2 = XreadSimpleValueSpecificationOrNull();

            if (e2 == null) {
                e2 = new ExpressionValue(ValuePool.INTEGER_1,
                                         Type.SQL_INTEGER);
            }

            if (token.tokenType == Tokens.ROW
                    || token.tokenType == Tokens.ROWS) {
                read();
            }

            readThis(Tokens.ONLY);
            sortAndSlice.setStrictLimit();
        }

        if (sortAndSlice.hasOrder() && token.tokenType == Tokens.USING) {
            read();
            readThis(Tokens.INDEX);
            sortAndSlice.setUsingIndex();
        }

        if (e1 == null) {
            e1 = new ExpressionValue(ValuePool.INTEGER_0, Type.SQL_INTEGER);
        }

        boolean valid = true;

        if (e1.isUnresolvedParam()) {
            e1.setDataType(session, Type.SQL_INTEGER);
        }

        if (e2 != null) {
            if (e2.isUnresolvedParam()) {
                e2.setDataType(session, Type.SQL_INTEGER);
            }
        }

        if (valid) {
            sortAndSlice.addLimitCondition(new ExpressionOp(OpTypes.LIMIT, e1,
                    e2));

            return;
        }

        throw Error.error(ErrorCode.X_42563, ErrorCode.M_INVALID_LIMIT);
    }

    private SortAndSlice XreadOrderBy() {

        SortAndSlice sortAndSlice = new SortAndSlice();

        while (true) {
            Expression        e;
            ExpressionOrderBy o;
            boolean           isDesc    = false;
            boolean           nullsLast = false;

            e = XreadValueExpression();
            o = new ExpressionOrderBy(e);

            if (token.tokenType == Tokens.DESC) {
                o.setDescending();

                isDesc = true;

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

            if (database.sqlNullsOrder) {
                nullsLast = !database.sqlNullsFirst;
            } else {
                nullsLast = database.sqlNullsFirst == isDesc;
            }

            o.setNullsLast(nullsLast);

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

                if (token.tokenType == Tokens.FIRST) {
                    read();
                    o.setNullsLast(false);
                } else if (token.tokenType == Tokens.LAST) {
                    read();
                    o.setNullsLast(true);
                } else {
                    throw unexpectedToken();
                }
            }

            sortAndSlice.addOrderExpression(o);

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

                continue;
            }

            break;
        }

        return sortAndSlice;
    }

    protected RangeVariable readRangeVariableForDataChange(int operation) {

        Table table = readTableName(true);
        ExpressionPeriodOp appPeriodSpec =
            XreadQueryApplicationPeriodSpecOrNull(table);
        SimpleName alias = null;

        if (appPeriodSpec != null) {
            throw Error.error(ErrorCode.X_0A501);
        }

        if (operation != StatementTypes.TRUNCATE) {
            switch (token.tokenType) {

                case Tokens.OVERRIDING :
                    break;

                case Tokens.AS :
                    read();
                    checkIsNonCoreReservedIdentifier();

                // fall through
                default :
                    if (isNonCoreReservedIdentifier()) {
                        if (!database.sqlSyntaxMys
                                || operation != StatementTypes.INSERT) {
                            alias = HsqlNameManager.getSimpleName(
                                token.tokenString, isDelimitedIdentifier());

                            read();
                        }
                    }
            }

            if (alias == null && lastSynonym != null) {
                alias =
                    HsqlNameManager.getSimpleName(lastSynonym.name,
                                                  lastSynonym.isNameQuoted);
            }
        }

        if (table.isView) {
            switch (operation) {

                case StatementTypes.MERGE :
                    if (table.isTriggerUpdatable()
                            && table.isTriggerInsertable()) {
                        break;
                    }

                    if (table.isTriggerUpdatable()
                            || table.isTriggerInsertable()) {

                        // all or nothing
                    } else if (table.isUpdatable() && table.isInsertable()) {
                        break;
                    }

                    throw Error.error(ErrorCode.X_42545);
                case StatementTypes.UPDATE_WHERE :
                    if (table.isTriggerUpdatable()) {
                        break;
                    }

                    if (table.isUpdatable()) {
                        break;
                    }

                    throw Error.error(ErrorCode.X_42545);
                case StatementTypes.DELETE_WHERE :
                    if (table.isTriggerDeletable()) {
                        break;
                    }

                    if (table.isUpdatable()) {
                        break;
                    }

                    throw Error.error(ErrorCode.X_42545);
                case StatementTypes.INSERT :
                    if (table.isTriggerInsertable()) {
                        break;
                    }

                    if (table.isInsertable()) {
                        break;
                    }

                    if (session.isProcessingScript()) {
                        break;
                    }

                    throw Error.error(ErrorCode.X_42545);
                case StatementTypes.TRUNCATE :
                    throw Error.error(ErrorCode.X_42545);
            }

            table = ((View) table).newDerivedTable(session, compileContext);
        }

        RangeVariable range = new RangeVariable(table, alias, null, null,
            compileContext);

        if (table.isSystemVersioned()) {
            ExpressionPeriodOp sysPeriodSpec = new ExpressionPeriodOp();

            sysPeriodSpec.setSystemRangeVariable(session, range);
            range.setSystemPeriodCondition(sysPeriodSpec);
        }

        return range;
    }

    protected Table readNamedSubqueryOrNull() {

        if (!isSimpleName()) {
            return null;
        }

        TableDerived td = compileContext.getNamedSubQuery(token.tokenString);

        if (td == null) {
            return null;
        }

        read();

        if (td.isRecompiled()) {
            td = td.newDerivedTable(session, compileContext);
        }

        return td;
    }

    /**
     * Creates a RangeVariable from the parse context. 

*/ protected RangeVariable readTableOrSubquery() { Table table = null; SimpleName alias = null; SimpleName[] columnNameList = null; OrderedHashSet columnList = null; ExpressionPeriodOp sysPeriodSpec = null; boolean joinedTable = false; boolean isLateral = false; boolean isTableName = false; switch (token.tokenType) { case Tokens.OPENBRACKET : { table = XreadTableSubqueryOrNull(false); if (table == null) { table = XreadJoinedTableAsSubqueryOrNull(); if (table == null) { table = XreadTableSubqueryOrNull(true); if (table == null) { throw unexpectedToken(); } break; } joinedTable = true; } break; } case Tokens.UNNEST : { Expression e = XreadCollectionDerivedTable(OpTypes.TABLE_SUBQUERY); table = e.getTable(); isLateral = true; break; } case Tokens.LATERAL : { Expression e = XreadLateralDerivedTable(); table = e.getTable(); isLateral = true; break; } case Tokens.TABLE : { Expression e = XreadTableFunctionDerivedTable(); table = e.getTable(); isLateral = true; break; } default : { table = readNamedSubqueryOrNull(); if (table == null) { table = readTableName(true); isTableName = true; // attach to range variable below sysPeriodSpec = XreadQuerySystemPeriodSpecOrNull(table); } if (table.isView()) { table = ((View) table).newDerivedTable(session, compileContext); } } } boolean hasAs = false; if (token.tokenType == Tokens.AS) { read(); checkIsNonCoreReservedIdentifier(); hasAs = true; } if (isNonCoreReservedIdentifier()) { boolean limit = token.tokenType == Tokens.LIMIT || token.tokenType == Tokens.OFFSET || token.tokenType == Tokens.FETCH; boolean minus = token.tokenType == Tokens.MINUS_EXCEPT; int position = getPosition(); alias = HsqlNameManager.getSimpleName(token.tokenString, isDelimitedIdentifier()); read(); if (token.tokenType == Tokens.OPENBRACKET) { columnList = new OrderedHashSet(); columnNameList = readColumnNameList(columnList); } else if (!hasAs && limit) { if (token.tokenType == Tokens.COLON || token.tokenType == Tokens.QUESTION || token.tokenType == Tokens.X_VALUE) { alias = null; rewind(position); } } else if (!hasAs && minus) { rewind(position); } } if (isTableName && alias == null && lastSynonym != null) { alias = HsqlNameManager.getSimpleName(lastSynonym.name, lastSynonym.isNameQuoted); } if (database.sqlSyntaxMss) { if (readIfThis(Tokens.WITH)) { readNestedParenthesisedTokens(); } } RangeVariable range; if (joinedTable) { range = new RangeVariableJoined(table, alias, columnList, columnNameList, compileContext); } else { range = new RangeVariable(table, alias, columnList, columnNameList, compileContext); if (sysPeriodSpec != null) { sysPeriodSpec.setSystemRangeVariable(session, range); range.setSystemPeriodCondition(sysPeriodSpec); } } if (isLateral) { range.isLateral = true; } return range; } private Expression readAggregateFunctionOrNull() { int position = getPosition(); int tokenT = token.tokenType; Expression expr; read(); if (token.tokenType != Tokens.OPENBRACKET) { rewind(position); return null; } readThis(Tokens.OPENBRACKET); expr = readAggregateExpression(tokenT); readThis(Tokens.CLOSEBRACKET); readFilterClause(expr); return expr; } private void readFilterClause(Expression e) { ExpressionLogical condition = XreadFilterExpressionOrNull(); if (condition != null) { e.setCondition(condition); } } ExpressionLogical XreadFilterExpressionOrNull() { int position = getPosition(); ExpressionLogical condition = null; if (token.tokenType == Tokens.FILTER) { read(); if (token.tokenType != Tokens.OPENBRACKET) { rewind(position); return null; } readThis(Tokens.OPENBRACKET); readThis(Tokens.WHERE); condition = (ExpressionLogical) XreadBooleanValueExpression(); readThis(Tokens.CLOSEBRACKET); } return condition; } private Expression readAggregateExpression(int tokenT) { int type = ParserDQL.getExpressionType(tokenT); boolean distinct = false; boolean all = false; SortAndSlice sort = null; String separator = null; if (token.tokenType == Tokens.DISTINCT) { distinct = true; read(); } else if (token.tokenType == Tokens.ALL) { all = true; read(); } int position = getPosition(); Expression e = XreadValueExpression(); switch (type) { case OpTypes.COUNT : if (e.getType() == OpTypes.MULTICOLUMN) { if (((ExpressionColumn) e).tableName != null) { throw unexpectedToken(); } if (all || distinct) { throw unexpectedToken(); } e.opType = OpTypes.ASTERISK; } else { if (token.tokenType == Tokens.COMMA) { rewind(position); e = XreadRowElementList(false); } } break; case OpTypes.STDDEV_POP : case OpTypes.STDDEV_SAMP : case OpTypes.VAR_POP : case OpTypes.VAR_SAMP : if (all || distinct) { throw unexpectedToken(all ? Tokens.T_ALL : Tokens.T_DISTINCT); } break; case OpTypes.ARRAY_AGG : case OpTypes.GROUP_CONCAT : { if (token.tokenType == Tokens.ORDER) { read(); readThis(Tokens.BY); sort = XreadOrderBy(); } if (type == OpTypes.GROUP_CONCAT) { if (token.tokenType == Tokens.SEPARATOR) { read(); checkIsQuotedString(); separator = (String) token.tokenValue; read(); } } return new ExpressionArrayAggregate(type, distinct, e, sort, separator); } case OpTypes.MEDIAN : { return new ExpressionArrayAggregate(type, distinct, e, sort, separator); } default : if (e.getType() == OpTypes.MULTICOLUMN || e.getType() == OpTypes.ASTERISK) { throw unexpectedToken(Tokens.T_ASTERISK); } } Expression aggregateExp = new ExpressionAggregate(type, distinct, e); return aggregateExp; } //-------------------------------------- // returns null // := | Expression XreadValueSpecificationOrNull() { Expression e = null; boolean minus = false; switch (token.tokenType) { case Tokens.PLUS_OP : read(); break; case Tokens.MINUS_OP : read(); minus = true; break; default : } e = XreadUnsignedValueSpecificationOrNull(); if (e == null) { return null; } if (minus) { e = new ExpressionArithmetic(OpTypes.NEGATE, e); } return e; } // returns null // | Expression XreadUnsignedValueSpecificationOrNull() { Expression e; switch (token.tokenType) { case Tokens.TRUE : read(); return Expression.EXPR_TRUE; case Tokens.FALSE : read(); return Expression.EXPR_FALSE; case Tokens.DEFAULT : if (compileContext.contextuallyTypedExpression) { read(); e = new ExpressionColumn(OpTypes.DEFAULT); return e; } break; case Tokens.NULL : e = new ExpressionValue(null, null); read(); return e; case Tokens.X_VALUE : e = new ExpressionValue(token.tokenValue, token.dataType); read(); return e; case Tokens.X_DELIMITED_IDENTIFIER : case Tokens.X_IDENTIFIER : if (!token.isHostParameter) { return null; } return null; case Tokens.COLON : read(); if (token.tokenType == Tokens.X_DELIMITED_IDENTIFIER || token.tokenType == Tokens.X_IDENTIFIER) {} else { throw unexpectedToken(Tokens.T_COLON); } // fall through case Tokens.QUESTION : ExpressionColumn p = new ExpressionColumn(OpTypes.DYNAMIC_PARAM); compileContext.addParameter(p, getPosition()); read(); return p; case Tokens.COLLATION : return XreadCurrentCollationSpec(); case Tokens.VALUE : case Tokens.CURRENT_CATALOG : case Tokens.CURRENT_DEFAULT_TRANSFORM_GROUP : case Tokens.CURRENT_PATH : case Tokens.CURRENT_ROLE : case Tokens.CURRENT_SCHEMA : case Tokens.CURRENT_TRANSFORM_GROUP_FOR_TYPE : case Tokens.CURRENT_USER : case Tokens.SESSION_USER : case Tokens.SYSTEM_USER : case Tokens.USER : FunctionSQL function = FunctionSQL.newSQLFunction(token.tokenString, compileContext); if (function == null) { return null; } return readSQLFunction(function); // read SQL parameter reference } return null; } // | | Expression XreadSimpleValueSpecificationOrNull() { Expression e; switch (token.tokenType) { case Tokens.X_VALUE : e = new ExpressionValue(token.tokenValue, token.dataType); read(); return e; case Tokens.COLON : read(); if (token.tokenType == Tokens.X_DELIMITED_IDENTIFIER || token.tokenType == Tokens.X_IDENTIFIER) {} else { throw unexpectedToken(Tokens.T_COLON); } // fall through case Tokens.QUESTION : ExpressionColumn p = new ExpressionColumn(OpTypes.DYNAMIC_PARAM); compileContext.addParameter(p, getPosition()); read(); return p; case Tokens.X_IDENTIFIER : case Tokens.X_DELIMITED_IDENTIFIER : checkValidCatalogName(token.namePrePrePrefix); e = new ExpressionColumn(token.namePrePrefix, token.namePrefix, token.tokenString); read(); return e; default : return null; } } // combined and // exclusively called // needed for predicate Expression XreadAllTypesValueExpressionPrimary(boolean isBoolean) { Expression e = null; int position = getPosition(); switch (token.tokenType) { case Tokens.EXISTS : case Tokens.UNIQUE : if (isBoolean) { return XreadPredicate(); } break; case Tokens.PERIOD : if (isBoolean) { break; } read(); if (readIfThis(Tokens.OPENBRACKET)) { e = XreadRowElementList(true); if (e.nodes.length != 2) { throw Error.error(ErrorCode.X_42564); } e = new ExpressionPeriod(e); readThis(Tokens.CLOSEBRACKET); } else { rewind(position); e = XreadSimpleValueExpressionPrimary(); if (e != null) { e = XreadArrayElementReference(e); } } break; case Tokens.ROW : if (isBoolean) { break; } read(); readThis(Tokens.OPENBRACKET); e = XreadRowElementList(true); readThis(Tokens.CLOSEBRACKET); break; default : e = XreadSimpleValueExpressionPrimary(); if (e != null) { e = XreadArrayElementReference(e); } } if (e == null) { if (token.tokenType == Tokens.ROW) { read(); checkIsThis(Tokens.OPENBRACKET); } else if (token.tokenType == Tokens.PERIOD) { read(); readThis(Tokens.OPENBRACKET); e = XreadRowElementList(true); if (e.nodes.length != 2) { throw Error.error(ErrorCode.X_42564); } e = new ExpressionPeriod(e); readThis(Tokens.CLOSEBRACKET); } if (token.tokenType == Tokens.OPENBRACKET) { read(); e = XreadRowElementList(true); readThis(Tokens.CLOSEBRACKET); } } if (isBoolean && e != null) { e = XreadPredicateRightPart(e); } return e; } // doesn't return null // ::= // | Expression XreadValueExpressionPrimary() { Expression e; e = XreadSimpleValueExpressionPrimary(); if (e != null) { e = XreadArrayElementReference(e); return e; } if (token.tokenType == Tokens.OPENBRACKET) { read(); e = XreadValueExpression(); readThis(Tokens.CLOSEBRACKET); } else { return null; } return e; } // returns null // :== this // :== this | Expression XreadSimpleValueExpressionPrimary() { Expression e; e = XreadUnsignedValueSpecificationOrNull(); if (e != null) { return e; } int position = getPosition(); switch (token.tokenType) { case Tokens.OPENBRACKET : read(); int subqueryPosition = getPosition(); readOpenBrackets(); switch (token.tokenType) { case Tokens.TABLE : case Tokens.VALUES : case Tokens.SELECT : TableDerived td = null; rewind(subqueryPosition); try { td = XreadSubqueryTableBody( OpTypes.SCALAR_SUBQUERY); readThis(Tokens.CLOSEBRACKET); } catch (HsqlException ex) { ex.setLevel(compileContext.subqueryDepth); if (lastError == null || lastError.getLevel() < ex.getLevel()) { lastError = ex; } rewind(position); return null; } if (td.queryExpression != null) { if (td.queryExpression.isSingleColumn()) { e = new Expression(OpTypes.SCALAR_SUBQUERY, td); } else { e = new Expression(OpTypes.ROW_SUBQUERY, td); } } if (e != null) { return e; } // fall through default : rewind(position); return null; } case Tokens.ASTERISK : e = new ExpressionColumn(token.namePrePrefix, token.namePrefix); getRecordedToken().setExpression(e); read(); return e; case Tokens.LEAST : { e = readLeastExpressionOrNull(); if (e != null) { return e; } break; } case Tokens.GREATEST : { e = readGreatestExpressionOrNull(); if (e != null) { return e; } break; } case Tokens.DECODE : { e = readDecodeExpressionOrNull(); if (e != null) { return e; } break; } case Tokens.CONCAT_WORD : { e = readConcatExpressionOrNull(); if (e != null) { return e; } break; } case Tokens.CONCAT_WS : { e = readConcatSeparatorExpressionOrNull(); if (e != null) { return e; } break; } case Tokens.IF : case Tokens.CASEWHEN : { e = readCaseWhenExpressionOrNull(); if (e != null) { return e; } break; } case Tokens.CASE : return readCaseExpression(); case Tokens.NULLIF : return readNullIfExpression(); case Tokens.COALESCE : return readCoalesceExpression(); case Tokens.IFNULL : case Tokens.ISNULL : { e = readIfNullExpressionOrNull(); if (e != null) { return e; } break; } case Tokens.NVL2 : { e = readIfNull2ExpressionOrNull(); if (e != null) { return e; } break; } case Tokens.CAST : e = readCastExpression(); if (e != null) { return e; } break; case Tokens.CONVERT : { e = readConvertExpressionOrNull(); if (e != null) { return e; } break; } case Tokens.DATE : case Tokens.TIME : case Tokens.TIMESTAMP : case Tokens.INTERVAL : e = readDateTimeIntervalLiteral(session); if (e != null) { return e; } break; case Tokens.ARRAY : return readCollection(OpTypes.ARRAY); case Tokens.COUNT : case Tokens.AVG : case Tokens.MAX : case Tokens.MIN : case Tokens.SUM : case Tokens.ANY : case Tokens.SOME : case Tokens.EVERY : case Tokens.STDDEV_POP : case Tokens.STDDEV_SAMP : case Tokens.VAR_POP : case Tokens.VAR_SAMP : case Tokens.GROUP_CONCAT : case Tokens.ARRAY_AGG : case Tokens.MEDIAN : e = readAggregateFunctionOrNull(); // general set function if (e != null) { return e; } break; case Tokens.NEXT : { e = readSequenceExpressionOrNull(OpTypes.SEQUENCE); if (e != null) { return e; } break; } case Tokens.PREVVAL : case Tokens.CURRENT : { e = readSequenceExpressionOrNull(OpTypes.SEQUENCE_CURRENT); if (e != null) { return e; } break; } case Tokens.CURRVAL : { if (database.sqlSyntaxPgs) { read(); readThis(Tokens.OPENBRACKET); String spec = readQuotedString(); Scanner scanner = session.getScanner(); scanner.reset(session, spec); scanner.scanNext(); String schemaName = session.getSchemaName(scanner.token.namePrefix); NumberSequence sequence = database.schemaManager.getSequence( scanner.token.tokenString, schemaName, true); e = new ExpressionColumn(sequence, OpTypes.SEQUENCE_CURRENT); readThis(Tokens.CLOSEBRACKET); return e; } break; } case Tokens.LASTVAL : { if (database.sqlSyntaxPgs) { read(); readThis(Tokens.OPENBRACKET); readThis(Tokens.CLOSEBRACKET); return FunctionCustom.newCustomFunction(session, Tokens.T_IDENTITY, Tokens.IDENTITY); } break; } case Tokens.NEXTVAL : { if (database.sqlSyntaxPgs) { return readNextvalFunction(); } else if (database.sqlSyntaxDb2) { e = readSequenceExpressionOrNull(OpTypes.SEQUENCE); if (e != null) { return e; } } break; } case Tokens.ROW_NUMBER : { read(); if (token.tokenType == Tokens.OPENBRACKET) { read(); readThis(Tokens.CLOSEBRACKET); readThis(Tokens.OVER); readThis(Tokens.OPENBRACKET); readThis(Tokens.CLOSEBRACKET); } else { rewind(position); break; } return new ExpressionColumn(OpTypes.ROWNUM); } case Tokens.ROWNUM : { read(); if (token.tokenType == Tokens.OPENBRACKET) { read(); if (token.tokenType == Tokens.CLOSEBRACKET) { read(); } else { rewind(position); break; } } else if (!database.sqlSyntaxOra && !database.sqlSyntaxDb2) { rewind(position); break; } return new ExpressionColumn(OpTypes.ROWNUM); } case Tokens.LEFT : case Tokens.RIGHT : // CLI function names break; case Tokens.TABLE : { read(); readThis(Tokens.OPENBRACKET); TableDerived td = XreadSubqueryTableBody(OpTypes.TABLE_SUBQUERY); readThis(Tokens.CLOSEBRACKET); return new Expression(OpTypes.TABLE_SUBQUERY, td); } default : if (isCoreReservedKey()) { throw unexpectedToken(); } } e = readColumnOrFunctionExpression(); if (e.isSelfAggregate()) { readFilterClause(e); } return e; } Expression readNextvalFunction() { read(); readThis(Tokens.OPENBRACKET); String spec = readQuotedString(); Scanner scanner = session.getScanner(); scanner.reset(session, spec); scanner.scanNext(); String schemaName = session.getSchemaName(scanner.token.namePrefix); NumberSequence sequence = database.schemaManager.getSequence(scanner.token.tokenString, schemaName, true); Expression e = new ExpressionColumn(sequence, OpTypes.SEQUENCE); readThis(Tokens.CLOSEBRACKET); return e; } // OK - composite production - // Expression XreadAllTypesPrimary(boolean boole) { Expression e = null; switch (token.tokenType) { case Tokens.SUBSTRING : case Tokens.SUBSTRING_REGEX : case Tokens.LOWER : case Tokens.UPPER : case Tokens.TRANSLATE_REGEX : case Tokens.TRIM : case Tokens.OVERLAY : case Tokens.NORMALIZE : // case Tokens.POSITION : case Tokens.OCCURRENCES_REGEX : case Tokens.POSITION_REGEX : case Tokens.EXTRACT : case Tokens.CHAR_LENGTH : case Tokens.CHARACTER_LENGTH : case Tokens.OCTET_LENGTH : case Tokens.CARDINALITY : case Tokens.ARRAY_MAX_CARDINALITY : case Tokens.MAX_CARDINALITY : case Tokens.ABS : case Tokens.MOD : case Tokens.LN : case Tokens.EXP : case Tokens.POWER : case Tokens.SQRT : case Tokens.FLOOR : case Tokens.CEILING : case Tokens.CEIL : case Tokens.WIDTH_BUCKET : FunctionSQL function = FunctionSQL.newSQLFunction(token.tokenString, compileContext); if (function == null) { throw unsupportedFeature(); } e = readSQLFunction(function); if (e != null) { break; } // fall through default : e = XreadAllTypesValueExpressionPrimary(boole); } e = XreadModifier(e); return e; } Expression XreadModifier(Expression e) { int position = getPosition(); switch (token.tokenType) { case Tokens.AT : { read(); Expression e1 = null; if (token.tokenType == Tokens.LOCAL) { read(); } else if (token.tokenType == Tokens.TIME) { readThis(Tokens.TIME); readThis(Tokens.ZONE); e1 = XreadValueExpressionPrimary(); switch (token.tokenType) { case Tokens.YEAR : case Tokens.MONTH : case Tokens.DAY : case Tokens.HOUR : case Tokens.MINUTE : case Tokens.SECOND : { IntervalType type = readIntervalType(session, false); if (e1.getType() == OpTypes.SUBTRACT) { e1.dataType = type; } else { e1 = new ExpressionOp(e1, type); } break; } default : } } else { rewind(position); return e; } e = new ExpressionOp(OpTypes.ZONE_MODIFIER, e, e1); break; } case Tokens.YEAR : case Tokens.MONTH : case Tokens.DAY : case Tokens.HOUR : case Tokens.MINUTE : case Tokens.SECOND : { IntervalType type = readIntervalType(session, true); if (e.getType() == OpTypes.SUBTRACT) { e.dataType = type; } else { e = new ExpressionOp(e, type); } break; } case Tokens.COLLATE : { read(); Collation collation = database.schemaManager.getCollation(session, token.tokenString, token.namePrefix); e.setCollation(collation); read(); } } return e; } Expression XreadValueExpressionWithContext() { Expression e; compileContext.contextuallyTypedExpression = true; e = XreadValueExpression(); compileContext.contextuallyTypedExpression = false; return e; } /** * ::= * * | * | * */ Expression XreadValueExpression() { Expression e = XreadAllTypesCommonValueExpression(true); if (token.tokenType == Tokens.LEFTBRACKET) { read(); Expression e1 = XreadNumericValueExpression(); readThis(Tokens.RIGHTBRACKET); e = new ExpressionAccessor(e, e1); } return e; } // union of Expression XreadRowOrCommonValueExpression() { return XreadAllTypesCommonValueExpression(false); } // union of // no and no Expression XreadAllTypesCommonValueExpression(boolean boole) { Expression e = XreadAllTypesTerm(boole); int type = 0; boolean end = false; while (true) { switch (token.tokenType) { case Tokens.PLUS_OP : type = OpTypes.ADD; boole = false; break; case Tokens.MINUS_OP : type = OpTypes.SUBTRACT; boole = false; break; case Tokens.CONCAT_OP : type = OpTypes.CONCAT; boole = false; break; case Tokens.OR : if (boole) { type = OpTypes.OR; break; } // fall through default : end = true; break; } if (end) { break; } read(); Expression a = e; e = XreadAllTypesTerm(boole); e = boole ? new ExpressionLogical(type, a, e) : new ExpressionArithmetic(type, a, e); } return e; } Expression XreadAllTypesTerm(boolean boole) { Expression e = XreadAllTypesFactor(boole); int type = 0; boolean end = false; while (true) { switch (token.tokenType) { case Tokens.ASTERISK : type = OpTypes.MULTIPLY; boole = false; break; case Tokens.DIVIDE_OP : type = OpTypes.DIVIDE; boole = false; break; case Tokens.AND : if (boole) { type = OpTypes.AND; break; } // fall through default : end = true; break; } if (end) { break; } read(); Expression a = e; e = XreadAllTypesFactor(boole); if (e == null) { throw unexpectedToken(); } e = boole ? new ExpressionLogical(type, a, e) : new ExpressionArithmetic(type, a, e); } return e; } Expression XreadAllTypesFactor(boolean boole) { Expression e; boolean minus = false; boolean not = false; boolean unknown = false; switch (token.tokenType) { case Tokens.PLUS_OP : read(); boole = false; break; case Tokens.MINUS_OP : read(); boole = false; minus = true; break; case Tokens.NOT : if (boole) { read(); not = true; } break; default : } e = XreadAllTypesPrimary(boole); if (boole && token.tokenType == Tokens.IS) { read(); if (token.tokenType == Tokens.NOT) { read(); not = !not; } if (token.tokenType == Tokens.TRUE) { read(); } else if (token.tokenType == Tokens.FALSE) { read(); not = !not; } else if (token.tokenType == Tokens.UNKNOWN) { read(); unknown = true; } else { throw unexpectedToken(); } } if (unknown) { e = new ExpressionLogical(OpTypes.IS_NULL, e); } else if (minus) { e = new ExpressionArithmetic(OpTypes.NEGATE, e); } else if (not) { e = new ExpressionLogical(OpTypes.NOT, e); } return e; } Expression XreadStringValueExpression() { return XreadCharacterValueExpression(); // XreadBinaryValueExpression(); } Expression XreadCharacterValueExpression() { Expression e = XreadCharacterPrimary(); Collation collation = readCollateClauseOrNull(); while (token.tokenType == Tokens.CONCAT_OP) { read(); Expression a = e; e = XreadCharacterPrimary(); collation = readCollateClauseOrNull(); e = new ExpressionArithmetic(OpTypes.CONCAT, a, e); } return e; } Expression XreadCharacterPrimary() { switch (token.tokenType) { case Tokens.SUBSTRING : // case Token.SUBSTRING_REGEX : case Tokens.LOWER : case Tokens.UPPER : // case Token.TRANSLATE_REGEX : case Tokens.TRIM : case Tokens.OVERLAY : // case Token.NORMALIZE : FunctionSQL function = FunctionSQL.newSQLFunction(token.tokenString, compileContext); Expression e = readSQLFunction(function); if (e != null) { return e; } } return XreadValueExpressionPrimary(); } Expression XreadNumericPrimary() { switch (token.tokenType) { case Tokens.POSITION : // case Token.OCCURRENCES_REGEX : // case Token.POSITION_REGEX : case Tokens.EXTRACT : case Tokens.CHAR_LENGTH : case Tokens.CHARACTER_LENGTH : case Tokens.OCTET_LENGTH : case Tokens.CARDINALITY : case Tokens.ABS : case Tokens.MOD : case Tokens.LN : case Tokens.EXP : case Tokens.POWER : case Tokens.SQRT : case Tokens.FLOOR : case Tokens.CEILING : case Tokens.CEIL : case Tokens.WIDTH_BUCKET : FunctionSQL function = FunctionSQL.newSQLFunction(token.tokenString, compileContext); if (function == null) { throw unexpectedToken(); } Expression e = readSQLFunction(function); if (e != null) { return e; } } return XreadValueExpressionPrimary(); } Expression XreadNumericValueExpression() { Expression e = XreadTerm(); while (true) { int type; if (token.tokenType == Tokens.PLUS_OP) { type = OpTypes.ADD; } else if (token.tokenType == Tokens.MINUS_OP) { type = OpTypes.SUBTRACT; } else { break; } read(); Expression a = e; e = XreadTerm(); e = new ExpressionArithmetic(type, a, e); } return e; } Expression XreadTerm() { Expression e = XreadFactor(); int type; while (true) { if (token.tokenType == Tokens.ASTERISK) { type = OpTypes.MULTIPLY; } else if (token.tokenType == Tokens.DIVIDE_OP) { type = OpTypes.DIVIDE; } else { break; } read(); Expression a = e; e = XreadFactor(); if (e == null) { throw unexpectedToken(); } e = new ExpressionArithmetic(type, a, e); } return e; } Expression XreadFactor() { Expression e; boolean minus = false; if (token.tokenType == Tokens.PLUS_OP) { read(); } else if (token.tokenType == Tokens.MINUS_OP) { read(); minus = true; } e = XreadNumericPrimary(); if (e == null) { return null; } if (minus) { e = new ExpressionArithmetic(OpTypes.NEGATE, e); } return e; } Expression XreadDatetimeValueExpression() { Expression e = XreadDateTimeIntervalTerm(); while (true) { int type; if (token.tokenType == Tokens.PLUS_OP) { type = OpTypes.ADD; } else if (token.tokenType == Tokens.MINUS_OP) { type = OpTypes.SUBTRACT; } else { break; } read(); Expression a = e; e = XreadDateTimeIntervalTerm(); e = new ExpressionArithmetic(type, a, e); } return e; } Expression XreadIntervalValueExpression() { Expression e = XreadDateTimeIntervalTerm(); while (true) { int type; if (token.tokenType == Tokens.PLUS_OP) { type = OpTypes.ADD; } else if (token.tokenType == Tokens.MINUS_OP) { type = OpTypes.SUBTRACT; } else { break; } read(); Expression a = e; e = XreadDateTimeIntervalTerm(); e = new ExpressionArithmetic(type, a, e); } return e; } Expression XreadDateTimeIntervalTerm() { switch (token.tokenType) { case Tokens.CURRENT_DATE : case Tokens.CURRENT_TIME : case Tokens.CURRENT_TIMESTAMP : case Tokens.LOCALTIME : case Tokens.LOCALTIMESTAMP : // case Tokens.ABS : FunctionSQL function = FunctionSQL.newSQLFunction(token.tokenString, compileContext); if (function == null) { throw unexpectedToken(); } return readSQLFunction(function); default : } return XreadValueExpressionPrimary(); } // returns null Expression XreadDateTimeValueFunctionOrNull() { FunctionSQL function = null; switch (token.tokenType) { case Tokens.CURRENT_DATE : case Tokens.CURRENT_TIME : case Tokens.CURRENT_TIMESTAMP : case Tokens.LOCALTIME : case Tokens.LOCALTIMESTAMP : function = FunctionSQL.newSQLFunction(token.tokenString, compileContext); break; case Tokens.SYSTIMESTAMP : case Tokens.NOW : case Tokens.TODAY : case Tokens.SYSDATE : function = FunctionCustom.newCustomFunction(session, token.tokenString, token.tokenType); if (function == null) { return null; } break; default : return null; } if (function == null) { throw unexpectedToken(); } return readSQLFunction(function); } Expression XreadBooleanValueExpression() { try { Expression e = XreadBooleanTermOrNull(); if (e == null) { throw Error.error(ErrorCode.X_42568); } while (true) { int type; if (token.tokenType == Tokens.OR) { type = OpTypes.OR; } else { break; } read(); Expression a = e; e = XreadBooleanTermOrNull(); if (e == null) { throw Error.error(ErrorCode.X_42568); } e = new ExpressionLogical(type, a, e); } if (e == null) { throw Error.error(ErrorCode.X_42568); } return e; } catch (HsqlException ex) { ex.setLevel(compileContext.subqueryDepth); if (lastError != null && lastError.getLevel() >= ex.getLevel()) { ex = lastError; lastError = null; } throw ex; } } Expression XreadBooleanTermOrNull() { Expression e = XreadBooleanFactorOrNull(); if (e == null) { return null; } int type; while (true) { if (token.tokenType == Tokens.AND) { type = OpTypes.AND; } else { break; } read(); Expression a = e; e = XreadBooleanFactorOrNull(); if (e == null) { throw unexpectedToken(); } e = new ExpressionLogical(type, a, e); } return e; } Expression XreadBooleanFactorOrNull() { Expression e; boolean not = false; if (token.tokenType == Tokens.NOT) { read(); not = true; } e = XreadBooleanTestOrNull(); if (e == null) { return null; } if (not) { e = new ExpressionLogical(OpTypes.NOT, e); } return e; } Expression XreadBooleanTestOrNull() { boolean unknown = false; boolean isNot = false; Expression e = XreadBooleanPrimaryOrNull(); if (e == null) { return e; } if (token.tokenType == Tokens.IS) { read(); if (token.tokenType == Tokens.NOT) { read(); isNot = true; } if (token.tokenType == Tokens.TRUE) { read(); } else if (token.tokenType == Tokens.FALSE) { read(); isNot = !isNot; } else if (token.tokenType == Tokens.UNKNOWN) { read(); unknown = true; } else { throw unexpectedToken(); } } if (unknown) { e = new ExpressionLogical(OpTypes.IS_NULL, e); } if (isNot) { e = new ExpressionLogical(OpTypes.NOT, e); } return e; } // ::= | Expression XreadBooleanPrimaryOrNull() { Expression e = null; int position; switch (token.tokenType) { case Tokens.EXISTS : case Tokens.UNIQUE : return XreadPredicate(); case Tokens.ROW : read(); readThis(Tokens.OPENBRACKET); e = XreadRowElementList(true); readThis(Tokens.CLOSEBRACKET); break; default : position = getPosition(); try { e = XreadAllTypesCommonValueExpression(false); } catch (HsqlException ex) { ex.setLevel(compileContext.subqueryDepth); if (lastError == null || lastError.getLevel() < ex.getLevel()) { lastError = ex; } rewind(position); } } if (e == null && token.tokenType == Tokens.OPENBRACKET) { read(); position = getPosition(); try { e = XreadRowElementList(true); readThis(Tokens.CLOSEBRACKET); } catch (HsqlException ex) { ex.setLevel(compileContext.subqueryDepth); if (lastError == null || lastError.getLevel() < ex.getLevel()) { lastError = ex; } rewind(position); e = XreadBooleanValueExpression(); readThis(Tokens.CLOSEBRACKET); } } if (e != null) { e = XreadPredicateRightPart(e); } return e; } // similar to Expression XreadBooleanPredicand() { Expression e; if (token.tokenType == Tokens.OPENBRACKET) { read(); e = XreadBooleanValueExpression(); readThis(Tokens.CLOSEBRACKET); return e; } else { e = XreadSimpleValueExpressionPrimary(); if (e != null) { e = XreadArrayElementReference(e); } return e; } } Expression XreadPredicate() { switch (token.tokenType) { case Tokens.EXISTS : { read(); Expression s = XreadTableSubquery(OpTypes.EXISTS); return new ExpressionLogical(OpTypes.EXISTS, s); } case Tokens.UNIQUE : { read(); Expression s = XreadTableSubquery(OpTypes.UNIQUE); return new ExpressionLogical(OpTypes.UNIQUE, s); } default : { Expression a = XreadRowValuePredicand(); return XreadPredicateRightPart(a); } } } Expression XreadPredicateRightPart(final Expression l) { boolean hasNot = false; boolean immediately = false; ExpressionLogical e = null; Expression r; int position = getPosition(); if (token.tokenType == Tokens.NOT) { read(); hasNot = true; } // valid for PRECEDES and SUCCEEDS predicates if (token.tokenType == Tokens.IMMEDIATELY) { read(); if (token.tokenType != Tokens.PRECEDES && token.tokenType != Tokens.SUCCEEDS) { throw unexpectedToken(); } immediately = true; } switch (token.tokenType) { case Tokens.IS : { if (hasNot) { throw unexpectedToken(); } read(); if (token.tokenType == Tokens.NOT) { hasNot = true; read(); } if (token.tokenType == Tokens.NULL) { read(); if (hasNot) { e = new ExpressionLogical(OpTypes.IS_NOT_NULL, l); hasNot = false; } else { e = new ExpressionLogical(OpTypes.IS_NULL, l); } break; } if (token.tokenType == Tokens.DISTINCT) { read(); readThis(Tokens.FROM); r = XreadRowValuePredicand(); e = new ExpressionLogical(OpTypes.NOT_DISTINCT, l, r); hasNot = !hasNot; break; } rewind(position); return l; } case Tokens.LIKE : { e = XreadLikePredicateRightPart(l); e.noOptimisation = isCheckOrTriggerCondition; break; } case Tokens.BETWEEN : { e = XreadBetweenPredicateRightPart(l); break; } case Tokens.IN : { e = XreadInPredicateRightPart(l); e.noOptimisation = isCheckOrTriggerCondition; break; } case Tokens.CONTAINS : { if (hasNot) { throw unexpectedToken(); } e = XreadPeriodPredicateRightPart(OpTypes.RANGE_CONTAINS, l); break; } case Tokens.EQUALS : { if (hasNot) { throw unexpectedToken(); } e = XreadPeriodPredicateRightPart(OpTypes.RANGE_EQUALS, l); break; } case Tokens.OVERLAPS : { if (hasNot) { throw unexpectedToken(); } e = XreadPeriodPredicateRightPart(OpTypes.RANGE_OVERLAPS, l); break; } case Tokens.PRECEDES : { if (hasNot) { throw unexpectedToken(); } if (immediately) { e = XreadPeriodPredicateRightPart( OpTypes.RANGE_IMMEDIATELY_PRECEDES, l); } else { e = XreadPeriodPredicateRightPart(OpTypes.RANGE_PRECEDES, l); } break; } case Tokens.SUCCEEDS : { if (hasNot) { throw unexpectedToken(); } if (immediately) { e = XreadPeriodPredicateRightPart( OpTypes.RANGE_IMMEDIATELY_SUCCEEDS, l); } else { e = XreadPeriodPredicateRightPart(OpTypes.RANGE_SUCCEEDS, l); } break; } case Tokens.EQUALS_OP : case Tokens.GREATER_EQUALS : case Tokens.GREATER_OP : case Tokens.LESS_OP : case Tokens.LESS_EQUALS : case Tokens.NOT_EQUALS : { if (hasNot) { throw unexpectedToken(); } int type = getExpressionType(token.tokenType); read(); switch (token.tokenType) { case Tokens.ANY : case Tokens.SOME : case Tokens.ALL : e = XreadQuantifiedComparisonRightPart(type, l); break; default : { Expression row = XreadRowValuePredicand(); e = new ExpressionLogical(type, l, row); break; } } break; } case Tokens.MATCH : { e = XreadMatchPredicateRightPart(l); break; } default : { if (hasNot) { throw unexpectedToken(); } return l; } } if (hasNot) { e = new ExpressionLogical(OpTypes.NOT, e); } return e; } private ExpressionLogical XreadBetweenPredicateRightPart( final Expression a) { boolean symmetric = false; read(); if (token.tokenType == Tokens.ASYMMETRIC) { read(); } else if (token.tokenType == Tokens.SYMMETRIC) { symmetric = true; read(); } Expression left = XreadRowValuePredicand(); readThis(Tokens.AND); Expression right = XreadRowValuePredicand(); Expression l = new ExpressionLogical(OpTypes.GREATER_EQUAL, a, left); Expression r = new ExpressionLogical(OpTypes.SMALLER_EQUAL, a, right); ExpressionLogical leftToRight = new ExpressionLogical(OpTypes.AND, l, r); if (symmetric) { l = new ExpressionLogical(OpTypes.SMALLER_EQUAL, a, left); r = new ExpressionLogical(OpTypes.GREATER_EQUAL, a, right); Expression rightToLeft = new ExpressionLogical(OpTypes.AND, l, r); return new ExpressionLogical(OpTypes.OR, leftToRight, rightToLeft); } else { return leftToRight; } } private ExpressionLogical XreadQuantifiedComparisonRightPart(int exprType, Expression l) { int tokenT = token.tokenType; int exprSubType = 0; Expression e; switch (token.tokenType) { case Tokens.ANY : case Tokens.SOME : exprSubType = OpTypes.ANY_QUANTIFIED; break; case Tokens.ALL : exprSubType = OpTypes.ALL_QUANTIFIED; break; default : throw Error.runtimeError(ErrorCode.U_S0500, "ParserDQL"); } read(); readThis(Tokens.OPENBRACKET); int position = getPosition(); readOpenBrackets(); switch (token.tokenType) { case Tokens.WITH : case Tokens.TABLE : case Tokens.VALUES : case Tokens.SELECT : rewind(position); TableDerived td = XreadSubqueryTableBody(OpTypes.IN); e = new Expression(OpTypes.TABLE_SUBQUERY, td); readThis(Tokens.CLOSEBRACKET); break; default : rewind(position); e = readAggregateExpression(tokenT); readThis(Tokens.CLOSEBRACKET); readFilterClause(e); } ExpressionLogical r = new ExpressionLogical(exprType, l, e); r.setSubType(exprSubType); return r; } private ExpressionLogical XreadInPredicateRightPart(Expression l) { int degree = l.getDegree(); Expression e = null; read(); readThis(Tokens.OPENBRACKET); int position = getPosition(); int brackets = readOpenBrackets(); switch (token.tokenType) { case Tokens.UNNEST : e = XreadCollectionDerivedTable(OpTypes.IN); readThis(Tokens.CLOSEBRACKET); readCloseBrackets(brackets); break; case Tokens.WITH : case Tokens.TABLE : case Tokens.VALUES : case Tokens.SELECT : { rewind(position); TableDerived td = XreadSubqueryTableBody(OpTypes.IN); e = new Expression(OpTypes.TABLE_SUBQUERY, td); readThis(Tokens.CLOSEBRACKET); break; } default : { rewind(position); e = XreadInValueListConstructor(degree); readThis(Tokens.CLOSEBRACKET); break; } } ExpressionLogical r; if (isCheckOrTriggerCondition) { r = new ExpressionLogical(OpTypes.IN, l, e); } else { r = new ExpressionLogical(OpTypes.EQUAL, l, e); r.setSubType(OpTypes.ANY_QUANTIFIED); } return r; } Expression XreadInValueList(int degree) { HsqlArrayList list = new HsqlArrayList(); while (true) { Expression e = XreadValueExpression(); if (e.getType() != OpTypes.ROW) { e = new Expression(OpTypes.ROW, new Expression[]{ e }); } list.add(e); if (token.tokenType == Tokens.COMMA) { read(); continue; } break; } Expression[] array = new Expression[list.size()]; list.toArray(array); Expression e = new Expression(OpTypes.VALUELIST, array); for (int i = 0; i < array.length; i++) { if (array[i].getType() != OpTypes.ROW) { array[i] = new Expression(OpTypes.ROW, new Expression[]{ array[i] }); } Expression[] args = array[i].nodes; if (args.length != degree) { // SQL error message throw unexpectedToken(); } for (int j = 0; j < degree; j++) { if (args[j].getType() == OpTypes.ROW) { // SQL error message throw unexpectedToken(); } } } return e; } private ExpressionLogical XreadLikePredicateRightPart(Expression a) { read(); Expression b = XreadStringValueExpression(); Expression escape = null; if (token.tokenString.equals(Tokens.T_ESCAPE)) { read(); escape = XreadStringValueExpression(); } return new ExpressionLike(a, b, escape, isCheckOrTriggerCondition); } private ExpressionLogical XreadMatchPredicateRightPart(Expression a) { boolean isUnique = false; int matchType = OpTypes.MATCH_SIMPLE; read(); if (token.tokenType == Tokens.UNIQUE) { read(); isUnique = true; } switch (token.tokenType) { default : matchType = isUnique ? OpTypes.MATCH_UNIQUE_SIMPLE : OpTypes.MATCH_SIMPLE; break; case Tokens.SIMPLE : read(); matchType = isUnique ? OpTypes.MATCH_UNIQUE_SIMPLE : OpTypes.MATCH_SIMPLE; break; case Tokens.PARTIAL : read(); matchType = isUnique ? OpTypes.MATCH_UNIQUE_PARTIAL : OpTypes.MATCH_PARTIAL; break; case Tokens.FULL : read(); matchType = isUnique ? OpTypes.MATCH_UNIQUE_FULL : OpTypes.MATCH_FULL; break; } int mode = isUnique ? OpTypes.MATCH_SIMPLE : OpTypes.IN; Expression s = XreadTableSubquery(mode); return new ExpressionLogical(matchType, a, s); } /** * OVERLAPS does not require PERIOD, others do. * CONTAINS can have single value right side */ private ExpressionLogical XreadPeriodPredicateRightPart(int opType, Expression left) { boolean isLeftRow = false; switch (left.getType()) { case OpTypes.COLUMN : left = new ExpressionPeriod((ExpressionColumn) left); break; case OpTypes.PERIOD : break; case OpTypes.ROW : if (left.nodes.length != 2) { throw Error.error(ErrorCode.X_42564); } isLeftRow = true; break; default : throw Error.error(ErrorCode.X_42564); } read(); boolean period = false; if (token.tokenType == Tokens.PERIOD) { if (isLeftRow) { throw unexpectedToken(); } read(); period = true; if (token.tokenType != Tokens.OPENBRACKET) { throw unexpectedTokenRequire(Tokens.T_OPENBRACKET); } } Expression right = XreadRowValuePredicand(); switch (right.getType()) { case OpTypes.COLUMN : if (period) { throw Error.error(ErrorCode.X_42564); } right = new ExpressionPeriod((ExpressionColumn) right); break; case OpTypes.ROW : if (right.nodes.length == 2) { if (period) { right = new ExpressionPeriod(right); } else { if (opType != OpTypes.RANGE_OVERLAPS) { throw Error.error(ErrorCode.X_42564); } return new ExpressionLogical(OpTypes.OVERLAPS, left, right); } break; } throw Error.error(ErrorCode.X_42564); default : if (opType != OpTypes.RANGE_CONTAINS) { throw Error.error(ErrorCode.X_42564); } if (period) { throw Error.error(ErrorCode.X_42564); } break; } return new ExpressionPeriodOp(opType, left, right); } Expression XreadRowValueExpression() { Expression e = XreadExplicitRowValueConstructorOrNull(); if (e != null) { return e; } return XreadRowValueSpecialCase(); } Expression XreadTableRowValueConstructor() { Expression e = XreadExplicitRowValueConstructorOrNull(); if (e != null) { return e; } return XreadRowValueSpecialCase(); } // union of | // | | // translated to // | | Expression XreadRowValuePredicand() { return XreadRowOrCommonValueExpression(); } Expression XreadRowValueSpecialCase() { Expression e = XreadSimpleValueExpressionPrimary(); if (e != null) { e = XreadArrayElementReference(e); } return e; } // // ISSUE - XreadCommonValueExpression and XreadBooleanValueExpression should merge Expression XreadRowValueConstructor() { Expression e; e = XreadExplicitRowValueConstructorOrNull(); if (e != null) { return e; } e = XreadRowOrCommonValueExpression(); if (e != null) { return e; } return XreadBooleanValueExpression(); } // returns null // must be called in conjunction with -1) { column = rangeVars[i].getColumn(index); read(); break; } } if (column == null) { throw Error.error(ErrorCode.X_42501, token.tokenString); } colIndexList.add(index); if (token.tokenType == Tokens.LEFTBRACKET) { if (!column.getDataType().isArrayType()) { throw unexpectedToken(); } read(); Expression e = XreadNumericValueExpression(); if (e == null) { throw Error.error(ErrorCode.X_42501, token.tokenString); } e = new ExpressionAccessor(column.getAccessor(), e); readThis(Tokens.RIGHTBRACKET); return e; } return column.getAccessor(); } Expression XreadCollectionDerivedTable(int type) { boolean ordinality = false; int position = getPosition(); readThis(Tokens.UNNEST); readThis(Tokens.OPENBRACKET); compileContext.incrementDepth(); HsqlArrayList list = new HsqlArrayList(); while (true) { Expression e = XreadValueExpression(); list.add(e); if (token.tokenType == Tokens.COMMA) { read(); } else { break; } } Expression[] array = new Expression[list.size()]; list.toArray(array); readThis(Tokens.CLOSEBRACKET); if (token.tokenType == Tokens.WITH) { read(); readThis(Tokens.ORDINALITY); ordinality = true; } Expression e = new ExpressionTable(array, ordinality); TableDerived td = newSubQueryTable(e, type); td.setSQL(getLastPart(position)); compileContext.decrementDepth(); return e; } Expression XreadTableFunctionDerivedTable() { int position = getPosition(); readThis(Tokens.TABLE); readThis(Tokens.OPENBRACKET); compileContext.incrementDepth(); Expression e = XreadValueExpression(); if (e.getType() != OpTypes.FUNCTION && e.getType() != OpTypes.SQL_FUNCTION) { compileContext.decrementDepth(); throw unexpectedToken(Tokens.T_TABLE); } readThis(Tokens.CLOSEBRACKET); e = new ExpressionTable(new Expression[]{ e }, false); TableDerived td = newSubQueryTable(e, OpTypes.TABLE_SUBQUERY); td.setSQL(getLastPart(position)); compileContext.decrementDepth(); return e; } Expression XreadLateralDerivedTable() { readThis(Tokens.LATERAL); readThis(Tokens.OPENBRACKET); TableDerived td = XreadSubqueryTableBody(OpTypes.TABLE_SUBQUERY); readThis(Tokens.CLOSEBRACKET); return new Expression(OpTypes.TABLE_SUBQUERY, td); } Expression XreadArrayConstructor() { readThis(Tokens.OPENBRACKET); TableDerived td = XreadSubqueryTableBody(OpTypes.TABLE_SUBQUERY); readThis(Tokens.CLOSEBRACKET); return new Expression(OpTypes.ARRAY_SUBQUERY, td); } // Additional Common Elements Collation readCollateClauseOrNull() { if (token.tokenType == Tokens.COLLATE) { read(); Collation collation = database.schemaManager.getCollation(session, token.tokenString, token.namePrefix); return collation; } return null; } Expression XreadArrayElementReference(Expression e) { if (token.tokenType == Tokens.LEFTBRACKET) { read(); Expression e1 = XreadNumericValueExpression(); readThis(Tokens.RIGHTBRACKET); e = new ExpressionAccessor(e, e1); } return e; } Expression readRow() { Expression r = null; while (true) { Expression e = XreadValueExpressionWithContext(); if (r == null) { r = e; } else if (r.getType() == OpTypes.ROW) { if (e.getType() == OpTypes.ROW && r.nodes[0].getType() != OpTypes.ROW) { r = new Expression(OpTypes.ROW, new Expression[] { r, e }); } else { r.nodes = (Expression[]) ArrayUtil.resizeArray(r.nodes, r.nodes.length + 1); r.nodes[r.nodes.length - 1] = e; } } else { r = new Expression(OpTypes.ROW, new Expression[] { r, e }); } if (token.tokenType != Tokens.COMMA) { break; } read(); } return r; } Expression readCaseExpression() { Expression predicand = null; read(); if (token.tokenType != Tokens.WHEN) { predicand = XreadRowValuePredicand(); } return readCaseWhen(predicand); } /** * Reads part of a CASE .. WHEN expression */ private Expression readCaseWhen(final Expression l) { readThis(Tokens.WHEN); Expression condition = null; if (l == null) { condition = XreadBooleanValueExpression(); } else { while (true) { Expression newCondition = XreadPredicateRightPart(l); if (l == newCondition) { newCondition = new ExpressionLogical(l, XreadRowValuePredicand()); } if (condition == null) { condition = newCondition; } else { condition = new ExpressionLogical(OpTypes.OR, condition, newCondition); } if (token.tokenType == Tokens.COMMA) { read(); } else { break; } } } readThis(Tokens.THEN); Expression current = XreadValueExpression(); Expression elseExpr = null; if (token.tokenType == Tokens.WHEN) { elseExpr = readCaseWhen(l); } else if (token.tokenType == Tokens.ELSE) { read(); elseExpr = XreadValueExpression(); readThis(Tokens.END); readIfThis(Tokens.CASE); } else { elseExpr = new ExpressionValue((Object) null, (Type) null); readThis(Tokens.END); readIfThis(Tokens.CASE); } Expression alt = new ExpressionOp(OpTypes.ALTERNATIVE, current, elseExpr); Expression casewhen = new ExpressionOp(OpTypes.CASEWHEN, condition, alt); return casewhen; } /** * reads a CASEWHEN expression */ private Expression readCaseWhenExpressionOrNull() { Expression l = null; int position = getPosition(); if (token.tokenType == Tokens.IF) { if (database.sqlSyntaxMys || database.sqlSyntaxMss) {} else { return null; } } read(); if (!readIfThis(Tokens.OPENBRACKET)) { rewind(position); return null; } l = XreadBooleanValueExpression(); readThis(Tokens.COMMA); Expression then = XreadValueExpression(); readThis(Tokens.COMMA); Expression thenelse = new ExpressionOp(OpTypes.ALTERNATIVE, then, XreadValueExpression()); l = new ExpressionOp(OpTypes.CASEWHEN, l, thenelse); readThis(Tokens.CLOSEBRACKET); return l; } /** * Reads a CAST expression */ private Expression readCastExpression() { Expression e; Type typeObject; read(); readThis(Tokens.OPENBRACKET); e = XreadValueExpression(); readThis(Tokens.AS); typeObject = readTypeDefinition(false, true); if (e.isUnresolvedParam()) { e.setDataType(session, typeObject); } else { e = new ExpressionOp(e, typeObject); } readThis(Tokens.CLOSEBRACKET); return e; } private Expression readConvertExpressionOrNull() { Expression e; Expression mode = null; Type typeObject; int position = getPosition(); read(); if (!readIfThis(Tokens.OPENBRACKET)) { rewind(position); return null; } if (database.sqlSyntaxMss) { typeObject = readTypeDefinition(false, true); readThis(Tokens.COMMA); e = XreadValueExpression(); if (readIfThis(Tokens.COMMA)) { mode = this.XreadSimpleValueSpecificationOrNull(); } } else { e = XreadValueExpression(); readThis(Tokens.COMMA); typeObject = Type.getTypeForJDBCConvertToken(token.tokenType); if (typeObject == null) { typeObject = readTypeDefinition(false, true); } else { read(); } } if (e.isUnresolvedParam() && mode == null) { e.setDataType(session, typeObject); } else { e = new ExpressionOp(e, typeObject, mode); } readThis(Tokens.CLOSEBRACKET); return e; } /** * reads a Column or Function expression */ private Expression readColumnOrFunctionExpression() { String name = token.tokenString; boolean isSimpleQuoted = isDelimitedSimpleName(); Token recordedToken = getRecordedToken(); checkIsIdentifier(); if (isUndelimitedSimpleName()) { Expression e = readFunction(); if (e != null) { return e; } } read(); if (token.tokenType != Tokens.OPENBRACKET) { checkValidCatalogName(recordedToken.namePrePrePrefix); Expression column = new ExpressionColumn(recordedToken.namePrePrefix, recordedToken.namePrefix, name); return column; } RoutineSchema routineSchema = (RoutineSchema) database.schemaManager.findSchemaObject(session, name, recordedToken.namePrefix, recordedToken.namePrePrefix, SchemaObject.FUNCTION); if (routineSchema == null && recordedToken.namePrefix == null && !isViewDefinition) { String schema = session.getSchemaName(null); ReferenceObject synonym = database.schemaManager.findSynonym(recordedToken.tokenString, schema, SchemaObject.ROUTINE); if (synonym != null) { HsqlName synonymName = synonym.getTarget(); routineSchema = (RoutineSchema) database.schemaManager.findSchemaObject( synonymName.name, synonymName.schema.name, SchemaObject.ROUTINE); } } if (routineSchema == null && isSimpleQuoted) { HsqlName schema = database.schemaManager.getDefaultSchemaHsqlName(); routineSchema = (RoutineSchema) database.schemaManager.findSchemaObject(name, schema.name, SchemaObject.FUNCTION); if (routineSchema == null) { Routine.createRoutines(session, schema, name); routineSchema = (RoutineSchema) database.schemaManager.findSchemaObject( name, schema.name, SchemaObject.FUNCTION); } } if (routineSchema == null) { if (lastError != null) { throw lastError; } throw Error.error(ErrorCode.X_42501, name); } HsqlArrayList list = new HsqlArrayList(); readThis(Tokens.OPENBRACKET); if (token.tokenType == Tokens.CLOSEBRACKET) { read(); } else { while (true) { Expression e = XreadValueExpression(); list.add(e); if (token.tokenType == Tokens.COMMA) { read(); } else { readThis(Tokens.CLOSEBRACKET); break; } } } FunctionSQLInvoked function = new FunctionSQLInvoked(routineSchema); Expression[] arguments = new Expression[list.size()]; list.toArray(arguments); function.setArguments(arguments); compileContext.addFunctionCall(function); recordedToken.setExpression(routineSchema); return function; } Expression readFunction() { FunctionSQL function = FunctionCustom.newCustomFunction(session, token.tokenString, token.tokenType); if (function != null) { int pos = getPosition(); try { Expression e = readSQLFunction(function); if (e != null) { return e; } } catch (HsqlException ex) { ex.setLevel(compileContext.subqueryDepth); if (lastError == null || lastError.getLevel() < ex.getLevel()) { lastError = ex; } rewind(pos); } } else if (isReservedKey()) { function = FunctionSQL.newSQLFunction(token.tokenString, compileContext); if (function != null) { Expression e = readSQLFunction(function); if (e != null) { return e; } } } return null; } Expression readCollection(int type) { read(); if (token.tokenType == Tokens.OPENBRACKET) { return XreadArrayConstructor(); } else { readThis(Tokens.LEFTBRACKET); HsqlArrayList list = new HsqlArrayList(); for (int i = 0; ; i++) { if (token.tokenType == Tokens.RIGHTBRACKET) { read(); break; } if (i > 0) { readThis(Tokens.COMMA); } Expression e = XreadValueExpression(); list.add(e); } Expression[] array = new Expression[list.size()]; list.toArray(array); return new Expression(OpTypes.ARRAY, array); } } private Expression readDecodeExpressionOrNull() { int position = getPosition(); read(); if (!readIfThis(Tokens.OPENBRACKET)) { rewind(position); return null; } Expression casewhen = null; Expression alternative = null; Expression main = XreadValueExpression(); readThis(Tokens.COMMA); do { Expression v = XreadValueExpression(); if (token.tokenType == Tokens.COMMA) { readThis(Tokens.COMMA); } else { if (alternative == null) { throw unexpectedToken(); } alternative.setRightNode(v); break; } Expression l = new ExpressionLogical(OpTypes.NOT_DISTINCT, main, v); Expression r = XreadValueExpression(); Expression a = new ExpressionOp(OpTypes.ALTERNATIVE, r, null); Expression c = new ExpressionOp(OpTypes.CASEWHEN, l, a); if (casewhen == null) { casewhen = c; } else { alternative.setRightNode(c); } alternative = a; if (token.tokenType == Tokens.COMMA) { readThis(Tokens.COMMA); } else { alternative.setRightNode(new ExpressionValue(null, null)); break; } } while (true); readThis(Tokens.CLOSEBRACKET); return casewhen; } private Expression readConcatExpressionOrNull() { Expression root; Expression r; // turn into a concatenation int position = getPosition(); read(); if (!readIfThis(Tokens.OPENBRACKET)) { rewind(position); return null; } root = XreadValueExpression(); readThis(Tokens.COMMA); do { r = XreadValueExpression(); root = new ExpressionArithmetic(OpTypes.CONCAT, root, r); if (token.tokenType == Tokens.COMMA) { readThis(Tokens.COMMA); } else if (token.tokenType == Tokens.CLOSEBRACKET) { readThis(Tokens.CLOSEBRACKET); break; } } while (true); return root; } private Expression readConcatSeparatorExpressionOrNull() { HsqlArrayList array = new HsqlArrayList(); int position = getPosition(); read(); if (!readIfThis(Tokens.OPENBRACKET)) { rewind(position); return null; } Expression e = XreadValueExpression(); array.add(e); readThis(Tokens.COMMA); e = XreadValueExpression(); array.add(e); readThis(Tokens.COMMA); do { e = XreadValueExpression(); array.add(e); if (token.tokenType == Tokens.COMMA) { readThis(Tokens.COMMA); } else if (token.tokenType == Tokens.CLOSEBRACKET) { readThis(Tokens.CLOSEBRACKET); break; } } while (true); Expression[] expressions = new Expression[array.size()]; array.toArray(expressions); return new ExpressionOp(OpTypes.CONCAT_WS, expressions); } private Expression readLeastExpressionOrNull() { int position = getPosition(); read(); if (!readIfThis(Tokens.OPENBRACKET)) { rewind(position); return null; } Expression casewhen = null; do { casewhen = readValue(casewhen, OpTypes.SMALLER); if (token.tokenType == Tokens.COMMA) { readThis(Tokens.COMMA); } else { break; } } while (true); readThis(Tokens.CLOSEBRACKET); return casewhen; } private Expression readGreatestExpressionOrNull() { int position = getPosition(); read(); if (!readIfThis(Tokens.OPENBRACKET)) { rewind(position); return null; } Expression casewhen = null; do { casewhen = readValue(casewhen, OpTypes.GREATER); if (token.tokenType == Tokens.COMMA) { readThis(Tokens.COMMA); } else { break; } } while (true); readThis(Tokens.CLOSEBRACKET); return casewhen; } private Expression readValue(Expression e, int opType) { Expression r = XreadValueExpression(); if (e == null) { return r; } Expression l = new ExpressionLogical(opType, e, r); Expression a = new ExpressionOp(OpTypes.ALTERNATIVE, e, r); return new ExpressionOp(OpTypes.CASEWHEN, l, a); } /** * Reads a NULLIF expression */ private Expression readNullIfExpression() { read(); readThis(Tokens.OPENBRACKET); Expression c = XreadValueExpression(); readThis(Tokens.COMMA); Expression alternative = new ExpressionOp(OpTypes.ALTERNATIVE, new ExpressionValue((Object) null, (Type) null), c); c = new ExpressionLogical(c, XreadValueExpression()); c = new ExpressionOp(OpTypes.CASEWHEN, c, alternative); readThis(Tokens.CLOSEBRACKET); return c; } /** * Reads a ISNULL or ISNULL of NVL expression */ private Expression readIfNullExpressionOrNull() { int position = getPosition(); read(); if (!readIfThis(Tokens.OPENBRACKET)) { rewind(position); return null; } Expression c = XreadValueExpression(); readThis(Tokens.COMMA); Expression e = XreadValueExpression(); Expression condition = new ExpressionLogical(OpTypes.IS_NULL, c); Expression alt = new ExpressionOp(OpTypes.ALTERNATIVE, e, c); c = new ExpressionOp(OpTypes.CASEWHEN, condition, alt); c.setSubType(OpTypes.CAST); alt.setSubType(OpTypes.CAST); readThis(Tokens.CLOSEBRACKET); return c; } /** * Reads a NVL2 expression */ private Expression readIfNull2ExpressionOrNull() { int position = getPosition(); read(); if (!readIfThis(Tokens.OPENBRACKET)) { rewind(position); return null; } Expression c = XreadValueExpression(); readThis(Tokens.COMMA); Expression e1 = XreadValueExpression(); readThis(Tokens.COMMA); Expression e2 = XreadValueExpression(); Expression condition = new ExpressionLogical(OpTypes.IS_NULL, c); Expression alt = new ExpressionOp(OpTypes.ALTERNATIVE, e2, e1); c = new ExpressionOp(OpTypes.CASEWHEN, condition, alt); c.setSubType(OpTypes.CAST); alt.setSubType(OpTypes.CAST); readThis(Tokens.CLOSEBRACKET); return c; } /** * Reads a COALESE expression */ private Expression readCoalesceExpression() { Expression c = null; read(); readThis(Tokens.OPENBRACKET); Expression leaf = null; while (true) { Expression current = XreadValueExpression(); if (leaf != null && token.tokenType == Tokens.CLOSEBRACKET) { readThis(Tokens.CLOSEBRACKET); leaf.setLeftNode(current); break; } Expression expressionNull = new ExpressionValue((Object) null, (Type) null); Expression condition = new ExpressionLogical(OpTypes.IS_NULL, current); Expression alt = new ExpressionOp(OpTypes.ALTERNATIVE, expressionNull, current); Expression casewhen = new ExpressionOp(OpTypes.CASEWHEN, condition, alt); if (session.database.sqlSyntaxMys) { alt.setSubType(OpTypes.CAST); casewhen.setSubType(OpTypes.CAST); } if (c == null) { c = casewhen; } else { leaf.setLeftNode(casewhen); } leaf = alt; readThis(Tokens.COMMA); } return c; } Expression readSQLFunction(FunctionSQL function) { int position = getPosition(); read(); short[] parseList = function.parseList; if (parseList.length == 0) { return function; } HsqlArrayList exprList = new HsqlArrayList(); boolean isOpenBracket = token.tokenType == Tokens.OPENBRACKET; if (!isOpenBracket) { if (parseList[0] == Tokens.X_OPTION) { return function; } else { rewind(position); return null; } } try { readExpression(exprList, parseList, 0, parseList.length, false); lastError = null; } catch (HsqlException e) { if (function.parseListAlt == null) { throw e; } rewind(position); read(); parseList = function.parseListAlt; exprList = new HsqlArrayList(); readExpression(exprList, parseList, 0, parseList.length, false); lastError = null; } Expression[] expr = new Expression[exprList.size()]; exprList.toArray(expr); function.setArguments(expr); return function.getFunctionExpression(); } void readExpression(HsqlArrayList exprList, short[] parseList, int start, int count, boolean isOption) { for (int i = start; i < start + count; i++) { int exprType = parseList[i]; switch (exprType) { case Tokens.QUESTION : { Expression e = null; e = XreadAllTypesCommonValueExpression(false); exprList.add(e); continue; } case Tokens.X_TOKEN : { if (super.isUndelimitedSimpleName()) { Expression e = new ExpressionValue(token.tokenString, Type.SQL_VARCHAR); read(); exprList.add(e); continue; } throw unexpectedToken(); } case Tokens.X_POS_INTEGER : { Expression e = null; Integer value = readIntegerObject(); if (value.intValue() < 0) { throw Error.error(ErrorCode.X_42592); } e = new ExpressionValue(value, Type.SQL_INTEGER); exprList.add(e); continue; } case Tokens.X_OPTION : { i++; int expressionCount = exprList.size(); int position = getPosition(); int elementCount = parseList[i++]; int initialExprIndex = exprList.size(); try { readExpression(exprList, parseList, i, elementCount, true); } catch (HsqlException ex) { ex.setLevel(compileContext.subqueryDepth); if (lastError == null || lastError.getLevel() < ex.getLevel()) { lastError = ex; } rewind(position); exprList.setSize(expressionCount); for (int j = i; j < i + elementCount; j++) { if (parseList[j] == Tokens.QUESTION || parseList[j] == Tokens.X_KEYSET || parseList[j] == Tokens.X_POS_INTEGER) { exprList.add(null); } } i += elementCount - 1; continue; } if (initialExprIndex == exprList.size()) { if (parseList[i] != Tokens.OPENBRACKET) { exprList.add(null); } } i += elementCount - 1; continue; } case Tokens.X_REPEAT : { i++; int elementCount = parseList[i++]; int parseIndex = i; while (true) { int initialExprIndex = exprList.size(); readExpression(exprList, parseList, parseIndex, elementCount, true); if (exprList.size() == initialExprIndex) { break; } } i += elementCount - 1; continue; } case Tokens.X_KEYSET : { int elementCount = parseList[++i]; Expression e = null; if (ArrayUtil.find(parseList, token.tokenType, i + 1, elementCount) == -1) { if (!isOption) { throw unexpectedToken(); } } else { e = new ExpressionValue( ValuePool.getInt(token.tokenType), Type.SQL_INTEGER); read(); } exprList.add(e); i += elementCount; continue; } case Tokens.OPENBRACKET : case Tokens.CLOSEBRACKET : case Tokens.COMMA : default : if (token.tokenType != exprType) { throw unexpectedToken(); } read(); } } } private Expression readSequenceExpressionOrNull(int opType) { int position = getPosition(); switch (opType) { case OpTypes.SEQUENCE : if (token.tokenType == Tokens.NEXT) { read(); if (token.tokenType != Tokens.VALUE) { rewind(position); return null; } readThis(Tokens.VALUE); } else if (database.sqlSyntaxDb2 && token.tokenType == Tokens.NEXTVAL) { read(); } else if (database.sqlSyntaxDb2 && token.tokenType == Tokens.PREVVAL) { read(); } else { rewind(position); return null; } break; case OpTypes.SEQUENCE_CURRENT : read(); readThis(Tokens.VALUE); break; default : } readThis(Tokens.FOR); checkIsSchemaObjectName(); NumberSequence sequence = database.schemaManager.findSequence(session, token.tokenString, token.namePrefix); if (sequence == null) { throw Error.error(ErrorCode.X_42501, token.tokenString); } Token recordedToken = getRecordedToken(); read(); Expression e = new ExpressionColumn(sequence, opType); recordedToken.setExpression(sequence); compileContext.addSequence(sequence); return e; } SimpleName readSimpleName() { checkIsSimpleName(); SimpleName name = HsqlNameManager.getSimpleName(token.tokenString, isDelimitedIdentifier()); read(); return name; } HsqlName readNewSchemaName() { HsqlName name = readNewSchemaObjectName(SchemaObject.SCHEMA, false); SqlInvariants.checkSchemaNameNotSystem(name.name); return name; } HsqlName readNewSchemaObjectName(int type, boolean checkSchema) { checkIsSchemaObjectName(); HsqlName hsqlName = database.nameManager.newHsqlName(token.tokenString, isDelimitedIdentifier(), type); if (token.namePrefix != null) { switch (type) { case SchemaObject.LABEL : case SchemaObject.VARIABLE : case SchemaObject.GRANTEE : case SchemaObject.CATALOG : throw unexpectedToken(); case SchemaObject.CURSOR : { if (token.namePrePrefix == null && !token.isDelimitedPrefix && (Tokens.T_MODULE.equals(token.namePrefix))) { // local } else { throw unexpectedTokenRequire(Tokens.T_MODULE); } break; } case SchemaObject.SCHEMA : { checkValidCatalogName(token.namePrefix); if (token.namePrePrefix != null) { throw tooManyIdentifiers(); } break; } case SchemaObject.SERVER : case SchemaObject.WRAPPER : { checkValidCatalogName(token.namePrefix); if (token.namePrePrefix != null) { throw tooManyIdentifiers(); } break; } case SchemaObject.COLUMN : { throw tooManyIdentifiers(); } default : { checkValidCatalogName(token.namePrePrefix); HsqlName schemaName; if (checkSchema) { schemaName = session.getSchemaHsqlName(token.namePrefix); } else { schemaName = session.database.schemaManager.findSchemaHsqlName( token.namePrefix); if (schemaName == null) { schemaName = database.nameManager.newHsqlName( token.namePrefix, isDelimitedIdentifier(), SchemaObject.SCHEMA); } } hsqlName.setSchemaIfNull(schemaName); break; } } } read(); return hsqlName; } HsqlName readNewDependentSchemaObjectName(HsqlName parentName, int type) { HsqlName name = readNewSchemaObjectName(type, true); name.parent = parentName; name.setSchemaIfNull(parentName.schema); if (name.schema != null && parentName.schema != null && name.schema != parentName.schema) { throw Error.error(ErrorCode.X_42505, token.namePrefix); } return name; } HsqlName readSchemaName() { checkIsSchemaObjectName(); checkValidCatalogName(token.namePrefix); HsqlName schema = session.getSchemaHsqlName(token.tokenString); read(); return schema; } SchemaObject readSchemaObjectName(int type) { checkIsSchemaObjectName(); checkValidCatalogName(token.namePrePrefix); String schema = session.getSchemaName(token.namePrefix); SchemaObject object = database.schemaManager.getSchemaObject(token.tokenString, schema, type); read(); return object; } SchemaObject readSchemaObjectName(HsqlName schemaName, int type) { checkIsSchemaObjectName(); SchemaObject object = database.schemaManager.getSchemaObject(token.tokenString, schemaName.name, type); if (token.namePrefix != null) { if (!token.namePrefix.equals(schemaName.name)) { // todo - better error message throw Error.error(ErrorCode.X_42505, token.namePrefix); } if (token.namePrePrefix != null) { if (!token.namePrePrefix.equals( database.getCatalogName().name)) { // todo - better error message throw Error.error(ErrorCode.X_42505, token.namePrefix); } } } read(); return object; } Table readTableName() { return readTableName(false); } Table readTableName(boolean orSynonym) { checkIsIdentifier(); lastSynonym = null; Table table = database.schemaManager.findTable(session, token.tokenString, token.namePrefix, token.namePrePrefix); if (table == null) { boolean trySynonym = orSynonym && token.namePrefix == null && !isViewDefinition; if (trySynonym) { ReferenceObject reference = database.schemaManager.findSynonym( token.tokenString, session.getCurrentSchemaHsqlName().name, SchemaObject.TABLE); if (reference != null) { table = (Table) database.schemaManager.getSchemaObject( reference.getTarget()); lastSynonym = reference.getName(); } } if (table == null) { throw Error.error(ErrorCode.X_42501, token.tokenString); } } getRecordedToken().setExpression(table); read(); return table; } /** * Returns a period condition (including a default) for all tables with a * system period. Otherwise null; */ ExpressionPeriodOp XreadQuerySystemPeriodSpecOrNull(Table table) { int position = getPosition(); if (!table.isSystemVersioned()) { return null; } if (token.tokenType == Tokens.FOR) { read(); } else { ExpressionPeriodOp periodExpression = new ExpressionPeriodOp(); return periodExpression; } if (token.tokenType == Tokens.SYSTEM_TIME) { read(); } else { rewind(position); return null; } switch (token.tokenType) { case Tokens.AS : { read(); readThis(Tokens.OF); Expression point = XreadValueExpression(); return new ExpressionPeriodOp(point); } case Tokens.BETWEEN : { read(); readIfThis(Tokens.ASYMMETRIC); Expression pointStart = XreadValueExpression(); readThis(Tokens.AND); Expression pointEnd = XreadValueExpression(); return new ExpressionPeriodOp(pointStart, pointEnd); } case Tokens.FROM : { read(); Expression pointStart = XreadValueExpression(); readThis(Tokens.TO); Expression pointEnd = XreadValueExpression(); return new ExpressionPeriodOp(pointStart, pointEnd); } default : throw unexpectedToken(); } } ExpressionPeriodOp XreadQueryApplicationPeriodSpecOrNull(Table table) { PeriodDefinition period = table.getApplicationPeriod(); if (period == null) { return null; } if (token.tokenType == Tokens.FOR) { read(); } else { return null; } readThis(Tokens.PORTION); readThis(Tokens.OF); checkIsSimpleName(); if (!token.tokenString.equals(period.periodName.getNameString())) { throw Error.error(ErrorCode.X_42501, token.tokenString); } read(); readThis(Tokens.FROM); Expression pointStart = XreadValueExpression(); readThis(Tokens.TO); Expression pointEnd = XreadValueExpression(); ExpressionPeriod left = new ExpressionPeriod(period); ExpressionPeriod right = new ExpressionPeriod(pointStart, pointEnd); return new ExpressionPeriodOp(OpTypes.RANGE_OVERLAPS, left, right); } ColumnSchema readSimpleColumnName(RangeVariable rangeVar, boolean withPrefix) { ColumnSchema column = null; checkIsIdentifier(); if (!withPrefix && token.namePrefix != null) { throw tooManyIdentifiers(); } int index = rangeVar.findColumn(token.namePrePrefix, token.namePrefix, token.tokenString); if (index == -1) { throw Error.error(ErrorCode.X_42501, token.tokenString); } column = rangeVar.getTable().getColumn(index); read(); return column; } ColumnSchema readSimpleColumnName(Table table, boolean withPrefix) { checkIsIdentifier(); if (withPrefix) { if (token.namePrefix != null && !table.getName().name.equals(token.namePrefix)) { throw Error.error(ErrorCode.X_42501, token.namePrefix); } } else if (token.namePrefix != null) { throw tooManyIdentifiers(); } int index = table.findColumn(token.tokenString); if (index == -1) { throw Error.error(ErrorCode.X_42501, token.tokenString); } ColumnSchema column = table.getColumn(index); read(); return column; } StatementQuery compileDeclareCursorOrNull(RangeGroup[] rangeGroups, boolean isRoutine) { int sensitivity = ResultConstants.SQL_ASENSITIVE; int scrollability = ResultConstants.SQL_NONSCROLLABLE; int holdability = ResultConstants.SQL_NONHOLDABLE; int returnability = ResultConstants.SQL_WITHOUT_RETURN; int position = getPosition(); readThis(Tokens.DECLARE); HsqlName cursorName = readNewSchemaObjectName(SchemaObject.CURSOR, false); switch (token.tokenType) { case Tokens.SENSITIVE : read(); sensitivity = ResultConstants.SQL_SENSITIVE; break; case Tokens.INSENSITIVE : read(); sensitivity = ResultConstants.SQL_INSENSITIVE; break; case Tokens.ASENSITIVE : read(); break; default : } if (token.tokenType == Tokens.NO) { readThis(Tokens.SCROLL); } else { if (token.tokenType == Tokens.SCROLL) { read(); scrollability = ResultConstants.SQL_SCROLLABLE; } } if (token.tokenType != Tokens.CURSOR) { rewind(position); return null; } readThis(Tokens.CURSOR); for (int round = 0; round < 2; round++) { if (token.tokenType == Tokens.WITH) { read(); if (round == 0 && token.tokenType == Tokens.HOLD) { read(); holdability = ResultConstants.SQL_HOLDABLE; } else { readThis(Tokens.RETURN); round++; returnability = ResultConstants.SQL_WITH_RETURN; } } else if (token.tokenType == Tokens.WITHOUT) { read(); if (round == 0 && token.tokenType == Tokens.HOLD) { read(); } else { readThis(Tokens.RETURN); round++; } } } readThis(Tokens.FOR); int props = ResultProperties.getProperties(sensitivity, ResultConstants.SQL_UPDATABLE, scrollability, holdability, returnability); StatementQuery cs = compileCursorSpecification(rangeGroups, props, isRoutine); cs.setCursorName(cursorName); return cs; } /** * Retrieves a SELECT or other query expression Statement from this parse context. */ StatementQuery compileCursorSpecification(RangeGroup[] rangeGroups, int props, boolean isRoutine) { OrderedHashSet colNames = null; QueryExpression queryExpression = XreadQueryExpression(); if (token.tokenType == Tokens.FOR) { read(); if (token.tokenType == Tokens.READ || token.tokenType == Tokens.FETCH) { read(); readThis(Tokens.ONLY); props = ResultProperties.addUpdatable(props, false); } else { readThis(Tokens.UPDATE); props = ResultProperties.addUpdatable(props, true); if (token.tokenType == Tokens.OF) { readThis(Tokens.OF); colNames = new OrderedHashSet(); readColumnNameList(colNames, null, false); } if (database.sqlSyntaxOra) { readIfThis(Tokens.NOWAIT); } } } if (database.sqlSyntaxDb2) { if (readIfThis(Tokens.WITH)) { if (!readIfThis("CS")) { if (!readIfThis("RR")) { if (!readIfThis("RS")) { readThis("UR"); } } } } if (readIfThis(Tokens.USE)) { readThis(Tokens.AND); readThis(Tokens.T_KEEP); if (!readIfThis(Tokens.T_EXCLUSIVE)) { if (!readIfThis(Tokens.T_SHARE)) { readThis(Tokens.UPDATE); } } readThis(Tokens.LOCKS); } } if (ResultProperties.isUpdatable(props)) { queryExpression.isUpdatable = true; } queryExpression.setReturningResult(); queryExpression.resolve(session, rangeGroups, null); StatementQuery cs = isRoutine ? new StatementCursor(session, queryExpression, compileContext) : new StatementQuery(session, queryExpression, compileContext); return cs; } StatementDMQL compileShortCursorSpecification(int props) { QueryExpression select = XreadSimpleTable(); if (ResultProperties.isUpdatable(props)) { select.isUpdatable = true; } select.setReturningResult(); select.resolve(session); StatementDMQL cs = new StatementQuery(session, select, compileContext); return cs; } int readCloseBrackets(int limit) { int count = 0; while (count < limit && token.tokenType == Tokens.CLOSEBRACKET) { read(); count++; } return count; } int readOpenBrackets() { int count = 0; while (token.tokenType == Tokens.OPENBRACKET) { count++; read(); } return count; } void readNestedParenthesisedTokens() { readThis(Tokens.OPENBRACKET); do { read(); if (token.tokenType == Tokens.OPENBRACKET) { readNestedParenthesisedTokens(); } if (token.tokenType == Tokens.X_ENDPARSE) { throw unexpectedToken(); } } while (token.tokenType != Tokens.CLOSEBRACKET); read(); } void checkValidCatalogName(String name) { if (name != null && !name.equals(database.getCatalogName().name)) { throw Error.error(ErrorCode.X_42501, name); } } void rewind(int position) { super.rewind(position); compileContext.rewind(position); } public static final class CompileContext { final Session session; final ParserBase parser; final CompileContext baseContext; final int basePosition; boolean isViewTable; // private int subqueryDepth; private HsqlArrayList namedSubqueries; // private OrderedIntKeyHashMap parameters = new OrderedIntKeyHashMap(); private HsqlArrayList usedSequences = new HsqlArrayList(8, true); private HsqlArrayList usedRoutines = new HsqlArrayList(8, true); private OrderedIntKeyHashMap rangeVariables = new OrderedIntKeyHashMap(); private HsqlArrayList usedObjects = new HsqlArrayList(8, true); Type currentDomain; boolean contextuallyTypedExpression; Routine callProcedure; // private RangeGroup[] outerRangeGroups = RangeGroup.emptyArray; // private final int initialRangeVarIndex; private int rangeVarIndex; public CompileContext(Session session) { this(session, null, null); } public CompileContext(Session session, ParserBase parser, CompileContext baseContext) { this.session = session; this.parser = parser; this.baseContext = baseContext; if (baseContext == null) { initialRangeVarIndex = rangeVarIndex = 1; basePosition = 0; } else { initialRangeVarIndex = rangeVarIndex = baseContext.getRangeVarCount(); basePosition = baseContext.parser.getPosition(); subqueryDepth = baseContext.getDepth(); } } public void reset() { rangeVarIndex = initialRangeVarIndex; subqueryDepth = 0; rangeVariables.clear(); parameters.clear(); usedSequences.clear(); usedRoutines.clear(); callProcedure = null; usedObjects.clear(); outerRangeGroups = RangeGroup.emptyArray; // currentDomain = null; contextuallyTypedExpression = false; } public int getDepth() { return subqueryDepth; } public void incrementDepth() { subqueryDepth++; if (baseContext != null) { baseContext.subqueryDepth++; } } public void decrementDepth() { clearSubqueries(); subqueryDepth--; if (baseContext != null) { baseContext.subqueryDepth--; } } public void decrementDepth(int toDepth) { while (subqueryDepth > toDepth) { decrementDepth(); } } public void rewind(int position) { if (baseContext != null) { baseContext.rewind(basePosition + position); return; } rewindRangeVariables(position); rewindParameters(position); } private void rewindRangeVariables(int position) { for (int i = rangeVariables.size() - 1; i >= 0; i--) { int rangePos = rangeVariables.getKey(i, -1); if (rangePos > position) { rangeVariables.removeKeyAndValue(i); } } if (rangeVariables.size() > 0) { RangeVariable range = (RangeVariable) rangeVariables.getValue( rangeVariables.size() - 1); rangeVarIndex = range.rangePosition + 1; } else { rangeVarIndex = initialRangeVarIndex; } } private void rewindParameters(int position) { if (baseContext != null) { baseContext.rewindParameters(basePosition + position); return; } Iterator it = parameters.keySet().iterator(); while (it.hasNext()) { int pos = it.nextInt(); if (pos >= position) { it.remove(); } } } public void setCurrentSubquery(HsqlName name) { isViewTable = name.type == SchemaObject.VIEW; } public void registerRangeVariable(RangeVariable range) { int nextRangePosition = basePosition; if (parser != null) { nextRangePosition += parser.getPosition(); } if (isViewTable) { range.isViewSubquery = true; } registerRangeVariable(range, nextRangePosition); } private void registerRangeVariable(RangeVariable range, int position) { if (baseContext != null) { baseContext.registerRangeVariable(range, position); return; } range.rangePosition = getNextRangeVarIndex(); range.level = subqueryDepth; rangeVariables.put(position, range); } public void setNextRangeVarIndex(int n) { if (baseContext != null) { baseContext.setNextRangeVarIndex(n); return; } rangeVarIndex = n; } private int getNextRangeVarIndex() { if (baseContext != null) { return baseContext.getNextRangeVarIndex(); } return rangeVarIndex++; } private int test; public int getNextResultRangeVarIndex() { RangeVariable range = new RangeVariable(null, null, false, RangeVariable.PLACEHOLDER_RANGE); registerRangeVariable(range); return range.rangePosition; } public int getRangeVarCount() { if (baseContext != null) { return baseContext.getRangeVarCount(); } return rangeVarIndex; } public RangeVariable[] getAllRangeVariables() { HsqlArrayList list = new HsqlArrayList(); for (int i = 0; i < rangeVariables.size(); i++) { RangeVariable range = (RangeVariable) rangeVariables.getValue(i); if (range.rangeType != RangeVariable.PLACEHOLDER_RANGE) { list.add(range); } } RangeVariable[] array = new RangeVariable[list.size()]; list.toArray(array); return array; } public RangeGroup[] getOuterRanges() { if (baseContext != null) { return baseContext.outerRangeGroups; } return outerRangeGroups; } public void setOuterRanges(RangeGroup[] rangeGroups) { outerRangeGroups = rangeGroups; } public NumberSequence[] getSequences() { if (usedSequences.size() == 0) { return NumberSequence.emptyArray; } NumberSequence[] array = new NumberSequence[usedSequences.size()]; usedSequences.toArray(array); return array; } public Routine[] getRoutines() { if (callProcedure == null && usedRoutines.size() == 0) { return Routine.emptyArray; } OrderedHashSet set = new OrderedHashSet(); for (int i = 0; i < usedRoutines.size(); i++) { FunctionSQLInvoked function = (FunctionSQLInvoked) usedRoutines.get(i); set.add(function.routine); } if (callProcedure != null) { set.add(callProcedure); } Routine[] array = new Routine[set.size()]; set.toArray(array); return array; } private void initSubqueryNames() { if (namedSubqueries == null) { namedSubqueries = new HsqlArrayList(); } if (namedSubqueries.size() <= subqueryDepth) { namedSubqueries.setSize(subqueryDepth + 1); } HashMappedList set = (HashMappedList) namedSubqueries.get(subqueryDepth); if (set == null) { set = new HashMappedList(); namedSubqueries.set(subqueryDepth, set); } } private void clearSubqueries() { if (namedSubqueries != null) { if (namedSubqueries.size() > subqueryDepth) { HashMappedList set = (HashMappedList) namedSubqueries.get(subqueryDepth); if (set != null) { set.clear(); } } } } private void registerSubquery(String name) { initSubqueryNames(); HashMappedList set = (HashMappedList) namedSubqueries.get(subqueryDepth); boolean added = set.add(name, null); if (!added) { throw Error.error(ErrorCode.X_42504, name); } } private void registerSubquery(String name, TableDerived td) { HashMappedList set = (HashMappedList) namedSubqueries.get(subqueryDepth); set.put(name, td); } private void unregisterSubqueries() { if (namedSubqueries == null) { return; } for (int i = subqueryDepth; i < namedSubqueries.size(); i++) { namedSubqueries.set(i, null); } } private TableDerived getNamedSubQuery(String name) { if (baseContext != null) { TableDerived td = baseContext.getNamedSubQuery(name); if (td != null) { return td; } } if (namedSubqueries == null) { return null; } for (int i = subqueryDepth; i >= 0; i--) { if (namedSubqueries.size() <= i) { continue; } HashMappedList set = (HashMappedList) namedSubqueries.get(i); if (set == null) { continue; } TableDerived td = (TableDerived) set.get(name); if (td != null) { return td; } } return null; } private void addParameter(ExpressionColumn e, int position) { e.parameterIndex = parameters.size(); parameters.put(position, e); } private void addSchemaObject(SchemaObject object) { usedObjects.add(object); } private void addSequence(SchemaObject object) { usedSequences.add(object); } void addFunctionCall(FunctionSQLInvoked function) { usedRoutines.add(function); } void addProcedureCall(Routine procedure) { callProcedure = procedure; } ExpressionColumn[] getParameters() { if (parameters.size() == 0) { return ExpressionColumn.emptyArray; } ExpressionColumn[] result = new ExpressionColumn[parameters.size()]; parameters.valuesToArray(result); parameters.clear(); return result; } public OrderedHashSet getSchemaObjectNames() { OrderedHashSet set = new OrderedHashSet(); for (int i = 0; i < usedSequences.size(); i++) { SchemaObject object = (SchemaObject) usedSequences.get(i); set.add(object.getName()); } for (int i = 0; i < usedObjects.size(); i++) { SchemaObject object = (SchemaObject) usedObjects.get(i); set.add(object.getName()); } for (int i = 0; i < rangeVariables.size(); i++) { RangeVariable range = (RangeVariable) rangeVariables.getValue(i); if (range.isViewSubquery) { continue; } if (range.rangeType == RangeVariable.PLACEHOLDER_RANGE) { continue; } HsqlName name = range.rangeTable.getName(); if (name.schema != SqlInvariants.SYSTEM_SCHEMA_HSQLNAME) { set.add(name); set.addAll(range.getColumnNames()); if (range.periodCondition != null) { if (range.periodCondition.isSystemVersionCondition()) { set.add(range.rangeTable.systemPeriod.getName()); } } } else if (name.type == SchemaObject.TRANSITION) { set.addAll(range.getColumnNames()); } } Routine[] routines = getRoutines(); for (int i = 0; i < routines.length; i++) { set.add(routines[i].getSpecificName()); } return set; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy