com.splicemachine.db.impl.sql.compile.DMLModStatementNode.java Source code

Java tutorial

Introduction

Here is the source code for com.splicemachine.db.impl.sql.compile.DMLModStatementNode.java

Source

/*
 * Apache Derby is a subproject of the Apache DB project, and is licensed under
 * the Apache License, Version 2.0 (the "License"); you may not use these files
 * except in compliance with the License. You may obtain a copy of the License at:
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed
 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 *
 * Splice Machine, Inc. has modified this file.
 *
 * All Splice Machine modifications are Copyright 2012 - 2016 Splice Machine, Inc.,
 * and are licensed to you under the License; you may not use this file except in
 * compliance with the License.
 *
 * Unless required by applicable law or agreed to in writing, software distributed
 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 *
 */

package com.splicemachine.db.impl.sql.compile;

import java.util.Hashtable;
import java.util.Iterator;
import java.util.Vector;

import com.splicemachine.db.catalog.DefaultInfo;
import com.splicemachine.db.iapi.error.StandardException;
import com.splicemachine.db.iapi.reference.ClassName;
import com.splicemachine.db.iapi.reference.SQLState;
import com.splicemachine.db.iapi.services.classfile.VMOpcode;
import com.splicemachine.db.iapi.services.compiler.MethodBuilder;
import com.splicemachine.db.iapi.services.context.ContextManager;
import com.splicemachine.db.iapi.services.io.FormatableBitSet;
import com.splicemachine.db.iapi.services.sanity.SanityManager;
import com.splicemachine.db.iapi.sql.compile.C_NodeTypes;
import com.splicemachine.db.iapi.sql.compile.CompilerContext;
import com.splicemachine.db.iapi.sql.compile.NodeFactory;
import com.splicemachine.db.iapi.sql.compile.Parser;
import com.splicemachine.db.iapi.sql.compile.Visitable;
import com.splicemachine.db.iapi.sql.compile.Visitor;
import com.splicemachine.db.iapi.sql.conn.Authorizer;
import com.splicemachine.db.iapi.sql.conn.LanguageConnectionContext;
import com.splicemachine.db.iapi.sql.depend.Dependent;
import com.splicemachine.db.iapi.sql.dictionary.*;
import com.splicemachine.db.iapi.types.DataTypeDescriptor;
import com.splicemachine.db.iapi.store.access.TransactionController;
import com.splicemachine.db.impl.sql.execute.FKInfo;
import com.splicemachine.db.impl.sql.execute.TriggerInfo;
import org.apache.commons.lang3.ArrayUtils;

/**
 * A DMLStatement for a table modification: to wit, INSERT
 * UPDATE or DELETE.
 *
 */

abstract class DMLModStatementNode extends DMLStatementNode {
    //   protected DataDictionary   dataDictionary;
    protected FromVTI targetVTI;
    protected TableName targetTableName;
    protected ResultColumnList resultColumnList;
    protected int lockMode; // lock mode for the target table

    protected FKInfo[] fkInfo; // array of FKInfo structures
    // generated during bind
    protected TriggerInfo triggerInfo; // generated during bind
    public TableDescriptor targetTableDescriptor;

    /* The indexes that could be affected by this statement */
    public IndexRowGenerator[] indicesToMaintain;
    public long[] indexConglomerateNumbers;
    public String[] indexNames;
    protected ConstraintDescriptorList relevantCdl;
    protected GenericDescriptorList relevantTriggers;

    // PRIVATE
    private boolean requiresDeferredProcessing;
    private int statementType;
    private boolean bound;
    private ValueNode checkConstraints;

    /* Info required to perform referential actions */
    protected String[] fkTableNames; // referencing table names.
    protected int[] fkRefActions; //type of referential actions 
    protected ColumnDescriptorList[] fkColDescriptors;
    protected long[] fkIndexConglomNumbers; //conglomerate number of the backing index
    protected boolean isDependentTable;
    protected int[][] fkColArrays;
    protected Hashtable graphHashTable;
    // Hash Table which maitains the querytreenode graph 
    protected TableName synonymTableName;
    /* Primary Key Column numbers */
    protected int[] pkColumns;

    /**
    * Initializer for a DMLModStatementNode -- delegate to DMLStatementNode
    *
    * @param resultSet   A ResultSetNode for the result set of the
    *         DML statement
    */
    public void init(Object resultSet) {
        super.init(resultSet);
        statementType = getStatementType();
    }

    /**
     * Initializer for a DMLModStatementNode -- delegate to DMLStatementNode
     *
     * @param resultSet   A ResultSetNode for the result set of the
     *         DML statement
     * @param statementType used by nodes that allocate a DMLMod directly
     *         (rather than inheriting it).
     */
    public void init(Object resultSet, Object statementType) {
        super.init(resultSet);
        this.statementType = ((Integer) statementType).intValue();
    }

    void setTarget(QueryTreeNode targetName) {
        if (targetName instanceof TableName) {
            this.targetTableName = (TableName) targetName;
        } else {
            if (SanityManager.DEBUG) {
                if (!(targetName instanceof FromVTI)) {
                    SanityManager.THROWASSERT(
                            "targetName expected to be FromVTI, not " + targetName.getClass().getName());
                }
            }
            this.targetVTI = (FromVTI) targetName;
            targetVTI.setTarget();
        }
    }

    /**
     * If the DML is on a temporary table, generate the code to mark temporary table as modified in the current UOW.
     * At rollback transaction (or savepoint), we will check if the temporary table was modified in that UOW.
     * If yes, we will remove all the data from the temporary table
     *
     * @param acb   The ActivationClassBuilder for the class being built
     * @param mb   The execute() method to be built
     *
     * @exception StandardException      Thrown on error
     */
    protected void generateCodeForTemporaryTable(ActivationClassBuilder acb, MethodBuilder mb)
            throws StandardException {
        if (targetTableDescriptor != null
                && targetTableDescriptor.getTableType() == TableDescriptor.GLOBAL_TEMPORARY_TABLE_TYPE
                && targetTableDescriptor.isOnRollbackDeleteRows() == true) {
            mb.pushThis();
            mb.callMethod(VMOpcode.INVOKEINTERFACE, ClassName.Activation, "getLanguageConnectionContext",
                    ClassName.LanguageConnectionContext, 0);
            mb.push(targetTableDescriptor.getName());
            mb.callMethod(VMOpcode.INVOKEINTERFACE, null, "markTempTableAsModifiedInUnitOfWork", "void", 1);
            mb.endStatement();
        }
    }

