Package com.foundationdb.sql.optimizer

Source Code of com.foundationdb.sql.optimizer.AISBinder$FunctionDefined

/**
* Copyright (C) 2009-2013 FoundationDB, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

package com.foundationdb.sql.optimizer;

import com.foundationdb.server.error.AmbiguousColumNameException;
import com.foundationdb.server.error.CorrelationNameAlreadyUsedException;
import com.foundationdb.server.error.JoinNodeAdditionException;
import com.foundationdb.server.error.NoSuchColumnException;
import com.foundationdb.server.error.NoSuchFunctionException;
import com.foundationdb.server.error.NoSuchTableException;
import com.foundationdb.server.error.NoTableSpecifiedInQueryException;
import com.foundationdb.server.error.ProcedureCalledAsFunctionException;
import com.foundationdb.server.error.SQLParserInternalException;
import com.foundationdb.server.error.SelectExistsErrorException;
import com.foundationdb.server.error.SubqueryOneColumnException;
import com.foundationdb.server.error.TableIsBadSubqueryException;
import com.foundationdb.server.error.UnsupportedFullOuterJoinException;
import com.foundationdb.server.error.ViewHasBadSubqueryException;
import com.foundationdb.server.error.WholeGroupQueryException;
import com.foundationdb.sql.StandardException;
import com.foundationdb.sql.parser.*;
import com.foundationdb.sql.views.ViewDefinition;
import com.foundationdb.ais.model.AkibanInformationSchema;
import com.foundationdb.ais.model.Column;
import com.foundationdb.ais.model.Columnar;
import com.foundationdb.ais.model.Join;
import com.foundationdb.ais.model.JoinColumn;
import com.foundationdb.ais.model.Routine;
import com.foundationdb.ais.model.Table;
import com.foundationdb.ais.model.View;

import java.util.*;

public class AISBinder implements Visitor
{
    private AkibanInformationSchema ais;
    private String defaultSchemaName;
    private Deque<BindingContext> bindingContexts;
    private Set<QueryTreeNode> visited;
    private boolean resultColumnsAvailableBroadly;
    private boolean allowSubqueryMultipleColumns;
    private Set<ValueNode> havingClauses;
    private AISBinderContext context;
    private boolean expandViews;
    private FunctionDefined functionDefined;

    public AISBinder(AkibanInformationSchema ais, String defaultSchemaName) {
        this.ais = ais;
        this.defaultSchemaName = defaultSchemaName;
    }

    public String getDefaultSchemaName() {
        return defaultSchemaName;
    }

    public void setDefaultSchemaName(String defaultSchemaName) {
        this.defaultSchemaName = defaultSchemaName;
    }

    public boolean isResultColumnsAvailableBroadly() {
        return resultColumnsAvailableBroadly;
    }

    public void setResultColumnsAvailableBroadly(boolean resultColumnsAvailableBroadly) {
        this.resultColumnsAvailableBroadly = resultColumnsAvailableBroadly;
    }

    public boolean isAllowSubqueryMultipleColumns() {
        return allowSubqueryMultipleColumns;
    }

    public void setAllowSubqueryMultipleColumns(boolean allowSubqueryMultipleColumns) {
        this.allowSubqueryMultipleColumns = allowSubqueryMultipleColumns;
    }

    public interface FunctionDefined {
        public boolean isDefined(String name);
    }

    public void setFunctionDefined(FunctionDefined functionDefined) {
        this.functionDefined = functionDefined;
    }

    public AISBinderContext getContext() {
        return context;
    }

    protected void setContext(AISBinderContext context) {
        this.context = context;
    }

    public void bind(StatementNode stmt) throws StandardException {
        bind(stmt, true);
    }

    public void bind(QueryTreeNode node, boolean expandViews) throws StandardException {
        this.expandViews = expandViews;
        visited = new HashSet<>();
        bindingContexts = new ArrayDeque<>();
        havingClauses = new HashSet<>();
        try {
            node.accept(this);
        }
        finally {
            visited = null;
            bindingContexts = null;
        }
    }
   
    /* Hierarchical Visitor */

    public boolean visitBefore(QueryTreeNode node)  {
        boolean first = visited.add(node);

        if (first) {
            switch (node.getNodeType()) {
            case NodeTypes.SUBQUERY_NODE:
                subqueryNode((SubqueryNode)node);
                break;
            case NodeTypes.SELECT_NODE:
                selectNode((SelectNode)node);
                break;
            case NodeTypes.COLUMN_REFERENCE:
                columnReference((ColumnReference)node);
                break;
            case NodeTypes.INSERT_NODE:
            case NodeTypes.UPDATE_NODE:
            case NodeTypes.DELETE_NODE:
                dmlModStatementNode((DMLModStatementNode)node);
                break;
            case NodeTypes.UNION_NODE:
                unionNode((UnionNode)node);
                break;
            case NodeTypes.INTERSECT_OR_EXCEPT_NODE:
                intersectOrExceptNode((IntersectOrExceptNode)node);
                break;
            case NodeTypes.JAVA_TO_SQL_VALUE_NODE:
                javaValueNode(((JavaToSQLValueNode)node).getJavaValueNode());
            }
//TODO Add default case for nodes that are not implemented in either switch so easier to find bug

        }

        switch (node.getNodeType()) {
        case NodeTypes.CURSOR_NODE:
        case NodeTypes.DELETE_NODE:
        case NodeTypes.INSERT_NODE:
        case NodeTypes.UPDATE_NODE:
            pushBindingContext(((DMLStatementNode)node).getResultSetNode());
            break;
        case NodeTypes.FROM_SUBQUERY:
            pushBindingContext(((FromSubquery)node).getSubquery());
            break;
        case NodeTypes.SUBQUERY_NODE:
            pushBindingContext(((SubqueryNode)node).getResultSet());
            break;
        case NodeTypes.ORDER_BY_LIST:
        case NodeTypes.GROUP_BY_LIST:
            getBindingContext().resultColumnsAvailableContext = node;
            break;
        default:
            if (havingClauses.contains(node)) // No special node type.
                getBindingContext().resultColumnsAvailableContext = node;
            break;
        }

        return first;
    }

    public void visitAfter(QueryTreeNode node) {
        switch (node.getNodeType()) {
        case NodeTypes.CURSOR_NODE:
        case NodeTypes.FROM_SUBQUERY:
        case NodeTypes.SUBQUERY_NODE:
        case NodeTypes.DELETE_NODE:
        case NodeTypes.INSERT_NODE:
        case NodeTypes.UPDATE_NODE:
            popBindingContext();
            break;
        case NodeTypes.ORDER_BY_LIST:
        case NodeTypes.GROUP_BY_LIST:
            getBindingContext().resultColumnsAvailableContext = null;
            break;
        default:
            if (havingClauses.contains(node))
                getBindingContext().resultColumnsAvailableContext = null;
            break;
        }
    }

    /* Specific node types */

    protected void subqueryNode(SubqueryNode subqueryNode) {
        // The LHS of a subquery operator is bound in the outer context.
        if (subqueryNode.getLeftOperand() != null) {
            try {
                subqueryNode.getLeftOperand().accept(this);
            }
            catch (StandardException ex) {
                throw new SQLParserInternalException(ex);
            }
        }

        ResultSetNode resultSet = subqueryNode.getResultSet();
        ResultColumnList resultColumns = resultSet.getResultColumns();
        // The parser does not enforce the fact that a subquery can only
        // return a single column, so we must check here.
        if (resultColumns.size() != 1) {
            switch (subqueryNode.getSubqueryType()) {
            case IN:
            case NOT_IN:
            case EXISTS:
            case NOT_EXISTS:
                break;
            case EXPRESSION:
                if (allowSubqueryMultipleColumns)
                    break;
                /* else falls through */
            default:
                throw new SubqueryOneColumnException();
            }
        }

        SubqueryNode.SubqueryType subqueryType = subqueryNode.getSubqueryType();
        /* Verify the usage of "*" in the select list:
         *  o    Only valid in EXISTS subqueries
         *  o    If the AllResultColumn is qualified, then we have to verify
         *       that the qualification is a valid exposed name.
         *       NOTE: The exposed name can come from an outer query block.
         */
        verifySelectStarSubquery(resultSet, subqueryType);

        /* For an EXISTS subquery:
         *  o    If the SELECT list is a "*", then we convert it to a true.
         *       (We need to do the conversion since we don't want the "*" to
         *       get expanded.)
         */
        if (subqueryType == SubqueryNode.SubqueryType.EXISTS) {
            try {
                resultSet = setResultToBooleanTrueNode(resultSet);
            }
            catch (StandardException ex) {
                throw new SQLParserInternalException(ex);
            }
            subqueryNode.setResultSet(resultSet);
        }
    }

    protected void verifySelectStarSubquery(ResultSetNode resultSet,
                                            SubqueryNode.SubqueryType subqueryType) {
        if (resultSet instanceof SetOperatorNode) {
            SetOperatorNode setOperatorNode = (SetOperatorNode)resultSet;
            verifySelectStarSubquery(setOperatorNode.getLeftResultSet(), subqueryType);
            verifySelectStarSubquery(setOperatorNode.getRightResultSet(), subqueryType);
            return;
        }
        if (!(resultSet.getResultColumns().get(0) instanceof AllResultColumn)) {
            return;
        }
        // Select * currently only valid for EXISTS/NOT EXISTS.
        if ((subqueryType != SubqueryNode.SubqueryType.EXISTS) &&
            (!allowSubqueryMultipleColumns ||
             (subqueryType != SubqueryNode.SubqueryType.EXPRESSION))) {
            throw new SelectExistsErrorException ();
        }
    }

    /**
     * Set the result column for the subquery to a boolean true,
     * Useful for transformations such as
     * changing:
     *      where exists (select ... from ...)
     * to:
     *      where (select true from ...)
     *
     * NOTE: No transformation is performed if the ResultColumn.expression is
     * already the correct boolean constant.
     *
     * This method is used during binding of EXISTS predicates to map
     * a subquery's result column list into a single TRUE node.  For
     * SELECT and VALUES subqueries this transformation is pretty
     * straightforward.  But for set operators (ex. INTERSECT) we have
     * to do some extra work.    To see why, assume we have the following
     * query:
     *
     *  select * from ( values 'BAD' ) as T
     *      where exists ((values 1) intersect (values 2))
     *
     * If we treated the INTERSECT in this query the same way that we
     * treat SELECT/VALUES subqueries then the above query would get
     * transformed into:
     *
     *  select * from ( values 'BAD' ) as T
     *      where exists ((values TRUE) intersect (values TRUE))
     *
     * Since both children of the INTERSECT would then have the same value,
     * the result of set operation would be a single value (TRUE), which
     * means the WHERE clause would evaluate to TRUE and thus the query
     * would return one row with value 'BAD'.    That would be wrong.
     *
     * To avoid this problem, we internally wrap this SetOperatorNode
     * inside a "SELECT *" subquery and then we change the new SelectNode's
     * result column list (as opposed to *this* nodes' result column list)
     * to a singe boolean true node:
     *
     *  select * from ( values 'BAD' ) as T where exists
     *          SELECT TRUE FROM ((values 1) intersect (values 2))
     *
     * In this case the left and right children of the INTERSECT retain
     * their values, which ensures that the result of the intersect
     * operation will be correct.    Since (1 intersect 2) is an empty
     * result set, the internally generated SELECT node will return
     * zero rows, which in turn means the WHERE predicate will return
     * NULL (an empty result set from a SubqueryNode is treated as NULL
     * at execution time; see impl/sql/execute/AnyResultSet). Since
     * NULL is not the same as TRUE the query will correctly return
     * zero rows.    DERBY-2370.
     *
     * @exception StandardException Thrown on error
     */
    public ResultSetNode setResultToBooleanTrueNode(ResultSetNode resultSet) throws StandardException {
        NodeFactory nodeFactory = resultSet.getNodeFactory();
        SQLParserContext parserContext = resultSet.getParserContext();
        if (resultSet instanceof SetOperatorNode) {
            // First create a FromList to hold this node (and only this node).
            FromList fromList = (FromList)nodeFactory.getNode(NodeTypes.FROM_LIST,
                                                              parserContext);
            fromList.addFromTable((SetOperatorNode)resultSet);

            // Now create a ResultColumnList that simply holds the "*".
            ResultColumnList rcl = (ResultColumnList)
                nodeFactory.getNode(NodeTypes.RESULT_COLUMN_LIST,
                                    parserContext);
            ResultColumn allResultColumn = (ResultColumn)
                nodeFactory.getNode(NodeTypes.ALL_RESULT_COLUMN,
                                    null,
                                    parserContext);
            rcl.addResultColumn(allResultColumn);

            /* Create a new SELECT node of the form:
             *  SELECT * FROM <thisSetOperatorNode>
             */
            resultSet = (ResultSetNode)
                nodeFactory.getNode(NodeTypes.SELECT_NODE,
                                    rcl,            // ResultColumns
                                    null,           // AGGREGATE list
                                    fromList, // FROM list
                                    null,           // WHERE clause
                                    null,           // GROUP BY list
                                    null,           // having clause
                                    null, /* window list */
                                    parserContext);

            /* And finally, transform the "*" in the new SELECT node
             * into a TRUE constant node.    This ultimately gives us:
             *
             *  SELECT TRUE FROM <thisSetOperatorNode>
             *
             * which has a single result column that is a boolean TRUE
             * constant.    So we're done.
             */
        }

        ResultColumnList resultColumns = resultSet.getResultColumns();
        ResultColumn resultColumn = resultColumns.get(0);
        if (resultColumns.get(0) instanceof AllResultColumn) {
            resultColumn = (ResultColumn)nodeFactory.getNode(NodeTypes.RESULT_COLUMN,
                                                             "",
                                                             null,
                                                             parserContext);
        }
        else if (resultColumn.getExpression().isBooleanTrue()) {
            // Nothing to do if query is already select TRUE ...
            return resultSet;
        }

        BooleanConstantNode booleanNode = (BooleanConstantNode)
            nodeFactory.getNode(NodeTypes.BOOLEAN_CONSTANT_NODE,
                                Boolean.TRUE,
                                parserContext);
        resultColumn.setExpression(booleanNode);
        resultColumn.setType(booleanNode.getType());
        resultColumns.set(0, resultColumn);
        return resultSet;
    }

    protected void selectNode(SelectNode selectNode) {
        FromList fromList = selectNode.getFromList();
        int size = fromList.size();
        for (int i = 0; i < size; i++) {
            FromTable fromTable = fromList.get(i);
            FromTable newFromTable = fromTable(fromTable, false);
            if (newFromTable != fromTable)
                fromList.set(i, newFromTable);
        }
        for (int i = 0; i < size; i++) {
            addFromTable(fromList.get(i));
        }
        expandAllsAndNameColumns(selectNode.getResultColumns(), fromList);
        if (selectNode.getHavingClause() != null)
            havingClauses.add(selectNode.getHavingClause());
    }

    // Process a FROM list table, finding the table binding.
    protected FromTable fromTable(FromTable fromTable, boolean nullable) {
        switch (fromTable.getNodeType()) {
        case NodeTypes.FROM_BASE_TABLE:
            return fromBaseTable((FromBaseTable)fromTable, nullable);
        case NodeTypes.JOIN_NODE:
            return joinNode((JoinNode)fromTable, nullable);
        case NodeTypes.HALF_OUTER_JOIN_NODE:
            return joinNode((HalfOuterJoinNode)fromTable, nullable);
        case NodeTypes.FULL_OUTER_JOIN_NODE:
            throw new UnsupportedFullOuterJoinException();
        case NodeTypes.FROM_SUBQUERY:
            return fromSubquery((FromSubquery)fromTable);
        default:
            // Subqueries in SELECT don't see earlier FROM list tables.
            try {
                return (FromTable)fromTable.accept(this);
            } catch (StandardException e) {
                throw new TableIsBadSubqueryException (fromTable.getOrigTableName().getSchemaName(), fromTable.getOrigTableName().getTableName(), e.getMessage());
            }
        }
    }

    protected FromTable fromBaseTable(FromBaseTable fromBaseTable, boolean nullable)  {
        TableName origName = fromBaseTable.getOrigTableName();
        String schemaName = origName.getSchemaName();
        if (schemaName == null)
            schemaName = defaultSchemaName;
        String tableName = origName.getTableName();
        Columnar table = null;
        View view = ais.getView(schemaName, tableName);
        if (view != null) {
            if (!context.isAccessible(view.getName())) {
                view = null;
            }
            else if (expandViews) {
                ViewDefinition viewdef = context.getViewDefinition(view);
                FromSubquery viewSubquery;
                try {
                    viewSubquery = viewdef.copySubquery(fromBaseTable.getParserContext());
                }
                catch (StandardException ex) {
                    throw new ViewHasBadSubqueryException(origName.toString(),
                                                          ex.getMessage());
                }
                if (fromBaseTable.getCorrelationName() != null)
                    tableName = fromBaseTable.getCorrelationName();
                viewSubquery.setCorrelationName(tableName);
                return fromTable(viewSubquery, false);
            }
            else {
                table = view;   // Shallow reference within another view definition.
            }
        }
        if (table == null)
            table = lookupTableName(origName, schemaName, tableName);
        origName.setUserData(table);
        fromBaseTable.setUserData(new TableBinding(table, nullable));
        return fromBaseTable;
    }
   
    protected FromTable joinNode(JoinNode joinNode, boolean nullable) {
        joinNode.setLeftResultSet(fromTable((FromTable)joinNode.getLeftResultSet(),
                                            nullable));
        joinNode.setRightResultSet(fromTable((FromTable)joinNode.getRightResultSet(),
                                             nullable));
        return joinNode;
    }

    protected FromTable joinNode(HalfOuterJoinNode joinNode, boolean nullable) {
        joinNode.setLeftResultSet(fromTable((FromTable)joinNode.getLeftResultSet(),
                                            nullable || joinNode.isRightOuterJoin()));
        joinNode.setRightResultSet(fromTable((FromTable)joinNode.getRightResultSet(),
                                             nullable || !joinNode.isRightOuterJoin()));
        return joinNode;
    }

    protected FromSubquery fromSubquery(FromSubquery fromSubquery) {
        // Do the subquery body first to get types, etc.
        try {
            fromSubquery.accept(this);

            if (fromSubquery.getResultColumns() == null) {
                ResultSetNode inner = fromSubquery.getSubquery();
                ResultColumnList innerRCL = inner.getResultColumns();
                if (innerRCL != null) {
                    NodeFactory nodeFactory = fromSubquery.getNodeFactory();
                    SQLParserContext parserContext = fromSubquery.getParserContext();
                    ResultColumnList outerRCL = (ResultColumnList)
                        nodeFactory.getNode(NodeTypes.RESULT_COLUMN_LIST, parserContext);
                    int ncols = 0;
                    for (ResultColumn innerColumn : innerRCL) {
                        guaranteeColumnName(innerColumn);
                        ValueNode valueNode = (ValueNode)
                            nodeFactory.getNode(NodeTypes.VIRTUAL_COLUMN_NODE,
                                                inner,
                                                innerColumn,
                                                ++ncols,
                                                parserContext);
                        ResultColumn outerColumn = (ResultColumn)
                            nodeFactory.getNode(NodeTypes.RESULT_COLUMN,
                                                innerColumn.getName(),
                                                valueNode,
                                                parserContext);
                        outerRCL.addResultColumn(outerColumn);
                        //valueNode.setUserData(new ColumnBinding(inner, innerColumn));
                    }
                    fromSubquery.setResultColumns(outerRCL);
                }
            }
        }
        catch (StandardException e) {
            throw new TableIsBadSubqueryException("", fromSubquery.getExposedName(), e.getMessage());
        }

        return fromSubquery;
    }

    protected void addFromTable(FromTable fromTable) {
        if (fromTable instanceof JoinNode) {
            try {
                addJoinNode((JoinNode)fromTable);
            } catch (StandardException e) {
                throw new JoinNodeAdditionException (fromTable.getOrigTableName().getSchemaName(), fromTable.getOrigTableName().getTableName(), e.getMessage());
            }
            return;
        }
        BindingContext bindingContext = getBindingContext();
        bindingContext.tables.add(fromTable);
        if (fromTable.getCorrelationName() != null) {
            FromTable prevTable =
                bindingContext.correlationNames.put(fromTable.getCorrelationName(),
                                                    fromTable);
            if ((prevTable != null) && bindingContext.tables.contains(prevTable)) {
                // Other occurrence in this same context.
                throw new CorrelationNameAlreadyUsedException(fromTable.getCorrelationName());
            }
        }
    }

    protected void addJoinNode(JoinNode joinNode) throws StandardException {
        FromTable fromLeft = (FromTable)joinNode.getLeftResultSet();
        FromTable fromRight = (FromTable)joinNode.getRightResultSet();
        if (joinNode.getNodeType() == NodeTypes.FULL_OUTER_JOIN_NODE)
        {
            throw new UnsupportedFullOuterJoinException();
        }
        addFromTable(fromLeft);
        addFromTable(fromRight);
        if ((joinNode.getUsingClause() != null) || joinNode.isNaturalJoin()) {
            // Replace USING clause with equivalent equality predicates, all bound up.
            NodeFactory nodeFactory = joinNode.getNodeFactory();
            SQLParserContext parserContext = joinNode.getParserContext();
            ValueNode conditions = null;
            if (joinNode.getUsingClause() != null) {
                for (ResultColumn rc : joinNode.getUsingClause()) {
                    String columnName = rc.getName();
                    ColumnBinding leftBinding = getColumnBinding(fromLeft, columnName);
                    if (leftBinding == null)
                        throw new NoSuchColumnException(columnName, rc);
                    ColumnBinding rightBinding = getColumnBinding(fromRight, columnName);
                    if (rightBinding == null)
                        throw new NoSuchColumnException(columnName, rc);
                    conditions = addJoinEquality(conditions,
                                                 columnName, leftBinding, rightBinding,
                                                 nodeFactory, parserContext);
                    BindingContext bindingContext = getBindingContext();
                    // USING is tricky case, since join columns do not repeat in expansion.
                    // (see 7.5 of sql1992 spec)
                    if (joinNode.getNodeType() == NodeTypes.FULL_OUTER_JOIN_NODE)
                    {
                        throw new UnsupportedFullOuterJoinException();
                    } else if (joinNode.getNodeType() == NodeTypes.HALF_OUTER_JOIN_NODE &&
                            ((HalfOuterJoinNode)joinNode).isRightOuterJoin())
                    {
                        // We use the value from the right table on a RIGHT OUTER JOIN
                        // so ignore the column in the left table
                        bindingContext.applyUsingColumn(fromLeft, columnName);
                    } else {
                        // We use the value from the left table on an INNER JOIN or
                        // LEFT OUTER JOIN so ignore the column in the right table
                        bindingContext.applyUsingColumn(fromRight, columnName);
                    }
                }
            }
            else if (joinNode.isNaturalJoin()) {
                Map<String,ColumnBinding> leftCols = new HashMap<>();
                getUniqueColumnBindings(fromLeft, leftCols);
                Map<String,ColumnBinding> rightCols = new HashMap<>();
                getUniqueColumnBindings(fromRight, rightCols);
                for (String columnName : leftCols.keySet()) {
                    ColumnBinding rightBinding = rightCols.get(columnName);
                    if (rightBinding == null) continue;
                    ColumnBinding leftBinding = leftCols.get(columnName);
                    conditions = addJoinEquality(conditions,
                                                 columnName, leftBinding, rightBinding,
                                                 nodeFactory, parserContext);
                }
            }
            if (joinNode.getJoinClause() == null) {
                joinNode.setJoinClause(conditions);
            }
            else {
                joinNode.setJoinClause((AndNode)nodeFactory.getNode(NodeTypes.AND_NODE,
                                                                    joinNode.getJoinClause(),
                                                                    conditions,
                                                                    parserContext));
            }
        }
        // Take care of any remaining column bindings in the ON clause.
        if (joinNode.getJoinClause() != null)
            joinNode.getJoinClause().accept(this);
    }

    protected ValueNode addJoinEquality(ValueNode conditions,
                                        String columnName, ColumnBinding leftBinding, ColumnBinding rightBinding,
                                        NodeFactory nodeFactory, SQLParserContext parserContext)
            throws StandardException {
        ColumnReference leftCR = (ColumnReference)
            nodeFactory.getNode(NodeTypes.COLUMN_REFERENCE,
                                columnName, leftBinding.getFromTable().getTableName(),
                                parserContext);
        ColumnReference rightCR = (ColumnReference)
            nodeFactory.getNode(NodeTypes.COLUMN_REFERENCE,
                                columnName, rightBinding.getFromTable().getTableName(),
                                parserContext);
        ValueNode condition = (ValueNode)
            nodeFactory.getNode(NodeTypes.BINARY_EQUALS_OPERATOR_NODE,
                                leftCR, rightCR,
                                parserContext);
        if (conditions == null) {
            conditions = condition;
        }
        else {
            conditions = (AndNode)nodeFactory.getNode(NodeTypes.AND_NODE,
                                                      conditions, condition,
                                                      parserContext);
        }
        return conditions;
    }

    protected void columnReference(ColumnReference columnReference) {
        ColumnBinding columnBinding = (ColumnBinding)columnReference.getUserData();
        if (columnBinding != null)
            return;

        String columnName = columnReference.getColumnName();
        if (columnReference.getTableNameNode() != null) {
            FromTable fromTable = findFromTable(columnReference.getTableNameNode());
            columnBinding = getColumnBinding(fromTable, columnName);
            if (columnBinding == null)
                throw new NoSuchColumnException(columnName, columnReference);
        }
        else {
            if (resultColumnsAvailable(getBindingContext().resultColumnsAvailableContext,
                                       columnReference)) {
                ResultColumnList resultColumns = getBindingContext().resultColumns;
                if (resultColumns != null) {
                    ResultColumn resultColumn = resultColumns.getResultColumn(columnName);
                    if (resultColumn != null) {
                        if (resultColumn.getExpression() instanceof ColumnReference) {
                            columnBinding = (ColumnBinding)((ColumnReference)resultColumn.getExpression()).getUserData();
                        }
                        if (columnBinding == null)
                            columnBinding = new ColumnBinding(null, resultColumn);
                    }
                }
            }
            if (columnBinding == null) {
                boolean ambiguous = false;
                outer:
                for (BindingContext bindingContext : bindingContexts) {
                    ColumnBinding contextBinding = null;
                    for (FromTable fromTable : bindingContext.tables) {
                        ColumnBinding tableBinding = getColumnBinding(fromTable, columnName);
                        if (tableBinding != null &&
                                !bindingContext.ignoreDueToUsing(fromTable, columnName)) {
                            if (contextBinding != null) {
                                ambiguous = true;
                                break outer;
                            }
                            contextBinding = tableBinding;
                        }
                    }
                    if (contextBinding != null) {
                        columnBinding = contextBinding;
                        break;
                    }
                }
                if (columnBinding == null) {
                    if (ambiguous)
                        throw new AmbiguousColumNameException(columnName, columnReference);
                    else
                        throw new NoSuchColumnException(columnName, columnReference);
                }
            }
        }
        columnReference.setUserData(columnBinding);
    }

    protected boolean resultColumnsAvailable(QueryTreeNode context, ColumnReference columnReference) {
        if (context == null)
            return false;
        if (!resultColumnsAvailableBroadly) {
            // The column ref must be immediately in the order / group by list, not
            // in a sub-expression.
            switch (context.getNodeType()) {
            case NodeTypes.ORDER_BY_LIST:
                for (OrderByColumn column : ((OrderByList)context)) {
                    if (column.getExpression() == columnReference) {
                        return true;
                    }
                }
                break;
            case NodeTypes.GROUP_BY_LIST:
                for (GroupByColumn column : ((GroupByList)context)) {
                    if (column.getColumnExpression() == columnReference) {
                        return true;
                    }
                }
                break;
            }
            return false;
        }
        return true;
    }

    protected Table lookupTableName(TableName origName, String schemaName, String tableName) {
        Table result = ais.getTable(schemaName, tableName);
        if ((result == null) ||
            ((context != null) && !context.isAccessible(result.getName())))
            throw new NoSuchTableException(schemaName, tableName, origName);
        return result;
    }

    protected FromTable findFromTable(TableName tableNameNode){
        String schemaName = tableNameNode.getSchemaName();
        String tableName = tableNameNode.getTableName();
        if (schemaName == null) {
            FromTable fromTable = getBindingContext().correlationNames.get(tableName);
            if (fromTable != null)
                return fromTable;

            schemaName = defaultSchemaName;
        }
        for (BindingContext bindingContext : bindingContexts) {
            for (FromTable fromTable : bindingContext.tables) {
                if ((fromTable instanceof FromBaseTable) &&
                    // Not allowed to reference correlated by underlying name.
                    (fromTable.getCorrelationName() == null)) {
                    FromBaseTable fromBaseTable = (FromBaseTable)fromTable;
                    TableBinding tableBinding = (TableBinding)fromBaseTable.getUserData();
                    assert (tableBinding != null) : "table not bound yet";
                    Columnar table = tableBinding.getTable();
                    if (table.getName().getSchemaName().equalsIgnoreCase(schemaName) &&
                        table.getName().getTableName().equalsIgnoreCase(tableName)) {
                        return fromBaseTable;
                    }
                }
            }
        }
        throw new NoSuchTableException(schemaName, tableName, tableNameNode);
    }

    protected void getUniqueColumnBindings(FromTable fromTable,
                                           Map<String,ColumnBinding>  bindings)
            throws StandardException {
        if (fromTable instanceof FromBaseTable) {
            FromBaseTable fromBaseTable = (FromBaseTable)fromTable;
            TableBinding tableBinding = (TableBinding)fromBaseTable.getUserData();
            assert (tableBinding != null) : "table not bound yet";
            Columnar table = tableBinding.getTable();
            for (Column column : table.getColumns()) {
                ColumnBinding prev = bindings.put(column.getName(),
                                                  new ColumnBinding(fromTable, column,
                                                                    tableBinding.isNullable()));
                if (prev != null)
                    throw new StandardException("Duplicate column name " + column.getName() + " not allowed with NATURAL JOIN");
            }
        }
        else if (fromTable instanceof FromSubquery) {
            FromSubquery fromSubquery = (FromSubquery)fromTable;
            for (ResultColumn resultColumn : fromSubquery.getResultColumns()) {
                ColumnBinding prev = bindings.put(resultColumn.getName(),
                                                  new ColumnBinding(fromTable, resultColumn));
                if (prev != null)
                    throw new StandardException("Duplicate column name " + resultColumn.getName() + " not allowed with NATURAL JOIN");
            }
        }
        else if (fromTable instanceof JoinNode) {
            JoinNode joinNode = (JoinNode)fromTable;
            getUniqueColumnBindings((FromTable)joinNode.getLeftResultSet(), bindings);
            getUniqueColumnBindings((FromTable)joinNode.getRightResultSet(), bindings);
        }
    }

    protected ColumnBinding getColumnBinding(FromTable fromTable, String columnName) {
        if (fromTable instanceof FromBaseTable) {
            FromBaseTable fromBaseTable = (FromBaseTable)fromTable;
            TableBinding tableBinding = (TableBinding)fromBaseTable.getUserData();
            assert (tableBinding != null) : "table not bound yet";
            Columnar table = tableBinding.getTable();
            Column column = table.getColumn(columnName);
            if (column == null)
                return null;
            return new ColumnBinding(fromTable, column, tableBinding.isNullable());
        }
        else if (fromTable instanceof FromSubquery) {
            FromSubquery fromSubquery = (FromSubquery)fromTable;
            ResultColumnList columns = fromSubquery.getResultColumns();
            if (columns == null)
                columns = fromSubquery.getSubquery().getResultColumns();
            ResultColumn resultColumn = columns.getResultColumn(columnName);
            if (resultColumn == null)
                return null;
            return new ColumnBinding(fromTable, resultColumn);
        }
        else if (fromTable instanceof JoinNode) {
            JoinNode joinNode = (JoinNode)fromTable;
            FromTable leftTable = (FromTable) joinNode.getLeftResultSet();
            ColumnBinding leftBinding = getColumnBinding(leftTable, columnName);
            if (leftBinding != null &&
                    !getBindingContext().ignoreDueToUsing(leftTable, columnName)) {
                return leftBinding;
            } else {
                return getColumnBinding((FromTable) joinNode.getRightResultSet(), columnName);
            }
        }
        else {
            assert false;
            return null;
        }
    }

    /**
     * Expand any *'s in the ResultColumnList.  In addition, we will guarantee that
     * each ResultColumn has a name.    (All generated names will be unique across the
     * entire statement.)
     *
     * @exception StandardException                             Thrown on error
     */
    public void expandAllsAndNameColumns(ResultColumnList rcl, FromList fromList) {
        if (rcl == null) return;

        boolean expanded = false;
        ResultColumnList allExpansion;
        TableName fullTableName;

        for (int index = 0; index < rcl.size(); index++) {
            ResultColumn rc = rcl.get(index);
            if (rc instanceof AllResultColumn) {
                AllResultColumn arc = (AllResultColumn)rc;

                expanded = true;

                fullTableName = arc.getTableNameObject();
                boolean recursive = arc.isRecursive();
                if (recursive && !allowSubqueryMultipleColumns) {
                    throw new WholeGroupQueryException();
                }
                allExpansion = expandAll(fullTableName, fromList, recursive);

                // Make sure that every column has a name.
                for (ResultColumn nrc : allExpansion) {
                    guaranteeColumnName(nrc);
                }

                // Replace the AllResultColumn with the expanded list.
                rcl.remove(index);
                for (int inner = 0; inner < allExpansion.size(); inner++) {
                    rcl.add(index + inner, allExpansion.get(inner));
                }
                index += allExpansion.size() - 1;

                // TODO: This is where Derby remembered the original size in
                // case other things get added to the RCL.
            }
            else {
                // Make sure that every column has a name.
                guaranteeColumnName(rc);
            }
        }
    }

    /**
     * Generate a unique (across the entire statement) column name for unnamed
     * ResultColumns
     *
     * @exception StandardException Thrown on error
     */
    protected void guaranteeColumnName(ResultColumn rc) {
        if (rc.getName() == null) {
            rc.setName(((SQLParser)rc.getParserContext()).generateColumnName());
            rc.setNameGenerated(true);
        }
    }

    /**
     * Expand a "*" into the appropriate ResultColumnList. If the "*"
     * is unqualified it will expand into a list of all columns in all
     * of the base tables in the from list at the current nesting level;
     * otherwise it will expand into a list of all of the columns in the
     * base table that matches the qualification.
     *
     * @param allTableName The qualification on the "*" as a String
     * @param fromList The select list
     *
     * @return ResultColumnList representing expansion
     *
     * @exception StandardException Thrown on error
     */
    protected ResultColumnList expandAll(TableName allTableName, FromList fromList, boolean recursive) {
        ResultColumnList resultColumnList = null;
        ResultColumnList tempRCList = null;

        for (FromTable fromTable : fromList) {
            tempRCList = getAllResultColumns(allTableName, fromTable, recursive);

            if (tempRCList == null)
                continue;

            /* Expand the column list and append to the list that
             * we will return.
             */
            if (resultColumnList == null)
                resultColumnList = tempRCList;
            else
                resultColumnList.addAll(tempRCList);

            // If the "*" is qualified, then we can stop the expansion as
            // soon as we find the matching table.
            if (allTableName != null)
                break;
        }

        // Give an error if the qualification name did not match an exposed name.
        if (resultColumnList == null) {
            if (allTableName == null) {
                throw new NoTableSpecifiedInQueryException ();
            } else {
                throw new NoSuchTableException(allTableName.getSchemaName(), allTableName.getTableName(), allTableName);
            }
        }

        return resultColumnList;
    }

    protected ResultColumnList getAllResultColumns(TableName allTableName,
                                                   ResultSetNode fromTable,
                                                   boolean recursive) {
        try {
            switch (fromTable.getNodeType()) {
            case NodeTypes.FROM_BASE_TABLE:
                return getAllResultColumns(allTableName, (FromBaseTable)fromTable,
                                           recursive);
            case NodeTypes.JOIN_NODE:
            case NodeTypes.HALF_OUTER_JOIN_NODE:
                return getAllResultColumns(allTableName, (JoinNode)fromTable, recursive);
            case NodeTypes.FROM_SUBQUERY:
                return getAllResultColumns(allTableName, (FromSubquery)fromTable);
            default:
                return null;
            }
        }
        catch (StandardException ex) {
            throw new SQLParserInternalException(ex);
        }
    }

    protected ResultColumnList getAllResultColumns(TableName allTableName,
                                                   FromBaseTable fromTable,
                                                   boolean recursive)
            throws StandardException {
        TableName exposedName = fromTable.getExposedTableName();
        if ((allTableName != null) && !allTableName.equals(exposedName))
            return null;

        NodeFactory nodeFactory = fromTable.getNodeFactory();
        SQLParserContext parserContext = fromTable.getParserContext();
        ResultColumnList rcList = (ResultColumnList)
            nodeFactory.getNode(NodeTypes.RESULT_COLUMN_LIST,
                                parserContext);
        TableBinding tableBinding = (TableBinding)fromTable.getUserData();
        Columnar table = tableBinding.getTable();
        for (Column column : table.getColumns()) {
            String columnName = column.getName();
            ValueNode valueNode = (ValueNode)
                nodeFactory.getNode(NodeTypes.COLUMN_REFERENCE,
                                    columnName,
                                    exposedName,
                                    parserContext);
            ResultColumn resultColumn = (ResultColumn)
                nodeFactory.getNode(NodeTypes.RESULT_COLUMN,
                                    columnName,
                                    valueNode,
                                    parserContext);
            rcList.addResultColumn(resultColumn);
            // Easy to do binding right here.
            valueNode.setUserData(new ColumnBinding(fromTable, column,
                                                    tableBinding.isNullable()));
        }
        if (recursive && (table instanceof Table)) {
            for (Join child : ((Table)table).getChildJoins()) {
                rcList.addResultColumn(childJoinSubquery(fromTable, child));
            }
        }
        return rcList;
    }

    protected ResultColumnList getAllResultColumns(TableName allTableName,
                                                   JoinNode fromJoin,
                                                   boolean recursive)
            throws StandardException {
        ResultColumnList leftRCL = getAllResultColumns(allTableName,
                                                       fromJoin.getLogicalLeftResultSet(),
                                                       recursive);
        ResultColumnList rightRCL = getAllResultColumns(allTableName,
                                                        fromJoin.getLogicalRightResultSet(),
                                                        recursive);

        if (leftRCL == null)
            return rightRCL;
        else if (rightRCL == null)
            return leftRCL;

        if (fromJoin.getUsingClause() == null) {
            leftRCL.addAll(rightRCL);
            return leftRCL;
        } else {
            ResultColumnList joinRCL;
            // USING is tricky case, since join columns do not repeat in expansion.
            // (see 7.5 of sql1992 spec)
            if (fromJoin.getNodeType() == NodeTypes.FULL_OUTER_JOIN_NODE)
            {
                throw new UnsupportedFullOuterJoinException();
            } else if (fromJoin.getNodeType() == NodeTypes.HALF_OUTER_JOIN_NODE && ((HalfOuterJoinNode)fromJoin).isRightOuterJoin())
            {
                joinRCL = rightRCL.getJoinColumns(fromJoin.getUsingClause());
            } else {
                joinRCL = leftRCL.getJoinColumns(fromJoin.getUsingClause());
            }
            leftRCL.removeJoinColumns(fromJoin.getUsingClause());
            joinRCL.addAll(leftRCL);
            rightRCL.removeJoinColumns(fromJoin.getUsingClause());
            joinRCL.addAll(rightRCL);
            return joinRCL;
        }
    }

    protected ResultColumnList getAllResultColumns(TableName allTableName,
                                                   FromSubquery fromSubquery)
            throws StandardException {
        NodeFactory nodeFactory = fromSubquery.getNodeFactory();
        SQLParserContext parserContext = fromSubquery.getParserContext();
        TableName exposedName = (TableName)
            nodeFactory.getNode(NodeTypes.TABLE_NAME,
                                null,
                                fromSubquery.getExposedName(),
                                parserContext);
        if ((allTableName != null) && !allTableName.equals(exposedName))
            return null;

        ResultColumnList rcList = (ResultColumnList)
            nodeFactory.getNode(NodeTypes.RESULT_COLUMN_LIST,
                                parserContext);
        for (ResultColumn resultColumn : fromSubquery.getResultColumns()) {
            String columnName = resultColumn.getName();
            boolean isNameGenerated = resultColumn.isNameGenerated();

            TableName tableName = exposedName;
            ValueNode valueNode = (ValueNode)
                nodeFactory.getNode(NodeTypes.COLUMN_REFERENCE,
                                    columnName,
                                    tableName,
                                    parserContext);
            resultColumn = (ResultColumn)
                nodeFactory.getNode(NodeTypes.RESULT_COLUMN,
                                    columnName,
                                    valueNode,
                                    parserContext);

            resultColumn.setNameGenerated(isNameGenerated);
            rcList.add(resultColumn);
        }
        return rcList;
    }

    /** Make a nested result set for this child (and so on recursively). */
    protected ResultColumn childJoinSubquery(FromBaseTable parentTable, Join child)
            throws StandardException {
        NodeFactory nodeFactory = parentTable.getNodeFactory();
        SQLParserContext parserContext = parentTable.getParserContext();
        Table childAisTable = child.getChild();
        Object childName = nodeFactory.getNode(NodeTypes.TABLE_NAME,
                                               childAisTable.getName().getSchemaName(),
                                               childAisTable.getName().getTableName(),
                                               parserContext);
        FromBaseTable childTable = (FromBaseTable)
            nodeFactory.getNode(NodeTypes.FROM_BASE_TABLE,
                                childName, childAisTable.getName().getTableName(),
                                null,  null, null, parserContext);
        childTable.setUserData(new TableBinding(childAisTable, false));
        ValueNode whereClause = null;
        for (JoinColumn join : child.getJoinColumns()) {
            ColumnReference parentPK = (ColumnReference)
                nodeFactory.getNode(NodeTypes.COLUMN_REFERENCE,
                                    join.getParent().getName(),
                                    parentTable.getTableName(),
                                    parserContext);
            parentPK.setUserData(new ColumnBinding(parentTable, join.getParent(), false));
            ColumnReference childFK = (ColumnReference)
                nodeFactory.getNode(NodeTypes.COLUMN_REFERENCE,
                                    join.getChild().getName(),
                                    childName,
                                    parserContext);
            childFK.setUserData(new ColumnBinding(childTable, join.getChild(), false));
            ValueNode equals = (ValueNode)
                nodeFactory.getNode(NodeTypes.BINARY_EQUALS_OPERATOR_NODE,
                                    parentPK,
                                    childFK,
                                    parserContext);
            if (whereClause == null) {
                whereClause = equals;
            }
            else {
                whereClause = (ValueNode)
                    nodeFactory.getNode(NodeTypes.AND_NODE,
                                        whereClause, equals,
                                        parserContext);
            }
        }
        FromList fromList = (FromList)
            nodeFactory.getNode(NodeTypes.FROM_LIST,
                                parserContext);
        fromList.addFromTable(childTable);
        ResultColumnList rcl = getAllResultColumns(null, childTable, true);
        SelectNode selectNode = (SelectNode)
            nodeFactory.getNode(NodeTypes.SELECT_NODE,
                                rcl, null, fromList, whereClause, null, null, null,
                                parserContext);
        SubqueryNode subquery = (SubqueryNode)
            nodeFactory.getNode(NodeTypes.SUBQUERY_NODE,
                                selectNode, SubqueryNode.SubqueryType.EXPRESSION,
                                null, null, null, null,
                                parserContext);
        ResultColumn resultColumn = (ResultColumn)
            nodeFactory.getNode(NodeTypes.RESULT_COLUMN,
                                childAisTable.getNameForOutput(),
                                subquery,
                                parserContext);
        return resultColumn;
    }

    protected void dmlModStatementNode(DMLModStatementNode node) {
        TableName tableName = node.getTargetTableName();
        String schemaName = tableName.getSchemaName();
        if (schemaName == null)
            schemaName = defaultSchemaName;
        Table table = lookupTableName(tableName, schemaName, tableName.getTableName());
        tableName.setUserData(table);
       
        ResultColumnList targetColumns = null;
        if (node instanceof InsertNode)
            targetColumns = ((InsertNode)node).getTargetColumnList();
        else
            targetColumns = node.getResultSetNode().getResultColumns();
        if (targetColumns != null) {
            for (ResultColumn targetColumn : targetColumns) {
                ColumnReference columnReference = targetColumn.getReference();
                String columnName = columnReference.getColumnName();
                Column column = table.getColumn(columnName);
                if (column == null)
                    throw new NoSuchColumnException(columnName, columnReference);
                ColumnBinding columnBinding = new ColumnBinding(null, column, false);
                columnReference.setUserData(columnBinding);
            }
        }
        if (node.getReturningList() != null) {
            ResultColumnList rcl = node.getReturningList();
            NodeFactory nodeFactory = node.getNodeFactory();
            SQLParserContext parserContext = node.getParserContext();
            FromBaseTable fromTable = null;
            FromList fromList = null;
            try {
                fromTable = (FromBaseTable)nodeFactory.getNode(NodeTypes.FROM_BASE_TABLE,
                        tableName, null, rcl, null, null, parserContext);
                fromTable.setUserData(new TableBinding(table, false));

                fromList = (FromList)nodeFactory.getNode(NodeTypes.FROM_LIST, Boolean.FALSE, parserContext);
                fromList.add(fromTable);
            } catch (StandardException ex) {
                throw new SQLParserInternalException(ex);
            }
            node.setUserData(fromTable);
           
            for (int index = 0; index < rcl.size(); index ++) {
                ResultColumn rc = rcl.get(index);
                if (rc instanceof AllResultColumn) {
                    AllResultColumn arc = (AllResultColumn)rc;
                    ResultColumnList allExpansion = expandAll(tableName, fromList, false);
                    // Make sure that every column has a name.
                    for (ResultColumn nrc : allExpansion) {
                        guaranteeColumnName(nrc);
                    }
                    // Replace the AllResultColumn with the expanded list.
                    rcl.remove(index);
                    for (int inner = 0; inner < allExpansion.size(); inner++) {
                        rcl.add(index + inner, allExpansion.get(inner));
                    }
                    index += allExpansion.size() - 1;
                } else {
                    // Make sure that every column has a name.
                    guaranteeColumnName(rc);
                }
            }
            pushBindingContext(null);
            getBindingContext().tables.add(fromTable);
            try {
                rcl.accept(this);
            } catch (StandardException ex) {
                throw new SQLParserInternalException(ex);
            }
            popBindingContext();
        }
    }

    protected void unionNode(UnionNode node) {
        pushBindingContext(null);
        try {
            node.getLeftResultSet().accept(this);
            node.setResultColumns(node.copyResultColumnsFromLeft());
        }
        catch (StandardException ex) {
            throw new SQLParserInternalException(ex);
        }
        popBindingContext();
        pushBindingContext(null);
        try {
            node.getRightResultSet().accept(this);
        }
        catch (StandardException ex) {
            throw new SQLParserInternalException(ex);
        }
        popBindingContext();
    }

    protected void intersectOrExceptNode(IntersectOrExceptNode node) {
        pushBindingContext(null);
        try {
            node.getLeftResultSet().accept(this);
            node.setResultColumns(node.copyResultColumnsFromLeft());
        }
        catch (StandardException ex) {
            throw new SQLParserInternalException(ex);
        }
        popBindingContext();
        pushBindingContext(null);
        try {
            node.getRightResultSet().accept(this);
        }
        catch (StandardException ex) {
            throw new SQLParserInternalException(ex);
        }
        popBindingContext();
    }



    protected void javaValueNode(JavaValueNode javaValue) {
        if ((javaValue instanceof StaticMethodCallNode) &&
            (functionDefined != null)) {
            StaticMethodCallNode methodCall = (StaticMethodCallNode)javaValue;
            Routine routine = null;
            if ((methodCall.getProcedureName() != null) &&
                (methodCall.getProcedureName().hasSchema())) {
                // Qualified name is always a routine and an immediate error if not.
                routine = ais.getRoutine(methodCall.getProcedureName().getSchemaName(),
                                         methodCall.getProcedureName().getTableName());
                if ((routine == null) || !context.isAccessible(routine.getName())) {
                    throw new NoSuchFunctionException(methodCall.getProcedureName().toString());
                }
            }
            else if (!functionDefined.isDefined(methodCall.getMethodName())) {
                // Unqualified only if not a built-in function and error deferred.
                routine = ais.getRoutine(defaultSchemaName, methodCall.getMethodName());
                if ((routine != null) && !context.isAccessible(routine.getName())) {
                    routine = null;
                }
            }
            if (routine != null) {
                if (routine.getReturnValue() == null) {
                    throw new ProcedureCalledAsFunctionException(routine.getName());
                }
                methodCall.setUserData(routine);
            }
        }
    }

    protected static class BindingContext {
        Collection<FromTable> tables = new ArrayList<>();
        Map<String,FromTable> correlationNames = new HashMap<>();
        ResultColumnList resultColumns;
        QueryTreeNode resultColumnsAvailableContext;
        private Map<FromTable, Set<String>> ignoredColumnsDueToUsing = new HashMap<>();

        public void applyUsingColumn(FromTable table,  String columnName)
        {
            Set<String> columnNames = ignoredColumnsDueToUsing.get(table);
            if (columnNames == null)
            {
                columnNames = new HashSet<>();
                ignoredColumnsDueToUsing.put(table, columnNames);
            }
            columnNames.add(columnName);
        }

        public boolean ignoreDueToUsing(FromTable table, String columnName) {
            Set<String> columnNames = ignoredColumnsDueToUsing.get(table);
            return columnNames != null && columnNames.contains(columnName);
        }
    }

    protected BindingContext getBindingContext() {
        return bindingContexts.peek();
    }
    protected void pushBindingContext(ResultSetNode resultSet) {
        BindingContext next = new BindingContext();
        if (!bindingContexts.isEmpty()) {
            // Initially inherit all the same correlation names (but can overwrite).
            next.correlationNames.putAll(bindingContexts.peek().correlationNames);
        }
        if (resultSet != null) {
            next.resultColumns = resultSet.getResultColumns();
        }
        bindingContexts.push(next);
    }
    protected void popBindingContext() {
        bindingContexts.pop();
    }

    /* Visitor interface.
       This is messy. Perhaps there should be an abstract class which makes the common
       Visitor interface into a Hierarchical Vistor pattern.
    */

    // To understand why this works, see QueryTreeNode.accept().
    public Visitable visit(Visitable node) {
        visitAfter((QueryTreeNode)node);
        return node;
    }

    public boolean skipChildren(Visitable node) {
        return ! visitBefore((QueryTreeNode)node);
    }

    public boolean visitChildrenFirst(Visitable node) {
        return true;
    }
    public boolean stopTraversal() {
        return false;
    }

}
TOP

Related Classes of com.foundationdb.sql.optimizer.AISBinder$FunctionDefined

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.