Package org.hsqldb

Source Code of org.hsqldb.ParserDML

/* Copyright (c) 2001-2014, 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.RangeGroup.RangeGroupSimple;
import org.hsqldb.error.Error;
import org.hsqldb.error.ErrorCode;
import org.hsqldb.lib.ArrayUtil;
import org.hsqldb.lib.HsqlArrayList;
import org.hsqldb.lib.HsqlList;
import org.hsqldb.lib.LongDeque;
import org.hsqldb.lib.OrderedHashSet;
import org.hsqldb.types.Type;

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

    ParserDML(Session session, Scanner t) {
        super(session, t, null);
    }

    /**
     * Retrieves an INSERT Statement from this parse context.
     */
    StatementDMQL compileInsertStatement(RangeGroup[] rangeGroups) {

        read();
        readThis(Tokens.INTO);

        boolean[]     columnCheckList;
        int[]         columnMap;
        int           colCount;
        Table         table;
        RangeVariable range;
        boolean       overridingUser    = false;
        boolean       overridingSystem  = false;
        boolean       assignsToIdentity = false;
        Token         tableToken;
        boolean       hasColumnList = false;

        tableToken = getRecordedToken();
        range      = readRangeVariableForDataChange(StatementTypes.INSERT);

        range.resolveRangeTableTypes(session, RangeVariable.emptyArray);

        table           = range.getTable();
        columnCheckList = null;
        columnMap       = table.getColumnMap();
        colCount        = table.getColumnCount();

        int   position  = getPosition();
        Table baseTable = table.isTriggerInsertable() ? table
                                                      : table.getBaseTable();

        switch (token.tokenType) {

            case Tokens.DEFAULT : {
                read();
                readThis(Tokens.VALUES);

                Expression insertExpression = new Expression(OpTypes.ROW,
                    new Expression[]{});

                insertExpression = new Expression(OpTypes.VALUELIST,
                                                  new Expression[]{
                                                      insertExpression });
                columnCheckList = table.getNewColumnCheckList();

                StatementDMQL cs = new StatementInsert(session, table,
                                                       columnMap,
                                                       insertExpression,
                                                       columnCheckList,
                                                       compileContext);

                return cs;
            }
            case Tokens.OPENBRACKET : {
                int brackets = readOpenBrackets();

                if (brackets == 1) {
                    boolean isQuery = false;

                    switch (token.tokenType) {

                        case Tokens.WITH :
                        case Tokens.SELECT :
                        case Tokens.TABLE : {
                            rewind(position);

                            isQuery = true;

                            break;
                        }
                        default :
                    }

                    if (isQuery) {
                        break;
                    }

                    OrderedHashSet columnNames = new OrderedHashSet();
                    boolean        withPrefix  = database.sqlSyntaxOra;

                    readSimpleColumnNames(columnNames, range, withPrefix);
                    readThis(Tokens.CLOSEBRACKET);

                    colCount      = columnNames.size();
                    columnMap     = table.getColumnIndexes(columnNames);
                    hasColumnList = true;

                    if (token.tokenType != Tokens.VALUES
                            && token.tokenType != Tokens.OVERRIDING) {
                        break;
                    }

                    // fall through
                } else {
                    rewind(position);

                    break;
                }
            }

            // fall through
            case Tokens.OVERRIDING : {
                if (token.tokenType == Tokens.OVERRIDING) {
                    read();

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

                        overridingUser = true;
                    } else if (token.tokenType == Tokens.SYSTEM) {
                        read();

                        overridingSystem = true;
                    } else {
                        throw unexpectedToken();
                    }

                    readThis(Tokens.VALUE);

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

            // fall through
            case Tokens.VALUES : {
                read();

                columnCheckList = table.getColumnCheckList(columnMap);

                Expression insertExpressions =
                    XreadContextuallyTypedTable(colCount);
                HsqlList unresolved =
                    insertExpressions.resolveColumnReferences(session,
                        RangeGroup.emptyGroup, rangeGroups, null);

                ExpressionColumn.checkColumnsResolved(unresolved);
                insertExpressions.resolveTypes(session, null);
                setParameterTypes(insertExpressions, table, columnMap);

                if (table != baseTable) {
                    int[] baseColumnMap = table.getBaseTableColumnMap();
                    int[] newColumnMap  = new int[columnMap.length];

                    ArrayUtil.projectRow(baseColumnMap, columnMap,
                                         newColumnMap);

                    columnMap = newColumnMap;
                }

                Expression[] rowList = insertExpressions.nodes;

                for (int j = 0; j < rowList.length; j++) {
                    Expression[] rowArgs = rowList[j].nodes;

                    for (int i = 0; i < rowArgs.length; i++) {
                        Expression e = rowArgs[i];
                        ColumnSchema column =
                            baseTable.getColumn(columnMap[i]);

                        if (column.isIdentity()) {
                            assignsToIdentity = true;

                            if (e.getType() != OpTypes.DEFAULT) {
                                if (baseTable.identitySequence.isAlways()) {
                                    if (!overridingUser && !overridingSystem) {
                                        throw Error.error(ErrorCode.X_42543);
                                    }
                                }

                                if (overridingUser) {
                                    rowArgs[i] =
                                        new ExpressionColumn(OpTypes.DEFAULT);
                                }
                            }
                        } else if (column.hasDefault()) {

                            //
                        } else if (column.isGenerated()) {
                            if (e.getType() != OpTypes.DEFAULT) {
                                throw Error.error(ErrorCode.X_42541);
                            }
                        } else {

                            // no explicit default
                        }

                        if (e.isUnresolvedParam()) {
                            e.setAttributesAsColumn(column, true);
                        }
                    }
                }

                if (!assignsToIdentity
                        && (overridingUser || overridingSystem)) {
                    throw unexpectedTokenRequire(Tokens.T_OVERRIDING);
                }

                if (!hasColumnList) {
                    tableToken.setWithColumnList();
                }

                StatementDMQL cs = new StatementInsert(session, table,
                                                       columnMap,
                                                       insertExpressions,
                                                       columnCheckList,
                                                       compileContext);

                return cs;
            }
            case Tokens.WITH :
            case Tokens.SELECT :
            case Tokens.TABLE : {
                break;
            }
            default : {
                throw unexpectedToken();
            }
        }

        columnCheckList = table.getColumnCheckList(columnMap);

        if (table != baseTable) {
            int[] baseColumnMap = table.getBaseTableColumnMap();
            int[] newColumnMap  = new int[columnMap.length];

            ArrayUtil.projectRow(baseColumnMap, columnMap, newColumnMap);

            columnMap = newColumnMap;
        }

        int enforcedDefaultIndex = baseTable.getIdentityColumnIndex();
        int overrideIndex        = -1;

        if (enforcedDefaultIndex != -1
                && ArrayUtil.find(columnMap, enforcedDefaultIndex) > -1) {
            if (baseTable.identitySequence.isAlways()) {
                if (!overridingUser && !overridingSystem) {
                    throw Error.error(ErrorCode.X_42543);
                }
            }

            if (overridingUser) {
                overrideIndex = enforcedDefaultIndex;
            }
        } else if (overridingUser || overridingSystem) {
            throw unexpectedTokenRequire(Tokens.T_OVERRIDING);
        }

        Type[] types = new Type[columnMap.length];

        ArrayUtil.projectRow(baseTable.getColumnTypes(), columnMap, types);
        compileContext.setOuterRanges(rangeGroups);

        QueryExpression queryExpression = XreadQueryExpression();

        queryExpression.setReturningResult();
        queryExpression.resolve(session, rangeGroups, types);

        if (colCount != queryExpression.getColumnCount()) {
            throw Error.error(ErrorCode.X_42546);
        }

        if (!hasColumnList) {
            tableToken.setWithColumnList();
        }

        StatementDMQL cs = new StatementInsert(session, table, columnMap,
                                               columnCheckList,
                                               queryExpression,
                                               compileContext, overrideIndex);

        return cs;
    }

    private static void setParameterTypes(Expression tableExpression,
                                          Table table, int[] columnMap) {

        for (int i = 0; i < tableExpression.nodes.length; i++) {
            Expression[] list = tableExpression.nodes[i].nodes;

            for (int j = 0; j < list.length; j++) {
                if (list[j].isUnresolvedParam()) {
                    list[j].setAttributesAsColumn(
                        table.getColumn(columnMap[j]), true);
                }
            }
        }
    }

    Statement compileTruncateStatement() {

        boolean         isTable         = false;
        boolean         withCommit      = false;
        boolean         noCheck         = false;
        boolean         restartIdentity = false;
        HsqlName        objectName      = null;
        RangeVariable[] rangeVariables  = null;
        Table           table           = null;
        HsqlName[]      writeTableNames = null;
        RangeVariable   targetRange     = null;

        readThis(Tokens.TRUNCATE);

        if (token.tokenType == Tokens.TABLE) {
            readThis(Tokens.TABLE);

            targetRange =
                readRangeVariableForDataChange(StatementTypes.TRUNCATE);
            rangeVariables = new RangeVariable[]{ targetRange };
            table          = rangeVariables[0].getTable();
            objectName     = table.getName();
            isTable        = true;
        } else {
            readThis(Tokens.SCHEMA);

            objectName = readSchemaName();
        }

        switch (token.tokenType) {

            case Tokens.CONTINUE : {
                read();
                readThis(Tokens.IDENTITY);

                break;
            }
            case Tokens.RESTART : {
                read();
                readThis(Tokens.IDENTITY);

                restartIdentity = true;

                break;
            }
        }

        if (!isTable) {
            checkIsThis(Tokens.AND);
        }

        if (readIfThis(Tokens.AND)) {
            readThis(Tokens.COMMIT);

            withCommit = true;

            if (readIfThis(Tokens.NO)) {
                readThis(Tokens.CHECK);

                noCheck = true;
            }
        }

        if (isTable) {
            writeTableNames = new HsqlName[]{ table.getName() };
        } else {
            writeTableNames =
                session.database.schemaManager.getCatalogAndBaseTableNames();
        }

        if (withCommit) {
            Object[] args = new Object[] {
                objectName, restartIdentity, noCheck
            };

            return new StatementCommand(StatementTypes.TRUNCATE, args, null,
                                        writeTableNames);
        }

        Statement cs = new StatementDML(session, table, targetRange,
                                        rangeVariables, compileContext,
                                        restartIdentity,
                                        StatementTypes.TRUNCATE);

        return cs;
    }

    /**
     * Creates a DELETE-type Statement from this parse context.
     */
    Statement compileDeleteStatement(RangeGroup[] rangeGroups) {

        Expression      condition       = null;
        boolean         restartIdentity = false;
        RangeVariable   targetRange;
        RangeVariable[] rangeVariables;
        Table           table;

        readThis(Tokens.DELETE);
        readThis(Tokens.FROM);

        targetRange =
            readRangeVariableForDataChange(StatementTypes.DELETE_WHERE);
        rangeVariables = new RangeVariable[]{ targetRange };
        table          = rangeVariables[0].getTable();

        compileContext.setOuterRanges(rangeGroups);

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

            condition = XreadBooleanValueExpression();

            RangeGroup rangeGroup = new RangeGroupSimple(rangeVariables,
                false);
            HsqlList unresolved = condition.resolveColumnReferences(session,
                rangeGroup, rangeGroups, null);

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

            if (condition.isUnresolvedParam()) {
                condition.dataType = Type.SQL_BOOLEAN;
            }

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

        Table baseTable = table.isTriggerDeletable() ? table
                                                     : table.getBaseTable();

        if (table != baseTable) {
            QuerySpecification baseSelect =
                table.getQueryExpression().getMainSelect();

            if (condition != null) {
                condition =
                    condition.replaceColumnReferences(rangeVariables[0],
                                                      baseSelect.exprColumns);
            }

            condition =
                ExpressionLogical.andExpressions(baseSelect.queryCondition,
                                                 condition);
            rangeVariables = baseSelect.rangeVariables;
        }

        if (condition != null) {
            rangeVariables[0].addJoinCondition(condition);

            RangeVariableResolver resolver =
                new RangeVariableResolver(rangeVariables, null,
                                          compileContext, false);

            resolver.processConditions(session);

            rangeVariables = resolver.rangeVariables;
        }

        for (int i = 0; i < rangeVariables.length; i++) {
            rangeVariables[i].resolveRangeTableTypes(session,
                    RangeVariable.emptyArray);
        }

        Statement cs = new StatementDML(session, table, targetRange,
                                        rangeVariables, compileContext,
                                        restartIdentity,
                                        StatementTypes.DELETE_WHERE);

        return cs;
    }

    /**
     * Creates an UPDATE-type Statement from this parse context.
     */
    StatementDMQL compileUpdateStatement(RangeGroup[] rangeGroups) {

        read();

        Expression[]    updateExpressions;
        int[]           columnMap;
        boolean[]       columnCheckList;
        OrderedHashSet  targetSet    = new OrderedHashSet();
        LongDeque       colIndexList = new LongDeque();
        HsqlArrayList   exprList     = new HsqlArrayList();
        RangeVariable   targetRange;
        RangeVariable[] rangeVariables;
        RangeGroup      rangeGroup;
        Table           table;
        Table           baseTable;

        targetRange =
            readRangeVariableForDataChange(StatementTypes.UPDATE_WHERE);
        rangeVariables = new RangeVariable[]{ targetRange };
        rangeGroup     = new RangeGroupSimple(rangeVariables, false);
        table          = rangeVariables[0].rangeTable;
        baseTable      = table.isTriggerUpdatable() ? table
                                                    : table.getBaseTable();

        readThis(Tokens.SET);
        readSetClauseList(rangeVariables, targetSet, colIndexList, exprList);

        columnMap = new int[colIndexList.size()];

        colIndexList.toArray(columnMap);

        Expression[] targets = new Expression[targetSet.size()];

        targetSet.toArray(targets);

        for (int i = 0; i < targets.length; i++) {
            this.resolveOuterReferencesAndTypes(rangeGroups, targets[i]);
        }

        columnCheckList   = table.getColumnCheckList(columnMap);
        updateExpressions = new Expression[exprList.size()];

        exprList.toArray(updateExpressions);

        Expression condition = null;

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

            condition = XreadBooleanValueExpression();

            HsqlList unresolved = condition.resolveColumnReferences(session,
                rangeGroup, rangeGroups, null);

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

            if (condition.isUnresolvedParam()) {
                condition.dataType = Type.SQL_BOOLEAN;
            }

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

        resolveUpdateExpressions(table, rangeGroup, columnMap,
                                 updateExpressions, rangeGroups);

        if (table != baseTable) {
            QuerySpecification baseSelect =
                ((TableDerived) table).getQueryExpression().getMainSelect();

            if (condition != null) {
                condition =
                    condition.replaceColumnReferences(rangeVariables[0],
                                                      baseSelect.exprColumns);
            }

            for (int i = 0; i < updateExpressions.length; i++) {
                updateExpressions[i] =
                    updateExpressions[i].replaceColumnReferences(
                        rangeVariables[0], baseSelect.exprColumns);
            }

            condition =
                ExpressionLogical.andExpressions(baseSelect.queryCondition,
                                                 condition);
            rangeVariables = baseSelect.rangeVariables;
        }

        if (condition != null) {
            rangeVariables[0].addJoinCondition(condition);

            RangeVariableResolver resolver =
                new RangeVariableResolver(rangeVariables, null,
                                          compileContext, false);

            resolver.processConditions(session);

            rangeVariables = resolver.rangeVariables;
        }

        for (int i = 0; i < rangeVariables.length; i++) {
            rangeVariables[i].resolveRangeTableTypes(session,
                    RangeVariable.emptyArray);
        }

        if (table != baseTable) {
            int[] baseColumnMap = table.getBaseTableColumnMap();
            int[] newColumnMap  = new int[columnMap.length];

            ArrayUtil.projectRow(baseColumnMap, columnMap, newColumnMap);

            columnMap = newColumnMap;

            for (int i = 0; i < columnMap.length; i++) {
                if (baseTable.colGenerated[columnMap[i]]) {
                    throw Error.error(ErrorCode.X_42513);
                }
            }
        }

        StatementDMQL cs = new StatementDML(session, targets, table,
                                            targetRange, rangeVariables,
                                            columnMap, updateExpressions,
                                            columnCheckList, compileContext);

        return cs;
    }

    void resolveUpdateExpressions(Table targetTable, RangeGroup rangeGroup,
                                  int[] columnMap,
                                  Expression[] colExpressions,
                                  RangeGroup[] rangeGroups) {

        HsqlList unresolved           = null;
        int      enforcedDefaultIndex = -1;

        if (targetTable.hasIdentityColumn()
                && targetTable.identitySequence.isAlways()) {
            enforcedDefaultIndex = targetTable.getIdentityColumnIndex();
        }

        for (int i = 0, ix = 0; i < columnMap.length; ix++) {
            Expression expr = colExpressions[ix];
            Expression e;

            // no generated column can be updated
            if (targetTable.colGenerated[columnMap[i]]) {
                throw Error.error(ErrorCode.X_42513);
            }

            if (expr.getType() == OpTypes.ROW) {
                Expression[] elements = expr.nodes;

                for (int j = 0; j < elements.length; j++, i++) {
                    e = elements[j];

                    if (enforcedDefaultIndex == columnMap[i]) {
                        if (e.getType() != OpTypes.DEFAULT) {
                            throw Error.error(ErrorCode.X_42541);
                        }
                    }

                    if (e.isUnresolvedParam()) {
                        e.setAttributesAsColumn(
                            targetTable.getColumn(columnMap[i]), true);
                    } else if (e.getType() == OpTypes.DEFAULT) {

                        //
                    } else {
                        unresolved = expr.resolveColumnReferences(session,
                                rangeGroup, rangeGroups, null);

                        ExpressionColumn.checkColumnsResolved(unresolved);

                        unresolved = null;

                        e.resolveTypes(session, null);
                    }
                }
            } else if (expr.getType() == OpTypes.ROW_SUBQUERY) {
                unresolved = expr.resolveColumnReferences(session, rangeGroup,
                        rangeGroups, null);

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

                int count = expr.table.queryExpression.getColumnCount();

                for (int j = 0; j < count; j++, i++) {
                    if (enforcedDefaultIndex == columnMap[i]) {
                        throw Error.error(ErrorCode.X_42541);
                    }
                }
            } else {
                e = expr;

                if (enforcedDefaultIndex == columnMap[i]) {
                    if (e.getType() != OpTypes.DEFAULT) {
                        throw Error.error(ErrorCode.X_42541);
                    }
                }

                if (e.isUnresolvedParam()) {
                    e.setAttributesAsColumn(
                        targetTable.getColumn(columnMap[i]), true);
                } else if (e.getType() == OpTypes.DEFAULT) {

                    //
                } else {
                    unresolved = expr.resolveColumnReferences(session,
                            rangeGroup, rangeGroups, null);

                    ExpressionColumn.checkColumnsResolved(unresolved);
                    e.resolveTypes(session, null);
                }

                i++;
            }
        }
    }

    void readSetClauseList(RangeVariable[] rangeVars, OrderedHashSet targets,
                           LongDeque colIndexList, HsqlArrayList expressions) {

        while (true) {
            int degree;

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

                int oldCount = targets.size();

                readTargetSpecificationList(targets, rangeVars, colIndexList);

                degree = targets.size() - oldCount;

                readThis(Tokens.CLOSEBRACKET);
            } else {
                Expression target = XreadTargetSpecification(rangeVars,
                    colIndexList);

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

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

                degree = 1;
            }

            readThis(Tokens.EQUALS);

            int position = getPosition();
            int brackets = readOpenBrackets();

            if (token.tokenType == Tokens.SELECT) {
                rewind(position);

                TableDerived td = XreadSubqueryTableBody(OpTypes.ROW_SUBQUERY);

                if (degree != td.queryExpression.getColumnCount()) {
                    throw Error.error(ErrorCode.X_42546);
                }

                Expression e = new Expression(OpTypes.ROW_SUBQUERY, td);

                expressions.add(e);

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

                    continue;
                }

                break;
            }

            if (brackets > 0) {
                rewind(position);
            }

            if (degree > 1) {
                readThis(Tokens.OPENBRACKET);

                Expression e = readRow();

                readThis(Tokens.CLOSEBRACKET);

                int rowDegree = e.getType() == OpTypes.ROW ? e.nodes.length
                                                           : 1;

                if (degree != rowDegree) {
                    throw Error.error(ErrorCode.X_42546);
                }

                expressions.add(e);
            } else {
                Expression e = XreadValueExpressionWithContext();

                expressions.add(e);
            }

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

                continue;
            }

            break;
        }
    }

    void readGetClauseList(RangeVariable[] rangeVars, OrderedHashSet targets,
                           LongDeque colIndexList, HsqlArrayList expressions) {

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

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

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

            readThis(Tokens.EQUALS);

            switch (token.tokenType) {

                case Tokens.ROW_COUNT :
                case Tokens.MORE :
                    int columnIndex =
                        ExpressionColumn.diagnosticsList.getIndex(
                            token.tokenString);
                    Expression e =
                        new ExpressionColumn(OpTypes.DIAGNOSTICS_VARIABLE,
                                             columnIndex);

                    expressions.add(e);
                    read();
                    break;
            }

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

                continue;
            }

            break;
        }
    }

    /**
     * Retrieves a MERGE Statement from this parse context.
     */
    StatementDMQL compileMergeStatement(RangeGroup[] rangeGroups) {

        boolean[]     insertColumnCheckList;
        int[]         insertColumnMap = null;
        int[]         updateColumnMap = null;
        int[]         baseUpdateColumnMap;
        Table         table;
        RangeVariable targetRange;
        RangeVariable sourceRange;
        Expression    mergeCondition;
        Expression[]  targets           = null;
        HsqlArrayList updateList        = new HsqlArrayList();
        Expression[]  updateExpressions = Expression.emptyArray;
        HsqlArrayList insertList        = new HsqlArrayList();
        Expression    insertExpression  = null;

        read();
        readThis(Tokens.INTO);

        targetRange = readRangeVariableForDataChange(StatementTypes.MERGE);
        table       = targetRange.rangeTable;

        readThis(Tokens.USING);
        compileContext.setOuterRanges(rangeGroups);

        sourceRange = readTableOrSubquery();

        RangeVariable[] targetRanges = new RangeVariable[]{ targetRange };

        sourceRange.resolveRangeTable(
            session, new RangeGroupSimple(targetRanges, false), rangeGroups);;
        sourceRange.resolveRangeTableTypes(session, targetRanges);
        compileContext.setOuterRanges(RangeGroup.emptyArray);

        // parse ON search conditions
        readThis(Tokens.ON);

        mergeCondition = XreadBooleanValueExpression();

        RangeVariable[] fullRangeVars   = new RangeVariable[] {
            sourceRange, targetRange
        };
        RangeVariable[] sourceRangeVars = new RangeVariable[]{ sourceRange };
        RangeVariable[] targetRangeVars = new RangeVariable[]{ targetRange };
        RangeGroup fullRangeGroup = new RangeGroupSimple(fullRangeVars, false);
        RangeGroup sourceRangeGroup = new RangeGroupSimple(sourceRangeVars,
            false);

        // parse WHEN clause(s) and convert lists to arrays
        insertColumnMap       = table.getColumnMap();
        insertColumnCheckList = table.getNewColumnCheckList();

        OrderedHashSet updateTargetSet    = new OrderedHashSet();
        OrderedHashSet insertColNames     = new OrderedHashSet();
        LongDeque      updateColIndexList = new LongDeque();

        readMergeWhen(updateColIndexList, insertColNames, updateTargetSet,
                      insertList, updateList, targetRangeVars, sourceRange);

        if (insertList.size() > 0) {
            int colCount = insertColNames.size();

            if (colCount != 0) {
                insertColumnMap = table.getColumnIndexes(insertColNames);
                insertColumnCheckList =
                    table.getColumnCheckList(insertColumnMap);
            }

            insertExpression = (Expression) insertList.get(0);

            setParameterTypes(insertExpression, table, insertColumnMap);
        }

        if (updateList.size() > 0) {
            targets = new Expression[updateTargetSet.size()];

            updateTargetSet.toArray(targets);

            for (int i = 0; i < targets.length; i++) {
                this.resolveOuterReferencesAndTypes(rangeGroups, targets[i]);
            }

            updateExpressions = new Expression[updateList.size()];

            updateList.toArray(updateExpressions);

            updateColumnMap = new int[updateColIndexList.size()];

            updateColIndexList.toArray(updateColumnMap);
        }

        if (updateExpressions.length != 0) {
            Table baseTable = table.isTriggerUpdatable() ? table
                                                         : table.getBaseTable();

            baseUpdateColumnMap = updateColumnMap;

            if (table != baseTable) {
                baseUpdateColumnMap = new int[updateColumnMap.length];

                ArrayUtil.projectRow(table.getBaseTableColumnMap(),
                                     updateColumnMap, baseUpdateColumnMap);
            }

            resolveUpdateExpressions(table, fullRangeGroup, updateColumnMap,
                                     updateExpressions, rangeGroups);
        }

        HsqlList unresolved = null;

        unresolved = mergeCondition.resolveColumnReferences(session,
                fullRangeGroup, rangeGroups, null);

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

        if (mergeCondition.isUnresolvedParam()) {
            mergeCondition.dataType = Type.SQL_BOOLEAN;
        }

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

        fullRangeVars[1].addJoinCondition(mergeCondition);

        RangeVariableResolver resolver =
            new RangeVariableResolver(fullRangeVars, null, compileContext,
                                      false);

        resolver.processConditions(session);

        fullRangeVars = resolver.rangeVariables;

        for (int i = 0; i < fullRangeVars.length; i++) {
            fullRangeVars[i].resolveRangeTableTypes(session,
                    RangeVariable.emptyArray);
        }

        if (insertExpression != null) {
            unresolved = insertExpression.resolveColumnReferences(session,
                    sourceRangeGroup, RangeGroup.emptyArray, null);
            unresolved = Expression.resolveColumnSet(session,
                    RangeVariable.emptyArray, rangeGroups, unresolved);

            ExpressionColumn.checkColumnsResolved(unresolved);
            insertExpression.resolveTypes(session, null);
        }

        StatementDMQL cs = new StatementDML(session, targets, sourceRange,
                                            targetRange, fullRangeVars,
                                            insertColumnMap, updateColumnMap,
                                            insertColumnCheckList,
                                            mergeCondition, insertExpression,
                                            updateExpressions, compileContext);

        return cs;
    }

    /**
     * Parses a WHEN clause from a MERGE statement. This can be either a
     * WHEN MATCHED or WHEN NOT MATCHED clause, or both, and the appropriate
     * values will be updated.
     *
     * If the var that is to hold the data is not null, then we already
     * encountered this type of clause, which is only allowed once, and at least
     * one is required.
     */
    private void readMergeWhen(LongDeque updateColIndexList,
                               OrderedHashSet insertColumnNames,
                               OrderedHashSet updateTargetSet,
                               HsqlArrayList insertExpressions,
                               HsqlArrayList updateExpressions,
                               RangeVariable[] targetRangeVars,
                               RangeVariable sourceRangeVar) {

        Table table       = targetRangeVars[0].rangeTable;
        int   columnCount = table.getColumnCount();

        readThis(Tokens.WHEN);

        if (token.tokenType == Tokens.MATCHED) {
            if (updateExpressions.size() != 0) {
                throw Error.error(ErrorCode.X_42547);
            }

            read();
            readThis(Tokens.THEN);
            readThis(Tokens.UPDATE);
            readThis(Tokens.SET);
            readSetClauseList(targetRangeVars, updateTargetSet,
                              updateColIndexList, updateExpressions);
        } else if (token.tokenType == Tokens.NOT) {
            if (insertExpressions.size() != 0) {
                throw Error.error(ErrorCode.X_42548);
            }

            read();
            readThis(Tokens.MATCHED);
            readThis(Tokens.THEN);
            readThis(Tokens.INSERT);

            // parse INSERT statement
            // optional column list
            int brackets = readOpenBrackets();

            if (brackets == 1) {
                boolean withPrefix = database.sqlSyntaxOra;

                readSimpleColumnNames(insertColumnNames, targetRangeVars[0],
                                      withPrefix);

                columnCount = insertColumnNames.size();

                readThis(Tokens.CLOSEBRACKET);

                brackets = 0;
            }

            readThis(Tokens.VALUES);

            Expression e = XreadContextuallyTypedTable(columnCount);

            if (e.nodes.length != 1) {
                throw Error.error(ErrorCode.X_21000);
            }

            insertExpressions.add(e);
        } else {
            throw unexpectedToken();
        }

        if (token.tokenType == Tokens.WHEN) {
            readMergeWhen(updateColIndexList, insertColumnNames,
                          updateTargetSet, insertExpressions,
                          updateExpressions, targetRangeVars, sourceRangeVar);
        }
    }

    /**
     * Retrieves a CALL Statement from this parse context.
     */

    // to do call argument name and type resolution
    StatementDMQL compileCallStatement(RangeGroup[] rangeGroups,
                                       boolean isStrictlyProcedure) {

        read();

        if (isIdentifier()) {
            checkValidCatalogName(token.namePrePrefix);

            RoutineSchema routineSchema =
                (RoutineSchema) database.schemaManager.findSchemaObject(
                    token.tokenString,
                    session.getSchemaName(token.namePrefix),
                    SchemaObject.PROCEDURE);

            if (routineSchema != null) {
                read();

                HsqlArrayList list    = new HsqlArrayList();
                boolean       bracket = true;

                if (database.sqlSyntaxOra) {
                    bracket = readIfThis(Tokens.OPENBRACKET);
                } else {
                    readThis(Tokens.OPENBRACKET);
                }

                if (bracket) {
                    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;
                            }
                        }
                    }
                }

                Expression[] arguments = new Expression[list.size()];

                list.toArray(arguments);

                Routine routine =
                    routineSchema.getSpecificRoutine(arguments.length);

                compileContext.addProcedureCall(routine);

                HsqlList unresolved = null;

                for (int i = 0; i < arguments.length; i++) {
                    Expression e = arguments[i];

                    if (e.isUnresolvedParam()) {
                        e.setAttributesAsColumn(
                            routine.getParameter(i),
                            routine.getParameter(i).isWriteable());
                    } else {
                        int paramMode =
                            routine.getParameter(i).getParameterMode();

                        unresolved =
                            arguments[i].resolveColumnReferences(session,
                                RangeGroup.emptyGroup, rangeGroups,
                                unresolved);

                        if (paramMode
                                != SchemaObject.ParameterModes.PARAM_IN) {
                            if (e.getType() != OpTypes.VARIABLE) {
                                throw Error.error(ErrorCode.X_42603);
                            }
                        }
                    }
                }

                ExpressionColumn.checkColumnsResolved(unresolved);

                for (int i = 0; i < arguments.length; i++) {
                    arguments[i].resolveTypes(session, null);

                    if (!routine.getParameter(
                            i).getDataType().canBeAssignedFrom(
                            arguments[i].getDataType())) {
                        throw Error.error(ErrorCode.X_42561);
                    }
                }

                StatementDMQL cs = new StatementProcedure(session, routine,
                    arguments, compileContext);

                return cs;
            }
        }

        if (isStrictlyProcedure) {
            throw Error.error(ErrorCode.X_42501, token.tokenString);
        }

        Expression expression = this.XreadValueExpression();
        HsqlList unresolved = expression.resolveColumnReferences(session,
            RangeGroup.emptyGroup, rangeGroups, null);

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

        StatementDMQL cs = new StatementProcedure(session, expression,
            compileContext);

        return cs;
    }

    void resolveOuterReferencesAndTypes(RangeGroup[] rangeGroups,
                                        Expression e) {

        HsqlList unresolved = e.resolveColumnReferences(session,
            RangeGroup.emptyGroup, 0, rangeGroups, null, false);

        ExpressionColumn.checkColumnsResolved(unresolved);
        e.resolveTypes(session, null);
    }
}
TOP

Related Classes of org.hsqldb.ParserDML

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