    /**
     * Verify the target table.  Get the TableDescriptor
     * if the target table is not a VTI.
     *
     * @exception StandardException      Thrown on error
     */
    void verifyTargetTable() throws StandardException {
        DataDictionary dataDictionary = getDataDictionary();
        if (targetTableName != null) {
            /*
            ** Get the TableDescriptor for the table we are inserting into
            */
            SchemaDescriptor sdtc = getSchemaDescriptor(targetTableName.getSchemaName());

            targetTableDescriptor = getTableDescriptor(targetTableName.getTableName(), sdtc);

            if (targetTableDescriptor == null) {
                // Check if the reference is for a synonym.
                TableName synonymTab = resolveTableToSynonym(targetTableName);
                if (synonymTab == null)
                    throw StandardException.newException(SQLState.LANG_TABLE_NOT_FOUND, targetTableName.toString());
                synonymTableName = targetTableName;
                targetTableName = synonymTab;
                sdtc = getSchemaDescriptor(targetTableName.getSchemaName());

                targetTableDescriptor = getTableDescriptor(synonymTab.getTableName(), sdtc);
                if (targetTableDescriptor == null)
                    throw StandardException.newException(SQLState.LANG_TABLE_NOT_FOUND, targetTableName.toString());
            }

            switch (targetTableDescriptor.getTableType()) {
            case TableDescriptor.VIEW_TYPE:
                // Views are currently not updatable
                throw StandardException.newException(SQLState.LANG_VIEW_NOT_UPDATEABLE, targetTableName);

            case TableDescriptor.VTI_TYPE:
                // fall through - currently all vti tables are system tables.
            case TableDescriptor.SYSTEM_TABLE_TYPE:
                // System tables are not updatable
                throw StandardException.newException(SQLState.LANG_UPDATE_SYSTEM_TABLE_ATTEMPTED, targetTableName);
            default:
                break;
            }
            getCompilerContext().createDependency(targetTableDescriptor);
        } else {
            /* VTI - VTIs in DML Mod are version 2 VTIs - They
             * must implement java.sql.PreparedStatement and have
             * the JDBC2.0 getMetaData() and getResultSetConcurrency()
             * methods and return an updatable ResultSet.
             */
            FromList dummyFromList = new FromList();
            targetVTI = (FromVTI) targetVTI.bindNonVTITables(dataDictionary, dummyFromList);
            targetVTI = (FromVTI) targetVTI.bindVTITables(dummyFromList);
        }
    }

    /**
    * Get a schema descriptor for the given table.
    * Uses this.targetTableName.
    *
    * @return Schema Descriptor
    *
    * @exception   StandardException   throws on schema name
    *                  that doesn't exist   
    */
    public SchemaDescriptor getSchemaDescriptor() throws StandardException {
        SchemaDescriptor sd;

        sd = getSchemaDescriptor(targetTableName.getSchemaName());

        return sd;
    }

    /**
      Get a map to efficiently find heap columns from a compressed set of
      read columns. The returns a map such that
        
      <PRE>
      map[heapColId (0 based)] -> readCol id (0 based)
      </PRE>
        
      @param column_map_length The number of columns(ints) in the map.
      @param readColsBitSet A language style (1 based) bit set with bits for
      read heap columns set.
        
      RESOLVE: Replace this with a call to RowUtil when the store and
      the language both use 0 base or 1 base offsets for columns. Today
      we can't use the store function because we have a 1 based FormatableBitSet.
      */
    public static int[] getReadColMap(int column_map_length, FormatableBitSet readColsBitSet) {
        if (readColsBitSet == null)
            return null;

        int partial_col_cnt = 0;
        int column_map[] = new int[column_map_length];
        int readColsBitSetSize = readColsBitSet.size();

        for (int base_index = 0; base_index < column_map.length; base_index++) {
            if (readColsBitSetSize > base_index && readColsBitSet.get(base_index + 1))
                column_map[base_index] = partial_col_cnt++;
            else
                // this column map offset entry should never be referenced.
                column_map[base_index] = -1;
        }

        return (column_map);
    }

    /**
     * Get and bind the ResultColumnList representing the columns in the
     * target table, given the table's name.
     *
     * @exception StandardException      Thrown on error
     */
    protected void getResultColumnList() throws StandardException {
        if (targetVTI == null) {
            getResultColumnList((ResultColumnList) null);
        } else {
            /* binding VTI - just point to VTI's RCL,
             * which was already bound.
             */
            resultColumnList = targetVTI.getResultColumns();
        }
    }

    /**
     * Get and bind the ResultColumnList representing the columns in the
     * target table, given the table's name.
     *
     * @exception StandardException      Thrown on error
     */
    protected FromBaseTable getResultColumnList(ResultColumnList inputRcl) throws StandardException {
        /* Get a ResultColumnList representing all the columns in the target */
        FromBaseTable fbt = (FromBaseTable) (getNodeFactory().getNode(C_NodeTypes.FROM_BASE_TABLE,
                synonymTableName != null ? synonymTableName : targetTableName, null, null, null,
                getContextManager()));

        fbt.bindNonVTITables(getDataDictionary(), (FromList) getNodeFactory().getNode(C_NodeTypes.FROM_LIST,
                getNodeFactory().doJoinOrderOptimization(), getContextManager()));

        getResultColumnList(fbt, inputRcl);
        return fbt;
    }

    /**
     * Get and bind the ResultColumnList representing the columns in the
     * target table, given a FromTable for the target table.
     *
     * @exception StandardException      Thrown on error
     */
    private void getResultColumnList(FromBaseTable fromBaseTable, ResultColumnList inputRcl)
            throws StandardException {
        if (inputRcl == null) {
            resultColumnList = fromBaseTable.getAllResultColumns(null);
            resultColumnList.bindResultColumnsByPosition(targetTableDescriptor);
        } else {
            resultColumnList = fromBaseTable.getResultColumnsForList(null, inputRcl,
                    fromBaseTable.getTableNameField());

            resultColumnList.bindResultColumnsByName(targetTableDescriptor, (DMLStatementNode) this);
        }
    }

