Package org.apache.derby.impl.sql.compile

Source Code of org.apache.derby.impl.sql.compile.TableOperatorNode

/*

   Derby - Class org.apache.derby.impl.sql.compile.TableOperatorNode

   Copyright 1997, 2004 The Apache Software Foundation or its licensors, as applicable.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file 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.

*/

package  org.apache.derby.impl.sql.compile;

import org.apache.derby.iapi.services.context.ContextManager;

import org.apache.derby.iapi.services.sanity.SanityManager;

import org.apache.derby.iapi.error.StandardException;

import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
import org.apache.derby.iapi.sql.compile.CompilerContext;
import org.apache.derby.iapi.sql.compile.Optimizable;
import org.apache.derby.iapi.sql.compile.Visitable;
import org.apache.derby.iapi.sql.compile.Visitor;
import org.apache.derby.iapi.sql.compile.Optimizer;
import org.apache.derby.iapi.sql.compile.OptimizableList;
import org.apache.derby.iapi.sql.compile.CostEstimate;
import org.apache.derby.iapi.sql.compile.OptimizerFactory;
import org.apache.derby.iapi.sql.compile.RequiredRowOrdering;
import org.apache.derby.iapi.sql.compile.C_NodeTypes;

import org.apache.derby.iapi.sql.dictionary.DataDictionary;
import org.apache.derby.iapi.sql.dictionary.TableDescriptor;

import org.apache.derby.iapi.util.JBitSet;

import java.util.Properties;

/**
* A TableOperatorNode represents a relational operator like UNION, INTERSECT,
* JOIN, etc. that takes two tables as parameters and returns a table.  The
* parameters it takes are represented as ResultSetNodes.
*
* Currently, all known table operators are binary operators, so there are no
* subclasses of this node type called "BinaryTableOperatorNode" and
* "UnaryTableOperatorNode".
*
* @author Jeff Lichtman
*/

public abstract class TableOperatorNode extends FromTable
{
  boolean      nestedInParens;
  ResultSetNode  leftResultSet;
  ResultSetNode  rightResultSet;
  Optimizer    leftOptimizer;
  Optimizer    rightOptimizer;
  private boolean   leftModifyAccessPathsDone;
  private boolean   rightModifyAccessPathsDone;

  /**
   * Initializer for a TableOperatorNode.
   *
   * @param leftResultSet    The ResultSetNode on the left side of this node
   * @param rightResultSet  The ResultSetNode on the right side of this node
   * @param tableProperties  Properties list associated with the table
   *
   * @exception StandardException    Thrown on error
   */
  public void init(Object leftResultSet,
               Object rightResultSet,
               Object tableProperties)
        throws StandardException
  {
    /* correlationName is always null */
    init(null, tableProperties);
    this.leftResultSet = (ResultSetNode) leftResultSet;
    this.rightResultSet = (ResultSetNode) rightResultSet;
  }

  /**
   * @see Optimizable#modifyAccessPath
   *
   * @exception StandardException    Thrown on error
   */
  public Optimizable modifyAccessPath(JBitSet outerTables) throws StandardException
  {
    boolean callModifyAccessPaths = false;

    if (leftResultSet instanceof FromTable)
    {
      if (leftOptimizer != null)
        leftOptimizer.modifyAccessPaths();
      else
      {
        leftResultSet =
          (ResultSetNode)
            ((FromTable) leftResultSet).modifyAccessPath(outerTables);
      }
      leftModifyAccessPathsDone = true;
    }
    else
    {
      callModifyAccessPaths = true;
    }

    if (rightResultSet instanceof FromTable)
    {
      if (rightOptimizer != null)
        rightOptimizer.modifyAccessPaths();
      else
      {
        rightResultSet =
          (ResultSetNode)
            ((FromTable) rightResultSet).modifyAccessPath(outerTables);
      }
      rightModifyAccessPathsDone = true;
    }
    else
    {
      callModifyAccessPaths = true;
    }

    if (callModifyAccessPaths)
    {
      return (Optimizable) modifyAccessPaths();
    }
    return this;
  }