    /**
     * Parse and bind the generating expressions of computed columns.
     *
     * @param dataDictionary    metadata
     * @param targetTableDescriptor metadata for the table that has the generated columns
     * @param sourceRCL  the tuple stream which drives the INSERT or UPDATE
     * @param targetRCL  the row in the table that's being INSERTed or UPDATEd
     * @param forUpdate true if this is an UPDATE. false otherwise.
     * @param updateResultSet more information on the tuple stream driving the UPDATE
     */
    void parseAndBindGenerationClauses(DataDictionary dataDictionary, TableDescriptor targetTableDescriptor,
            ResultColumnList sourceRCL, ResultColumnList targetRCL, boolean forUpdate,
            ResultSetNode updateResultSet) throws StandardException {
        CompilerContext compilerContext = getCompilerContext();
        int count = targetRCL.size();

        for (int i = 0; i < count; i++) {
            ResultColumn rc = (ResultColumn) targetRCL.elementAt(i);

            //
            // For updates, there are two copies of the column in the row: a
            // before image and the actual value which will be set when we
            // update the row. We only want to compile a generation clause for
            // the value which will be updated.
            //
            if (forUpdate && !rc.updated()) {
                continue;
            }

            if (rc.hasGenerationClause()) {
                ColumnDescriptor colDesc = rc.getTableColumnDescriptor();
                DataTypeDescriptor dtd = colDesc.getType();
                DefaultInfo di = colDesc.getDefaultInfo();
                ValueNode generationClause = parseGenerationClause(di.getDefaultText(), targetTableDescriptor);

                // insert CAST in case column data type is not same as the
                // resolved type of the generation clause
                generationClause = (ValueNode) getNodeFactory().getNode(C_NodeTypes.CAST_NODE, generationClause,
                        dtd, getContextManager());

                // Assignment semantics of implicit cast here:
                // Section 9.2 (Store assignment). There, General Rule 
                // 2.b.v.2 says that the database should raise an exception
                // if truncation occurs when stuffing a string value into a
                // VARCHAR, so make sure CAST doesn't issue warning only.
                ((CastNode) generationClause).setAssignmentSemantics();

                //
                // Unqualified function references should resolve to the
                // current schema at the time that the table was
                // created/altered. See DERBY-3945.
                //
                SchemaDescriptor originalCurrentSchema = getSchemaDescriptor(di.getOriginalCurrentSchema(), true);
                compilerContext.pushCompilationSchema(originalCurrentSchema);

                try {
                    bindRowScopedExpression(getNodeFactory(), getContextManager(), targetTableDescriptor, sourceRCL,
                            generationClause);
                } finally {
                    compilerContext.popCompilationSchema();
                }

                ResultColumn newRC = (ResultColumn) getNodeFactory().getNode(C_NodeTypes.RESULT_COLUMN,
                        generationClause.getTypeServices(), generationClause, getContextManager());

                // replace the result column in place
                newRC.setVirtualColumnId(i + 1); // column ids are 1-based
                newRC.setColumnDescriptor(targetTableDescriptor, colDesc);
                targetRCL.setElementAt(newRC, i);

                // if this is an update, then the result column may appear in the
                // source list as well. replace it there too and perform a
                // little extra binding so that check constraints will bind and
                // generate correctly if they reference the generated column
                if (forUpdate) {
                    for (int j = 0; j < sourceRCL.size(); j++) {
                        if (rc == sourceRCL.elementAt(j)) {
                            newRC.setName(rc.getName());
                            newRC.setResultSetNumber(updateResultSet.getResultSetNumber());
                            sourceRCL.setElementAt(newRC, j);

                        }
                    } // end of loop through sourceRCL
                } // end if this is an update statement
            } // end if this is a generated column

        } // end of loop through targetRCL
    }

    /**
     *   Parse the generation clause for a column.
     *
     *   @param   clauseText  Text of the generation clause
     *
     * @return   The parsed expression as a query tree.
     *
     * @exception StandardException      Thrown on failure
     */
    public ValueNode parseGenerationClause(String clauseText, TableDescriptor td) throws StandardException {
        Parser p;
        ValueNode clauseTree;
        LanguageConnectionContext lcc = getLanguageConnectionContext();
        CompilerContext compilerContext = getCompilerContext();

        /* Get a Statement to pass to the parser */

        /* We're all set up to parse. We have to build a compilable SQL statement
         * before we can parse -  So, we goober up a VALUES defaultText.
         */
        String select = "SELECT " + clauseText + " FROM " + td.getQualifiedName();

        /*
        ** Get a new compiler context, so the parsing of the select statement
        ** doesn't mess up anything in the current context (it could clobber
        ** the ParameterValueSet, for example).
        */
        CompilerContext newCC = lcc.pushCompilerContext();

        p = newCC.getParser();

        /* Finally, we can call the parser */
        // Since this is always nested inside another SQL statement, so topLevel flag
        // should be false
        Visitable qt = p.parseStatement(select);
        if (SanityManager.DEBUG) {
            if (!(qt instanceof CursorNode)) {
                SanityManager
                        .THROWASSERT("qt expected to be instanceof CursorNode, not " + qt.getClass().getName());
            }
            CursorNode cn = (CursorNode) qt;
            if (!(cn.getResultSetNode() instanceof SelectNode)) {
                SanityManager.THROWASSERT("cn.getResultSetNode() expected to be instanceof SelectNode, not "
                        + cn.getResultSetNode().getClass().getName());
            }
        }

        clauseTree = ((ResultColumn) ((CursorNode) qt).getResultSetNode().getResultColumns().elementAt(0))
                .getExpression();

        lcc.popCompilerContext(newCC);

        return clauseTree;
    }