  /** @see Optimizable#verifyProperties
   * @exception StandardException    Thrown on error
   */
  public void verifyProperties(DataDictionary dDictionary)
    throws StandardException
  {
    if (leftResultSet instanceof Optimizable)
    {
      ((Optimizable) leftResultSet).verifyProperties(dDictionary);
    }
    if (rightResultSet instanceof Optimizable)
    {
      ((Optimizable) rightResultSet).verifyProperties(dDictionary);
    }

    super.verifyProperties(dDictionary);
  }

  /**
   * Convert this object to a String.  See comments in QueryTreeNode.java
   * for how this should be done for tree printing.
   *
   * @return  This object as a String
   */

  public String toString()
  {
    if (SanityManager.DEBUG)
    {
      return "nestedInParens: " + nestedInParens + "\n" +
        leftResultSet.toString() + "\n" +
        rightResultSet.toString() + "\n" +
        super.toString();
    }
    else
    {
      return "";
    }
  }

  /**
   * 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
   *
   * @return  Nothing
   */

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

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

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

  /**
   * Get the leftResultSet from this node.
   *
   * @return ResultSetNode  The leftResultSet from this node.
   */
  public ResultSetNode getLeftResultSet()
  {
    return leftResultSet;
  }

  /**
   * Get the rightResultSet from this node.
   *
   * @return ResultSetNode  The rightResultSet from this node.
   */
  public ResultSetNode getRightResultSet()
  {
    return rightResultSet;
  }

  public ResultSetNode getLeftmostResultSet()
  {
    if (leftResultSet instanceof TableOperatorNode)
    {
      return ((TableOperatorNode) leftResultSet).getLeftmostResultSet();
    }
    else
    {
      return leftResultSet;
    }
  }

  public void setLeftmostResultSet(ResultSetNode newLeftResultSet)
  {
    if (leftResultSet instanceof TableOperatorNode)
    {
      ((TableOperatorNode) leftResultSet).setLeftmostResultSet(newLeftResultSet);
    }
    else
    {
      this.leftResultSet = newLeftResultSet;
    }
  }

  /**
   * Set the (query block) level (0-based) for this FromTable.
   *
   * @param level    The query block level for this FromTable.
   *
   * @return Nothing
   */
  public void setLevel(int level)
  {
    super.setLevel(level);
    if (leftResultSet instanceof FromTable)
    {
      ((FromTable) leftResultSet).setLevel(level);
    }
    if (rightResultSet instanceof FromTable)
    {
      ((FromTable) rightResultSet).setLevel(level);
    }
  }

  /**
   * Return the exposed name for this table, which is the name that
   * can be used to refer to this table in the rest of the query.
   *
   * @return  The exposed name for this table.
   */

  public String getExposedName()
  {
    return null;
  }

  /**
   * Mark whether or not this node is nested in parens.  (Useful to parser
   * since some trees get created left deep and others right deep.)
   *
   * @param nestedInParens  Whether or not this node is nested in parens.
   *
   * @return Nothing.
   */
  public void setNestedInParens(boolean nestedInParens)
  {
    this.nestedInParens = nestedInParens;
  }

  /**
   * Return whether or not the table operator for this node was
   * nested in parens in the query.  (Useful to parser
   * since some trees get created left deep and others right deep.)
   *
   * @return boolean    Whether or not this node was nested in parens.
   */
  public boolean getNestedInParens()
  {
    return nestedInParens;
  }


  /**
   * Bind the non VTI tables in this TableOperatorNode. This means getting
   * their TableDescriptors from the DataDictionary.
   * We will build an unbound RCL for this node.  This RCL must be
   * "bound by hand" after the underlying left and right RCLs
   * are bound.
   *
   * @param dataDictionary  The DataDictionary to use for binding
   * @param fromListParam    FromList to use/append to.
   *
   * @return  ResultSetNode    Returns this.
   *
   * @exception StandardException    Thrown on error
   */