    /**
     * Gets and binds all the constraints for an INSERT/UPDATE/DELETE.
     * First finds the constraints that are relevant to this node.
     * This is done by calling getAllRelevantConstriants().  If
     * getAllRelevantConstraints() has already been called, then
     * this list is used.  Then it creates appropriate 
     * dependencies. Then binds check constraints.  It also 
     * generates the array of FKInfo items that are used in
     * code generation.
        
     * Note: we have a new flag here to see if defer processing is enabled or
     *       not, the only scenario that is disabled is when we reapply the
     *       reply message we get from the source
     *
     *
     * @param dataDictionary      The DataDictionary
     * @param nodeFactory         Where to get query tree nodes.
     * @param targetTableDescriptor   The TableDescriptor
     * @param dependent         Parent object that will depend on all the constraints
     *                     that we look up. If this argument is null, then we
     *                     use the default dependent (the statement being compiled).
     * @param sourceRCL            RCL of the table being changed
     * @param changedColumnIds      If null, all columns being changed, otherwise array
     *                        of 1-based column ids for columns being changed
     * @param readColsBitSet      bit set for the read scan
     * @param skipCheckConstraints    whether to skip check constraints or not
     * @param includeTriggers      whether triggers are included in the processing
     *
     * @return   The bound, ANDed check constraints as a query tree.
     *
     * @exception StandardException      Thrown on failure
     */
    ValueNode bindConstraints(DataDictionary dataDictionary, NodeFactory nodeFactory,
            TableDescriptor targetTableDescriptor, Dependent dependent, ResultColumnList sourceRCL,
            int[] changedColumnIds, FormatableBitSet readColsBitSet, boolean skipCheckConstraints,
            boolean includeTriggers) throws StandardException {
        bound = true;

        /* Nothing to do if updatable VTI */
        if (targetVTI != null) {
            return null;
        }

        CompilerContext compilerContext = getCompilerContext();

        // Donot need privileges to execute constraints
        compilerContext.pushCurrentPrivType(Authorizer.NULL_PRIV);
        try {
            getAllRelevantConstraints(dataDictionary, targetTableDescriptor, skipCheckConstraints,
                    changedColumnIds);
            generatePKInfo(targetTableDescriptor);
            createConstraintDependencies(dataDictionary, relevantCdl, dependent);

            // Commented out the following line as part of work on DB-2229: referential actions.  The
            // referential actions we have implemented so far do not actually use Derby's FKInfo but
            // an assertion fails in this method when a DeleteNode is created for deleting by primary key.
            // We may need to re-enable this if future referential action implementations use FKInfo.
            //
            //generateFKInfo(relevantCdl, dataDictionary, targetTableDescriptor, readColsBitSet);

            getAllRelevantTriggers(dataDictionary, targetTableDescriptor, changedColumnIds, includeTriggers);
            createTriggerDependencies(relevantTriggers, dependent);
            generateTriggerInfo(relevantTriggers, targetTableDescriptor, changedColumnIds);

            if (skipCheckConstraints) {
                return null;
            }

            checkConstraints = generateCheckTree(relevantCdl, targetTableDescriptor);

            if (checkConstraints != null) {
                SchemaDescriptor originalCurrentSchema = targetTableDescriptor.getSchemaDescriptor();
                compilerContext.pushCompilationSchema(originalCurrentSchema);

                try {
                    bindRowScopedExpression(nodeFactory, getContextManager(), targetTableDescriptor, sourceRCL,
                            checkConstraints);
                } finally {
                    compilerContext.popCompilationSchema();
                }
            }
        } finally {
            compilerContext.popCurrentPrivType();
        }

        return checkConstraints;
    }

    /**
     * Binds an already parsed expression that only involves columns in a single
     * row. E.g., a check constraint or a generation clause.
     *
     * @param nodeFactory         Where to get query tree nodes.
     * @param targetTableDescriptor   The TableDescriptor for the constrained table.
     * @param sourceRCL      Result columns.
     * @param expression      Parsed query tree for row scoped expression
     *
     * @exception StandardException      Thrown on failure
     */
    static void bindRowScopedExpression(NodeFactory nodeFactory, ContextManager contextManager,
            TableDescriptor targetTableDescriptor, ResultColumnList sourceRCL, ValueNode expression)
            throws StandardException {

        TableName targetTableName = makeTableName(nodeFactory, contextManager,
                targetTableDescriptor.getSchemaName(), targetTableDescriptor.getName());

        /* We now have the expression as a query tree.  Now, we prepare
         * to bind that query tree to the source's RCL.  That way, the
         * generated code for the expression will be evaluated against the
         * source row to be inserted into the target table or
         * against the after portion of the source row for the update
         * into the target table.
         *      o  Goober up a new FromList which has a single table,
         *         a goobered up FromBaseTable for the target table
         *         which has the source's RCL as it RCL.
         *         (This allows the ColumnReferences in the expression
         *         tree to be bound to the right RCs.)
         *
          * Note that in some circumstances we may not actually verify
         * the expression against the source RCL but against a temp
         * row source used for deferred processing because of a trigger.
         * In this case, the caller of bindConstraints (UpdateNode)
         * has chosen to pass in the correct RCL to bind against.
         */
        FromList fakeFromList = (FromList) nodeFactory.getNode(C_NodeTypes.FROM_LIST,
                nodeFactory.doJoinOrderOptimization(), contextManager);
        FromBaseTable table = (FromBaseTable) nodeFactory.getNode(C_NodeTypes.FROM_BASE_TABLE, targetTableName,
                null, sourceRCL, null, contextManager);
        table.setTableNumber(0);
        fakeFromList.addFromTable(table);

        // Now we can do the bind.
        expression = expression.bindExpression(fakeFromList, (SubqueryList) null, (Vector) null);
    }

    /**
     * Determine whether or not there are check constraints on the
     * specified table.
     *
     * @param dd   The DataDictionary to use
     * @param td   The TableDescriptor for the table
     *
     * @return Whether or not there are check constraints on the specified table.
     *
     * @exception StandardException      Thrown on failure
     */
    protected boolean hasCheckConstraints(DataDictionary dd, TableDescriptor td) throws StandardException {
        ConstraintDescriptorList cdl = dd.getConstraintDescriptors(td);
        if (cdl == null)
            return false;
        ConstraintDescriptorList ccCDL = cdl.getSubList(DataDictionary.CHECK_CONSTRAINT);

        return (ccCDL.size() > 0);
    }

    /**
     * Determine whether or not there are generated columns in the
     * specified table.
     *
     * @param td   The TableDescriptor for the table
     *
     * @return Whether or not there are generated columns in the specified table.
     *
     * @exception StandardException      Thrown on failure
     */
    protected boolean hasGenerationClauses(TableDescriptor td) throws StandardException {
        ColumnDescriptorList list = td.getGeneratedColumns();

        return (list.size() > 0);
    }

    /**
     * Get the ANDing of all appropriate check constraints as 1 giant query tree.
     *
     * Makes the calling object (usually a Statement) dependent on all the constraints.
     *
     * @param cdl            The constriant descriptor list
     * @param td            The TableDescriptor
     *
     * @return   The ANDing of all appropriate check constraints as a query tree.
     *
     * @exception StandardException      Thrown on failure
     */
    private ValueNode generateCheckTree(ConstraintDescriptorList cdl, TableDescriptor td) throws StandardException {
        ConstraintDescriptorList ccCDL = cdl.getSubList(DataDictionary.CHECK_CONSTRAINT);
        int ccCDLSize = ccCDL.size();
        ValueNode checkTree = null;

        // Get the text of all the check constraints
        for (int index = 0; index < ccCDLSize; index++) {
            ConstraintDescriptor cd = ccCDL.elementAt(index);

            String constraintText = cd.getConstraintText();

            // Get the query tree for this constraint
            ValueNode oneConstraint = parseCheckConstraint(constraintText, td);

            // Put a TestConstraintNode above the constraint tree
            TestConstraintNode tcn = (TestConstraintNode) getNodeFactory().getNode(C_NodeTypes.TEST_CONSTRAINT_NODE,
                    oneConstraint, SQLState.LANG_CHECK_CONSTRAINT_VIOLATED,
                    td.getSchemaName() == null ? td.getName() : td.getSchemaName() + "." + td.getName(),
                    cd.getConstraintName(), getContextManager());

            // Link consecutive TestConstraintNodes with AND nodes
            if (checkTree == null) {
                checkTree = tcn;
            } else {
                checkTree = (ValueNode) getNodeFactory().getNode(C_NodeTypes.AND_NODE, tcn, checkTree,
                        getContextManager());
            }
        }

        return checkTree;
    }

    private void generatePKInfo(TableDescriptor td) throws StandardException {
        pkColumns = getPrimaryKeyInfo(td);
        return;
    }

    public static int[] getPrimaryKeyInfo(TableDescriptor td) throws StandardException {
        ConglomerateDescriptorList cdl = td.getConglomerateDescriptorList();
        for (int i = 0; i < cdl.size(); i++) {
            ConglomerateDescriptor cd = cdl.get(i);
            if (cd.isPrimaryKey()) {
                return cd.getIndexDescriptor().baseColumnPositions();
            }
        }
        return null;
    }

    /**
     * Generate the TriggerInfo structures used during code generation.
     *
     * @param triggerList            The trigger descriptor list
     * @param td            The TableDescriptor
     * @param changedCols      The columns that are being modified
     *
     * @exception StandardException      Thrown on failure
     */
    private void generateTriggerInfo(GenericDescriptorList triggerList, TableDescriptor td, int[] changedCols)
            throws StandardException {
        if ((triggerList != null) && (triggerList.size() > 0)) {
            triggerInfo = new TriggerInfo(td, changedCols, triggerList);
        }
    }

    /**
     * Return the FKInfo structure.  Just  a little wrapper
     * to make sure we don't try to access it until after
     * binding.
     *
     * @return the array of fkinfos
     */
    public FKInfo[] getFKInfo() {
        if (SanityManager.DEBUG) {
            SanityManager.ASSERT(bound, "attempt to access FKInfo " + "before binding");
        }
        return fkInfo;
    }

    /**
     * Return the TriggerInfo structure.  Just  a little wrapper
     * to make sure we don't try to access it until after
     * binding.
     *
     * @return the trigger info
     */
    public TriggerInfo getTriggerInfo() {
        if (SanityManager.DEBUG) {
            SanityManager.ASSERT(bound, "attempt to access TriggerInfo " + "before binding");
        }
        return triggerInfo;
    }

    /**
     * Get the check constraints for this node
     *
     * @return the check constraints, may be null
     */
    public ValueNode getCheckConstraints() {
        if (SanityManager.DEBUG) {
            SanityManager.ASSERT(bound, "attempt to access FKInfo " + "before binding");
        }
        return checkConstraints;
    }

    /**
     * Makes the calling object (usually a Statement) dependent on all the constraints.
     *
     * @param tdl            The trigger descriptor list
     * @param dependent         Parent object that will depend on all the constraints
     *                     that we look up. If this argument is null, then we
     *                     use the default dependent (the statement being compiled).
     *
     * @exception StandardException      Thrown on failure
     */
    private void createTriggerDependencies(GenericDescriptorList tdl, Dependent dependent)
            throws StandardException {
        CompilerContext compilerContext = getCompilerContext();

        for (Iterator descIter = tdl.iterator(); descIter.hasNext();) {
            TriggerDescriptor td = (TriggerDescriptor) descIter.next();
            /*
            ** The dependent now depends on this trigger.
            ** The default dependent is the statement being compiled.
            */
            if (dependent == null) {
                compilerContext.createDependency(td);
            } else {
                compilerContext.createDependency(dependent, td);
            }
        }
    }

    /**
     * Get all the triggers relevant to this DML operation
     *
     * @param dd            The data dictionary
     * @param td            The TableDescriptor
     * @param changedColumnIds   If null, all columns being changed, otherwise array
     *                     of 1-based column ids for columns being changed
     * @param includeTriggers   whether we allow trigger processing or not for
     *                      this table
     *
     * @return   the constraint descriptor list
     *
     * @exception StandardException      Thrown on failure
     */
    protected GenericDescriptorList getAllRelevantTriggers(DataDictionary dd, TableDescriptor td,
            int[] changedColumnIds, boolean includeTriggers) throws StandardException {
        if (relevantTriggers != null) {
            return relevantTriggers;
        }

        relevantTriggers = new GenericDescriptorList();

        if (!includeTriggers)
            return relevantTriggers;

        td.getAllRelevantTriggers(statementType, changedColumnIds, relevantTriggers);
        adjustDeferredFlag(relevantTriggers.size() > 0);
        return relevantTriggers;
    }

    protected void adjustDeferredFlag(boolean adjustment) {
        if (!requiresDeferredProcessing) {
            requiresDeferredProcessing = adjustment;
        }
    }