  public ResultSetNode bindNonVTITables(DataDictionary dataDictionary,
              FromList fromListParam)
              throws StandardException
  {
    leftResultSet = leftResultSet.bindNonVTITables(dataDictionary, fromListParam);
    rightResultSet = rightResultSet.bindNonVTITables(dataDictionary, fromListParam);
    /* Assign the tableNumber */
    if (tableNumber == -1// allow re-bind, in which case use old number
      tableNumber = getCompilerContext().getNextTableNumber();

    return this;
  }

  /**
   * Bind the VTI tables in this TableOperatorNode. This means getting
   * their TableDescriptors from the DataDictionary.
   * We will build an unbound RCL for this node.  This RCL must be
   * "bound by hand" after the underlying left and right RCLs
   * are bound.
   *
   * @param fromListParam    FromList to use/append to.
   *
   * @return  ResultSetNode    Returns this.
   *
   * @exception StandardException    Thrown on error
   */

  public ResultSetNode bindVTITables(FromList fromListParam)
              throws StandardException
  {
    leftResultSet = leftResultSet.bindVTITables(fromListParam);
    rightResultSet = rightResultSet.bindVTITables(fromListParam);

    return this;
  }

  /**
   * Bind the expressions under this TableOperatorNode.  This means
   * binding the sub-expressions, as well as figuring out what the
   * return type is for each expression.
   *
   * @return  Nothing
   *
   * @exception StandardException    Thrown on error
   */

  public void bindExpressions(FromList fromListParam)
        throws StandardException
  {
    /*
    ** Parameters not allowed in select list of either side of union,
    ** except when the union is for a table constructor.
    */
    if ( ! (this instanceof UnionNode) ||
       ! ((UnionNode) this).tableConstructor())
    {
      leftResultSet.rejectParameters();
      rightResultSet.rejectParameters();
    }

    leftResultSet.bindExpressions(fromListParam);
    rightResultSet.bindExpressions(fromListParam);
  }

  /**
   * Check for (and reject) ? parameters directly under the ResultColumns.
   * This is done for SELECT statements.  For TableOperatorNodes, we
   * simply pass the check through to the left and right children.
   *
   * @return  Nothing
   *
   * @exception StandardException    Thrown if a ? parameter found
   *                  directly under a ResultColumn
   */

  public void rejectParameters() throws StandardException
  {
    leftResultSet.rejectParameters();
    rightResultSet.rejectParameters();
  }

  /**
   * Bind the expressions in this ResultSetNode if it has tables.  This means binding the
   * sub-expressions, as well as figuring out what the return type is for
   * each expression.
   *
   * @param fromListParam    FromList to use/append to.
   *
   * @return  Nothing
   *
   * @exception StandardException    Thrown on error
   */
  public void bindExpressionsWithTables(FromList fromListParam)
          throws StandardException
  {
    /*
    ** Parameters not allowed in select list of either side of a set operator,
    ** except when the set operator is for a table constructor.
    */
    if ( ! (this instanceof UnionNode) ||
       ! ((UnionNode) this).tableConstructor())
    {
      leftResultSet.rejectParameters();
      rightResultSet.rejectParameters();
    }

    leftResultSet.bindExpressionsWithTables(fromListParam);
    rightResultSet.bindExpressionsWithTables(fromListParam);
  }

  /**
   * Bind the result columns of this ResultSetNode when there is no
   * base table to bind them to.  This is useful for SELECT statements,
   * where the result columns get their types from the expressions that
   * live under them.
   *
   * @param fromListParam    FromList to use/append to.
   *
   * @return  Nothing
   *
   * @exception StandardException    Thrown on error
   */
  public void bindResultColumns(FromList fromListParam)
          throws StandardException
  {
    leftResultSet.bindResultColumns(fromListParam);
    rightResultSet.bindResultColumns(fromListParam);
  }

  /**
   * Bind the result columns for this ResultSetNode to a base table.
   * This is useful for INSERT and UPDATE statements, where the
   * result columns get their types from the table being updated or
   * inserted into.
   * If a result column list is specified, then the verification that the
   * result column list does not contain any duplicates will be done when
   * binding them by name.
   *
   * @param targetTableDescriptor  The TableDescriptor for the table being
   *        updated or inserted into
   * @param targetColumnList  For INSERT statements, the user
   *          does not have to supply column
   *          names (for example, "insert into t
   *          values (1,2,3)".  When this
   *          parameter is null, it means that
   *          the user did not supply column
   *          names, and so the binding should
   *          be done based on order.  When it
   *          is not null, it means do the binding
   *          by name, not position.
   * @param statement      Calling DMLStatementNode (Insert or Update)
   * @param fromListParam    FromList to use/append to.
   *
   * @return  Nothing
   *
   * @exception StandardException    Thrown on error
   */

  public void bindResultColumns(TableDescriptor targetTableDescriptor,
          FromVTI targetVTI,
          ResultColumnList targetColumnList,
          DMLStatementNode statement,
          FromList fromListParam)
        throws StandardException
  {
    leftResultSet.bindResultColumns(targetTableDescriptor,
                    targetVTI,
                    targetColumnList,
                    statement, fromListParam);
    rightResultSet.bindResultColumns(targetTableDescriptor,
                    targetVTI,
                    targetColumnList,
                    statement, fromListParam);
  }

  /**
   * Determine whether or not the specified name is an exposed name in
   * the current query block.
   *
   * @param name  The specified name to search for as an exposed name.
   * @param schemaName  Schema name, if non-null.
   * @param exactMatch  Whether or not we need an exact match on specified schema and table
   *            names or match on table id.
   *
   * @return The FromTable, if any, with the exposed name.
   *
   * @exception StandardException    Thrown on error
   */
  protected FromTable getFromTableByName(String name, String schemaName, boolean exactMatch)
    throws StandardException
  {
    FromTable result = leftResultSet.getFromTableByName(name, schemaName, exactMatch);

    /* We search both sides for a TableOperatorNode (join nodes)
     * but only the left side for a UnionNode.
     */
    if (result == null)
    {
      result = rightResultSet.getFromTableByName(name, schemaName, exactMatch);
    }
    return result;
  }

  /**
   * Put a ProjectRestrictNode on top of each FromTable in the FromList.
   * ColumnReferences must continue to point to the same ResultColumn, so
   * that ResultColumn must percolate up to the new PRN.  However,
   * that ResultColumn will point to a new expression, a VirtualColumnNode,
   * which points to the FromTable and the ResultColumn that is the source for
   * the ColumnReference. 
   * (The new PRN will have the original of the ResultColumnList and
   * the ResultColumns from that list.  The FromTable will get shallow copies
   * of the ResultColumnList and its ResultColumns.  ResultColumn.expression
   * will remain at the FromTable, with the PRN getting a new
   * VirtualColumnNode for each ResultColumn.expression.)
   * We then project out the non-referenced columns.  If there are no referenced
   * columns, then the PRN's ResultColumnList will consist of a single ResultColumn
   * whose expression is 1.
   *
   * @param numTables      Number of tables in the DML Statement
   * @param gbl        The group by list, if any
   * @param fromList      The from list, if any
   *
   * @return The generated ProjectRestrictNode atop the original FromTable.
   *
   * @exception StandardException    Thrown on error
   */

  public ResultSetNode preprocess(int numTables,
                  GroupByList gbl,
                  FromList fromList)
                throws StandardException
  {
    leftResultSet = leftResultSet.preprocess(numTables, gbl, fromList);
    /* If leftResultSet is a FromSubquery, then we must explicitly extract
     * out the subquery (flatten it).  (SelectNodes have their own
     * method of flattening them.
     */
    if (leftResultSet instanceof FromSubquery)
    {
      leftResultSet = ((FromSubquery) leftResultSet).extractSubquery(numTables);
    }
    rightResultSet = rightResultSet.preprocess(numTables, gbl, fromList);
    /* If rightResultSet is a FromSubquery, then we must explicitly extract
     * out the subquery (flatten it).  (SelectNodes have their own
     * method of flattening them.
     */
    if (rightResultSet instanceof FromSubquery)
    {
      rightResultSet = ((FromSubquery) rightResultSet).extractSubquery(numTables);
    }

    /* Build the referenced table map (left || right) */
    referencedTableMap = (JBitSet) leftResultSet.getReferencedTableMap().clone();
    referencedTableMap.or((JBitSet) rightResultSet.getReferencedTableMap());
    referencedTableMap.set(tableNumber);

    /* Only generate a PRN if this node is not a flattenable join node. */
    if (isFlattenableJoinNode())
    {
      return this;
    }
    else
    {
      /* Project out any unreferenced RCs before we generate the PRN.
       * NOTE: We have to do this at the end of preprocess since it has to
       * be from the bottom up.  We can't do it until the join expression is
       * bound, since the join expression may contain column references that
       * are not referenced anywhere else above us.
       */
            projectResultColumns();
      return genProjectRestrict(numTables);
    }
  }

    /**
     * Find the unreferenced result columns and project them out. This is used in pre-processing joins
     * that are not flattened into the where clause.
     */
    void projectResultColumns() throws StandardException
    {
        resultColumns.doProjection();
    }
   
    /**
     * Set the referenced columns in the column list if it may not be correct.
     */
    void setReferencedColumns()
    {}
   
  /**
   * Optimize a TableOperatorNode.
   *
   * @param dataDictionary  The DataDictionary to use for optimization
   * @param predicateList    The PredicateList to apply.
   *
   * @return  ResultSetNode  The top of the optimized query tree
   *
   * @exception StandardException    Thrown on error
   */
  public ResultSetNode optimize(DataDictionary dataDictionary,
                  PredicateList predicateList,
                  double outerRows)
        throws StandardException
  {
    /* Get an optimizer, so we can get a cost structure */
    Optimizer optimizer =
              getOptimizer(
                (FromList) getNodeFactory().getNode(
                  C_NodeTypes.FROM_LIST,
                  getNodeFactory().doJoinOrderOptimization(),
                  this,
                  getContextManager()),
                  predicateList,
                  dataDictionary,
                  (RequiredRowOrdering) null);

    costEstimate = optimizer.newCostEstimate();

    /* RESOLVE: This is just a stub for now */
    leftResultSet = leftResultSet.optimize(
                      dataDictionary,
                      predicateList,
                      outerRows);
    rightResultSet = rightResultSet.optimize(
                      dataDictionary,
                      predicateList,
                      outerRows);

    /* The cost is the sum of the two child costs */
    costEstimate.setCost(leftResultSet.getCostEstimate().getEstimatedCost(),
               leftResultSet.getCostEstimate().rowCount(),
               leftResultSet.getCostEstimate().singleScanRowCount() +
               rightResultSet.getCostEstimate().singleScanRowCount());

    costEstimate.add(rightResultSet.costEstimate, costEstimate);

    return this;
  }

  /**
   * @see ResultSetNode#modifyAccessPaths
   *
   * @exception StandardException    Thrown on error
   */
  public ResultSetNode modifyAccessPaths() throws StandardException
  {
    /* Beetle 4454 - union all with another union all would modify access
     * paths twice causing NullPointerException, make sure we don't
     * do this again, if we have already done it in modifyAccessPaths(outertables)
     */
    if (!leftModifyAccessPathsDone)
    {
      if (leftOptimizer != null)
        leftOptimizer.modifyAccessPaths();
      else
        leftResultSet = leftResultSet.modifyAccessPaths();
    }
    if (!rightModifyAccessPathsDone)
    {
      if (rightOptimizer != null)
        rightOptimizer.modifyAccessPaths();
      else
        rightResultSet = rightResultSet.modifyAccessPaths();
    }
    return this;
  }

  /**
   * Search to see if a query references the specifed table name.
   *
   * @param name    Table name (String) to search for.
   * @param baseTable  Whether or not name is for a base table
   *
   * @return  true if found, else false
   *
   * @exception StandardException    Thrown on error
   */
  public boolean referencesTarget(String name, boolean baseTable)
    throws StandardException
  {
    return leftResultSet.referencesTarget(name, baseTable) ||
         rightResultSet.referencesTarget(name, baseTable);
  }

  /**
   * Return true if the node references SESSION schema tables (temporary or permanent)
   *
   * @return  true if references SESSION schema tables, else false
   *
   * @exception StandardException    Thrown on error
   */
  public boolean referencesSessionSchema()
    throws StandardException
  {
    return leftResultSet.referencesSessionSchema() ||
         rightResultSet.referencesSessionSchema();
  }

  /**
   * Optimize a source result set to this table operator.
   *
   * @exception StandardException    Thrown on error
   */
  protected ResultSetNode optimizeSource(
              Optimizer optimizer,
              ResultSetNode sourceResultSet,
              PredicateList predList,
              CostEstimate outerCost)
      throws StandardException
  {
    ResultSetNode  retval;

    if (sourceResultSet instanceof FromTable)
    {
      FromList optList = (FromList) getNodeFactory().getNode(
                  C_NodeTypes.FROM_LIST,
                  getNodeFactory().doJoinOrderOptimization(),
                  sourceResultSet,
                  getContextManager());

      /* If there is no predicate list, create an empty one */
      if (predList == null)
        predList = (PredicateList) getNodeFactory().getNode(
                        C_NodeTypes.PREDICATE_LIST,
                        getContextManager());

      LanguageConnectionContext lcc = getLanguageConnectionContext();
      OptimizerFactory optimizerFactory = lcc.getOptimizerFactory();
      optimizer = optimizerFactory.getOptimizer(optList,
                          predList,
                          getDataDictionary(),
                          (RequiredRowOrdering) null,
                          getCompilerContext().getNumTables(),
                            lcc);

      if (sourceResultSet == leftResultSet)
      {
        leftOptimizer = optimizer;
      }
      else if (sourceResultSet == rightResultSet)
      {
        rightOptimizer = optimizer;
      }
      else
      {
        if (SanityManager.DEBUG)
          SanityManager.THROWASSERT("Result set being optimized is neither left nor right");
      }
     
      /*
      ** Set the estimated number of outer rows from the outer part of
      ** the plan.
      */
      optimizer.setOuterRows(outerCost.rowCount());

      /* Optimize the underlying result set */
      while (optimizer.getNextPermutation())
      {
        while (optimizer.getNextDecoratedPermutation())
        {
          optimizer.costPermutation();
        }
      }

      retval = sourceResultSet;
    }
    else
    {
      retval = sourceResultSet.optimize(
                    optimizer.getDataDictionary(),
                    predList,
                    outerCost.rowCount());
    }

    return retval;
  }

  /**
   * Decrement (query block) level (0-based) for
   * all of the tables in this ResultSet tree.
   * This is useful when flattening a subquery.
   *
   * @param decrement  The amount to decrement by.
   *
   * @return Nothing;
   */
  void decrementLevel(int decrement)
  {
    leftResultSet.decrementLevel(decrement);
    rightResultSet.decrementLevel(decrement);
  }

  /**
   * Replace any DEFAULTs with the associated tree for the default.
   *
   * @param ttd  The TableDescriptor for the target table.
   * @param tcl  The RCL for the target table.
   *
   * @exception StandardException    Thrown on error
   */
  void replaceDefaults(TableDescriptor ttd, ResultColumnList tcl)
    throws StandardException
  {
    leftResultSet.replaceDefaults(ttd, tcl);
    rightResultSet.replaceDefaults(ttd, tcl);
  }

  /**
   * Notify the underlying result set tree that the result is
   * ordering dependent.  (For example, no bulk fetch on an index
   * if under an IndexRowToBaseRow.)
   *
   * @return Nothing.
   */
  void markOrderingDependent()
  {
    leftResultSet.markOrderingDependent();
    rightResultSet.markOrderingDependent();
  }

  /**
   * Accept a visitor, and call v.visit()
   * on child nodes as necessary. 
   *
   * @param v the visitor
   *
   * @exception StandardException on error
   */
  public Visitable accept(Visitor v)
    throws StandardException
  {
    if (v.skipChildren(this))
    {
      return v.visit(this);
    }

    Visitable returnNode = super.accept(v);

    if (leftResultSet != null && !v.stopTraversal())
    {
      leftResultSet = (ResultSetNode)leftResultSet.accept(v);
    }
    if (rightResultSet != null && !v.stopTraversal())
    {
      rightResultSet = (ResultSetNode)rightResultSet.accept(v);
    }
    return returnNode;
  }

  /**
   * apparently something special needs to be done for me....
   */
  public boolean needsSpecialRCLBinding()
  {
    return true;
  }
}
TOP

Related Classes of org.apache.derby.impl.sql.compile.TableOperatorNode

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.