    /**
     * Get all of our dependents due to a constraint.
     *
     * Makes the calling object (usually a Statement) dependent on all the constraints.
     *
     * @param dd            The data dictionary
     * @param cdl            The constraint descriptor list
     * @param dependent         Parent object that will depend on all the constraints
     *                     that we look up. If this argument is null, then we
     *                     use the default dependent (the statement being compiled).
     *
     * @exception StandardException      Thrown on failure
     */
    private void createConstraintDependencies(DataDictionary dd, ConstraintDescriptorList cdl, Dependent dependent)
            throws StandardException {
        CompilerContext compilerContext = getCompilerContext();

        int cdlSize = cdl.size();
        for (int index = 0; index < cdlSize; index++) {
            ConstraintDescriptor cd = cdl.elementAt(index);

            /*
            ** The dependent now depends on this constraint. 
            ** the default dependent is the statement 
            ** being compiled.
            */
            if (dependent == null) {
                compilerContext.createDependency(cd);
            } else {
                compilerContext.createDependency(dependent, cd);
            }

            /*
            ** We are also dependent on all referencing keys --
            ** if one of them is deleted, we'll have to recompile.
            ** Also, if there is a BULK_INSERT on the table
            ** we are going to scan to validate the constraint,
            ** the index number will change, so we'll add a
            ** dependency on all tables we will scan.
            */
            if (cd instanceof ReferencedKeyConstraintDescriptor) {
                ConstraintDescriptorList fkcdl = dd
                        .getActiveConstraintDescriptors(((ReferencedKeyConstraintDescriptor) cd)
                                .getForeignKeyConstraints(ConstraintDescriptor.ENABLED));

                int fklSize = fkcdl.size();
                for (int inner = 0; inner < fklSize; inner++) {
                    ConstraintDescriptor fkcd = fkcdl.elementAt(inner);
                    if (dependent == null) {
                        compilerContext.createDependency(fkcd);
                        compilerContext.createDependency(fkcd.getTableDescriptor());
                    } else {
                        compilerContext.createDependency(dependent, fkcd);
                        compilerContext.createDependency(dependent, fkcd.getTableDescriptor());
                    }
                }
            } else if (cd instanceof ForeignKeyConstraintDescriptor) {
                ForeignKeyConstraintDescriptor fkcd = (ForeignKeyConstraintDescriptor) cd;
                if (dependent == null) {
                    compilerContext.createDependency(fkcd.getReferencedConstraint().getTableDescriptor());
                } else {
                    compilerContext.createDependency(dependent,
                            fkcd.getReferencedConstraint().getTableDescriptor());
                }
            }
        }
    }

    /**
     * Get all the constraints relevant to this DML operation
     *
     * @param dd            The DataDictionary
     * @param td            The TableDescriptor
     * @param skipCheckConstraints Skip check constraints
     * @param changedColumnIds   If null, all columns being changed, otherwise array
     *                     of 1-based column ids for columns being changed
     *
     * @return   the constraint descriptor list
     *
     * @exception StandardException      Thrown on failure
     */
    protected ConstraintDescriptorList getAllRelevantConstraints(DataDictionary dd, TableDescriptor td,
            boolean skipCheckConstraints, int[] changedColumnIds) throws StandardException {
        if (relevantCdl != null) {
            return relevantCdl;
        }

        boolean[] needsDeferredProcessing = new boolean[1];
        relevantCdl = new ConstraintDescriptorList();

        needsDeferredProcessing[0] = requiresDeferredProcessing;
        td.getAllRelevantConstraints(statementType, skipCheckConstraints, changedColumnIds, needsDeferredProcessing,
                relevantCdl);

        adjustDeferredFlag(needsDeferredProcessing[0]);

        return relevantCdl;
    }

    /**
     * Does this DML Node require deferred processing?
     * Set to true if we have triggers or referential
     * constraints that need deferred processing.
     *
     * @return true/false 
     */
    public boolean requiresDeferredProcessing() {
        return requiresDeferredProcessing;
    }

    /**
      *   Parse a check constraint and turn it into a query tree.
      *
      *   @param   checkConstraintText   Text of CHECK CONSTRAINT.
      * @param   td               The TableDescriptor for the table the the constraint is on.
      *
      *
      * @return   The parsed check constraint as a query tree.
      *
      * @exception StandardException      Thrown on failure
      */
    public ValueNode parseCheckConstraint(String checkConstraintText, TableDescriptor td) throws StandardException {
        Parser p;
        ValueNode checkTree;
        LanguageConnectionContext lcc = getLanguageConnectionContext();
        CompilerContext compilerContext = getCompilerContext();

        /* Get a Statement to pass to the parser */

        /* We're all set up to parse. We have to build a compile SQL statement
         * before we can parse - we just have a WHERE clause right now.
         * So, we goober up a SELECT * FROM table WHERE checkDefs.
         */
        String select = "SELECT * FROM " + td.getQualifiedName() + " WHERE " + checkConstraintText;

        /*
        ** Get a new compiler context, so the parsing of the select statement
        ** doesn't mess up anything in the current context (it could clobber
        ** the ParameterValueSet, for example).
        */
        CompilerContext newCC = lcc.pushCompilerContext();

        p = newCC.getParser();

        /* Finally, we can call the parser */
        // Since this is always nested inside another SQL statement, so topLevel flag
        // should be false
        Visitable qt = p.parseStatement(select);
        if (SanityManager.DEBUG) {
            if (!(qt instanceof CursorNode)) {
                SanityManager
                        .THROWASSERT("qt expected to be instanceof CursorNode, not " + qt.getClass().getName());
            }
            CursorNode cn = (CursorNode) qt;
            if (!(cn.getResultSetNode() instanceof SelectNode)) {
                SanityManager.THROWASSERT("cn.getResultSetNode() expected to be instanceof SelectNode, not "
                        + cn.getResultSetNode().getClass().getName());
            }
        }

        checkTree = ((SelectNode) ((CursorNode) qt).getResultSetNode()).getWhereClause();

        lcc.popCompilerContext(newCC);

        return checkTree;
    }

    /**
      *   Generate the code to evaluate a tree of CHECK CONSTRAINTS.
      *
      *   @param   checkConstraints   Bound query tree of ANDed check constraints.
      *   @param   ecb               Expression Class Builder
      *
      *
      *
      * @exception StandardException      Thrown on error
      */
    public void generateCheckConstraints(ValueNode checkConstraints, ExpressionClassBuilder ecb, MethodBuilder mb)
            throws StandardException {
        // for the check constraints, we generate an exprFun
        // that evaluates the expression of the clause
        // against the current row of the child's result.
        // if there are no check constraints, simply pass null
        // to optimize for run time performance.

        // generate the function and initializer:
        // Note: Boolean lets us return nulls (boolean would not)
        // private Boolean exprN()
        // {
        //   return <<checkConstraints.generate(ps)>>;
        // }
        // static Method exprN = method pointer to exprN;

        // if there is no check constraint, we just want to pass null.
        if (checkConstraints == null) {
            mb.pushNull(ClassName.GeneratedMethod);
        } else {
            MethodBuilder userExprFun = generateCheckConstraints(checkConstraints, ecb);

            // check constraint is used in the final result set 
            // as an access of the new static
            // field holding a reference to this new method.
            ecb.pushMethodReference(mb, userExprFun);
        }
    }

    /**
      *   Generate a method to evaluate a tree of CHECK CONSTRAINTS.
      *
      *   @param   checkConstraints   Bound query tree of ANDed check constraints.
      *   @param   ecb               Expression Class Builder
      *
      *
      *
      * @exception StandardException      Thrown on error
      */
    public MethodBuilder generateCheckConstraints(ValueNode checkConstraints, ExpressionClassBuilder ecb)
            throws StandardException {
        // this sets up the method and the static field.
        // generates:
        //    java.lang.Object userExprFun { }
        MethodBuilder userExprFun = ecb.newUserExprFun();

        // check constraint knows it is returning its value;

        /* generates:
         *    return <checkExpress.generate(ecb)>;
         * and adds it to userExprFun
         */

        checkConstraints.generateExpression(ecb, userExprFun);
        userExprFun.methodReturn();

        // we are done modifying userExprFun, complete it.
        userExprFun.complete();

        return userExprFun;
    }

    /**
      *   Generate the code to evaluate all of the generation clauses. If there
      *   are generation clauses, this routine builds an Activation method which
      *   evaluates the generation clauses and fills in the computed columns.
       *
       * @param rcl  describes the row of expressions to be put into the bas table
       * @param resultSetNumber  index of base table into array of ResultSets
       * @param isUpdate true if this is for an UPDATE statement
       * @param ecb code generation state variable
       * @param mb the method being generated
       *
      * @exception StandardException      Thrown on error
      */
    public void generateGenerationClauses(ResultColumnList rcl, int resultSetNumber, boolean isUpdate,
            ExpressionClassBuilder ecb, MethodBuilder mb) throws StandardException {
        ResultColumn rc;
        int size = rcl.size();
        boolean hasGenerationClauses = false;

        for (int index = 0; index < size; index++) {
            rc = (ResultColumn) rcl.elementAt(index);

            //
            // Generated columns should be populated after the base row because
            // the generation clauses may refer to base columns that have to be filled
            // in first.
            //
            if (rc.hasGenerationClause()) {
                hasGenerationClauses = true;
                break;
            }
        }

        // we generate an exprFun
        // that evaluates the generation clauses
        // against the current row of the child's result.
        // if there are no generation clauses, simply pass null
        // to optimize for run time performance.

        // generate the function and initializer:
        // private Integer exprN()
        // {
        //   ...
        //   return 1 or NULL;
        // }
        // static Method exprN = method pointer to exprN;

        // if there are not generation clauses, we just want to pass null.
        if (!hasGenerationClauses) {
            mb.pushNull(ClassName.GeneratedMethod);
        } else {
            MethodBuilder userExprFun = generateGenerationClauses(rcl, resultSetNumber, isUpdate, ecb);

            // generation clause evaluation is used in the final result set 
            // as an access of the new static
            // field holding a reference to this new method.
            ecb.pushMethodReference(mb, userExprFun);
        }
    }

    /**
      *   Generate a method to compute all of the generation clauses in a row.
       *
       * @param rcl  describes the row of expressions to be put into the bas table
       * @param rsNumber  index of base table into array of ResultSets
       * @param isUpdate true if this is for an UPDATE statement
       * @param ecb code generation state variable
       *
      */
    private MethodBuilder generateGenerationClauses(ResultColumnList rcl, int rsNumber, boolean isUpdate,
            ExpressionClassBuilder ecb) throws StandardException {
        // this sets up the method and the static field.
        // generates:
        //    java.lang.Object userExprFun( ) { }
        MethodBuilder userExprFun = ecb.newUserExprFun();

        /* Push the the current row onto the stack. */
        userExprFun.pushThis();
        userExprFun.push(rsNumber);
        userExprFun.callMethod(VMOpcode.INVOKEVIRTUAL, ClassName.BaseActivation, "getCurrentRow", ClassName.Row, 1);

        // Loop through the result columns, computing generated columns
        // as we go. 
        int size = rcl.size();
        int startColumn = 0;
        // For UPDATEs, we only compute the updated value for the
        // column. The updated value lives in the second half of the row.
        // This means we ignore the first half of the row, which holds
        // the before-images of the columns.
        if (isUpdate) {
            // throw away the last cell in the row, which is the row id
            startColumn = size - 1;
            startColumn = startColumn / 2;
        }
        for (int i = startColumn; i < size; i++) {
            ResultColumn rc = (ResultColumn) rcl.elementAt(i);

            if (!rc.hasGenerationClause()) {
                continue;
            }

            userExprFun.dup(); // instance (current row)
            userExprFun.push(i + 1); // arg1

            rc.generateExpression(ecb, userExprFun);
            userExprFun.cast(ClassName.DataValueDescriptor);

            userExprFun.callMethod(VMOpcode.INVOKEINTERFACE, ClassName.Row, "setColumn", "void", 2);
        }

        /* generates:
         *    return;
         * And adds it to userExprFun
         */
        userExprFun.methodReturn();

        // we are done modifying userExprFun, complete it.
        userExprFun.complete();

        return userExprFun;
    }

    /**
     * Generate an optimized QueryTree from a bound QueryTree.  Actually,
     * it can annotate the tree in place rather than generate a new tree,
     * but this interface allows the root node of the optimized QueryTree
     * to be different from the root node of the bound QueryTree.
     *
     * For non-optimizable statements, this method is a no-op.
     *
     * Throws an exception if the tree is not bound, or if the binding
     * is out of date.
     *
     *
     * @exception StandardException         Thrown on failure
     */
    public void optimizeStatement() throws StandardException {
        /* First optimize the query */
        super.optimizeStatement();

        /* In language we always set it to row lock, it's up to store to
         * upgrade it to table lock.  This makes sense for the default read
         * committed isolation level and update lock.  For more detail, see
         * Beetle 4133.
         */
        lockMode = TransactionController.MODE_RECORD;
    }

    /**
     * Get the list of indexes that must be updated by this DML statement.
     * WARNING: As a side effect, it creates dependencies on those indexes.
     *
     * @param td   The table descriptor for the table being updated
     * @param updatedColumns   The updated column list.  If not update, null
     * @param colBitSet         a 1 based bit set of the columns in the list
     *
     * @exception StandardException      Thrown on error
     */
    protected void getAffectedIndexes(TableDescriptor td, ResultColumnList updatedColumns,
            FormatableBitSet colBitSet) throws StandardException {
        Vector conglomVector = new Vector();

        DMLModStatementNode.getXAffectedIndexes(td, updatedColumns, colBitSet, conglomVector);

        markAffectedIndexes(conglomVector);
    }

    /**
      *   Marks which indexes are affected by an UPDATE of the
      *   desired shape.
      *
      *   Is passed a list of updated columns. Does the following:
      *
      *   1)   finds all indices which overlap the updated columns
      *   2)   adds the index columns to a bitmap of affected columns
      *   3)   adds the index descriptors to a list of conglomerate
      *      descriptors.
      *
      *   @param   updatedColumns   a list of updated columns
      *   @param   colBitSet      OUT: evolving bitmap of affected columns
      *   @param   conglomVector   OUT: vector of affected indices
      *
      * @exception StandardException      Thrown on error
      */
    static void getXAffectedIndexes(TableDescriptor baseTable, ResultColumnList updatedColumns,
            FormatableBitSet colBitSet, Vector conglomVector) throws StandardException {
        ConglomerateDescriptor[] cds = baseTable.getConglomerateDescriptors();

        /* we only get distinct conglomerate numbers.  If duplicate indexes
         * share one conglomerate, we only return one number.
         */
        long[] distinctConglomNums = new long[cds.length - 1];
        int distinctCount = 0;

        int[] primaryKeyColumns = getPrimaryKeyInfo(baseTable);

        for (int index = 0; index < cds.length; index++) {
            ConglomerateDescriptor cd = cds[index];

            if (!cd.isIndex()) {
                continue;
            }

            /*
            ** If this index doesn't contain any updated
            ** columns, then we can skip it.
            */
            if ((updatedColumns != null)
                    && (!updatedColumns.updateOverlaps(cd.getIndexDescriptor().baseColumnPositions()))) {
                continue;
            }

            if (conglomVector != null) {
                int i;
                for (i = 0; i < distinctCount; i++) {
                    if (distinctConglomNums[i] == cd.getConglomerateNumber())
                        break;
                }
                if (i == distinctCount) // first appearence
                {
                    distinctConglomNums[distinctCount++] = cd.getConglomerateNumber();
                    conglomVector.add(cd);
                }
            }

            IndexRowGenerator ixd = cd.getIndexDescriptor();
            int[] cols = ixd.baseColumnPositions();

            if (colBitSet != null) {
                for (int i = 0; i < cols.length; i++) {
                    // Do not Add Primary Keys, we have them in the rowKey (BaseTable) or in the RowLocation (IndexTable)
                    if (!ArrayUtils.contains(primaryKeyColumns, cols[i]))
                        colBitSet.set(cols[i]);
                }
            } // end IF
        } // end loop through conglomerates

    }

    protected void markAffectedIndexes(Vector affectedConglomerates) throws StandardException {
        ConglomerateDescriptor cd;
        int indexCount = affectedConglomerates.size();
        CompilerContext cc = getCompilerContext();

        indicesToMaintain = new IndexRowGenerator[indexCount];
        indexConglomerateNumbers = new long[indexCount];
        indexNames = new String[indexCount];

        for (int ictr = 0; ictr < indexCount; ictr++) {
            cd = (ConglomerateDescriptor) affectedConglomerates.get(ictr);

            indicesToMaintain[ictr] = cd.getIndexDescriptor();
            indexConglomerateNumbers[ictr] = cd.getConglomerateNumber();
            indexNames[ictr] = ((cd.isConstraint()) ? null : cd.getConglomerateName());

            cc.createDependency(cd);
        }

    }

    public String statementToString() {
        return "DML MOD";
    }

    /**
     * Remap referenced columns in the cd to reflect the
     * passed in row map.
     *
     * @param cd       constraint descriptor
     * @param rowMap   1 based row map 
     */
    private int[] remapReferencedColumns(ConstraintDescriptor cd, int[] rowMap) {
        int[] oldCols = cd.getReferencedColumns();
        if (rowMap == null) {
            return oldCols;
        }

        int[] newCols = new int[oldCols.length];
        for (int i = 0; i < oldCols.length; i++) {
            newCols[i] = rowMap[oldCols[i]];
            if (SanityManager.DEBUG) {
                SanityManager.ASSERT(newCols[i] != 0,
                        "attempt to map a column " + oldCols[i]
                                + " which is not in our new column map.  Something is "
                                + "wrong with the logic to do partial reads for an update stmt");
            }
        }
        return newCols;
    }

    /**
     * Get a integer based row map from a bit set.
     * 
     * @param bitSet
     * @param td 
     * 
     */
    private int[] getRowMap(FormatableBitSet bitSet, TableDescriptor td) throws StandardException {
        if (bitSet == null) {
            return (int[]) null;
        }

        int size = td.getMaxColumnID();
        int[] iArray = new int[size + 1];
        int j = 1;
        for (int i = 1; i <= size; i++) {
            if (bitSet.get(i)) {
                iArray[i] = j++;
            }
        }
        return iArray;
    }

    public void setRefActionInfo(long fkIndexConglomId, int[] fkColArray, String parentResultSetId,
            boolean dependentScan) {
        resultSet.setRefActionInfo(fkIndexConglomId, fkColArray, parentResultSetId, dependentScan);
    }

    /**
     * Normalize synonym column references to have the name of the base table. 
     *
     * @param rcl              The result column list of the target table
     * @param targetTableName  The target tablename
     *
     * @exception StandardException      Thrown on error
     */
    public void normalizeSynonymColumns(ResultColumnList rcl, TableName targetTableName) throws StandardException {
        if (synonymTableName == null)
            return;

        String synTableName = synonymTableName.getTableName();

        int count = rcl.size();
        for (int i = 0; i < count; i++) {
            ResultColumn column = (ResultColumn) rcl.elementAt(i);
            ColumnReference reference = column.getReference();

            if (reference != null) {
                String crTableName = reference.getTableName();
                if (crTableName != null) {
                    if (synTableName.equals(crTableName)) {
                        reference.setTableNameNode(targetTableName);
                    } else {
                        throw StandardException.newException(SQLState.LANG_TABLE_NAME_MISMATCH, synTableName,
                                crTableName);
                    }
                }
            }
        }
    }

    /**
     * Prints the sub-nodes of this object.  See QueryTreeNode.java for
     * how tree printing is supposed to work.
     *
     * @param depth      The depth of this node in the tree
     */

    public void printSubNodes(int depth) {
        if (SanityManager.DEBUG) {
            super.printSubNodes(depth);

            printLabel(depth, "targetTableName: ");
            targetTableName.treePrint(depth + 1);

            if (resultColumnList != null) {
                printLabel(depth, "resultColumnList: ");
                resultColumnList.treePrint(depth + 1);
            }
        }
    }

    /**
     * Accept the visitor for all visitable children of this node.
     * 
     * @param v the visitor
     */
    @Override
    public void acceptChildren(Visitor v) throws StandardException {
        super.acceptChildren(v);

        if (targetTableName != null) {
            targetTableName.accept(v, this);
        }
    }
}