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

   Licensed to the Apache Software Foundation (ASF) under one or more
   contributor license agreements.  See the NOTICE file distributed with
   this work for additional information regarding copyright ownership.
   The ASF licenses this file to you 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".
*
*/

abstract class TableOperatorNode extends FromTable
{
  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)
      {
        /* We know leftOptimizer's list of Optimizables consists of
         * exactly one Optimizable, and we know that the Optimizable
         * is actually leftResultSet (see optimizeSource() of this
         * class). That said, the following call to modifyAccessPaths()
         * will effectively replace leftResultSet as it exists in
         * leftOptimizer's list with a "modified" node that *may* be
         * different from the original leftResultSet--for example, it
         * could be a new DISTINCT node whose child is the original
         * leftResultSet.  So after we've modified the node's access
         * path(s) we have to explicitly set this.leftResulSet to
         * point to the modified node. Otherwise leftResultSet would
         * continue to point to the node as it existed *before* it was
         * modified, and that could lead to incorrect behavior for
         * certain queries.  DERBY-1852.
         */
        leftOptimizer.modifyAccessPaths();
        leftResultSet = (ResultSetNode)
          ((OptimizerImpl)leftOptimizer)
            .optimizableList.getOptimizable(0);
      }
      else
      {
        leftResultSet =
          (ResultSetNode)
            ((FromTable) leftResultSet).modifyAccessPath(outerTables);
      }
      leftModifyAccessPathsDone = true;
    }
    else
    {
      callModifyAccessPaths = true;
    }

    if (rightResultSet instanceof FromTable)
    {
      if (rightOptimizer != null)
      {
        /* For the same reasons outlined above we need to make sure
         * we set rightResultSet to point to the *modified* right result
         * set node, which sits at position "0" in rightOptimizer's
         * list.
         */
        rightOptimizer.modifyAccessPaths();
        rightResultSet = (ResultSetNode)
          ((OptimizerImpl)rightOptimizer)
            .optimizableList.getOptimizable(0);
      }
      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);
  }

  /**
   * @see Optimizable#updateBestPlanMap
   *
   * Makes a call to add/load/remove the plan mapping for this node,
   * and then makes the necessary call to recurse on this node's
   * left and right child, in order to ensure that we've handled
   * the full plan all the way down this node's subtree.
   */
  public void updateBestPlanMap(short action,
    Object planKey) throws StandardException
  {
    super.updateBestPlanMap(action, planKey);

    // Now walk the children.  Note that if either child is not
    // an Optimizable and the call to child.getOptimizerImpl()
    // returns null, then that means we haven't tried to optimize
    // the child yet.  So in that case there's nothing to
    // add/load.

    if (leftResultSet instanceof Optimizable)
    {
      ((Optimizable)leftResultSet).
        updateBestPlanMap(action, planKey);
    }
    else if (leftResultSet.getOptimizerImpl() != null)
    {
      leftResultSet.getOptimizerImpl().
        updateBestPlanMaps(action, planKey);
    }

    if (rightResultSet instanceof Optimizable)
    {
      ((Optimizable)rightResultSet).
        updateBestPlanMap(action, planKey);
    }
    else if (rightResultSet.getOptimizerImpl() != null)
    {
      rightResultSet.getOptimizerImpl().
        updateBestPlanMaps(action, planKey);
    }
  }

  /**
   * 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: " + false + "\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
   */

  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.
   */
  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.)
   * The resulting state of this cal was never used so its
   * field was removed to save runtimespace for this node.
   * Further cleanup can be done including parser changes
   * if this call is really nor required.
   *
   * @param nestedInParens  Whether or not this node is nested in parens.
   */
  public void setNestedInParens(boolean 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.
   *
   * @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.
   *
   * @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.
   *
   * @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.
   *
   * @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.
   *
   * @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)
      {
        /* We know leftOptimizer's list of Optimizables consists of
         * exactly one Optimizable, and we know that the Optimizable
         * is actually leftResultSet (see optimizeSource() of this
         * class). That said, the following call to modifyAccessPaths()
         * will effectively replace leftResultSet as it exists in
         * leftOptimizer's list with a "modified" node that *may* be
         * different from the original leftResultSet--for example, it
         * could be a new DISTINCT node whose child is the original
         * leftResultSet.  So after we've modified the node's access
         * path(s) we have to explicitly set this.leftResulSet to
         * point to the modified node. Otherwise leftResultSet would
         * continue to point to the node as it existed *before* it was
         * modified, and that could lead to incorrect behavior for
         * certain queries.  DERBY-1852.
         */
        leftOptimizer.modifyAccessPaths();
        leftResultSet = (ResultSetNode)
          ((OptimizerImpl)leftOptimizer)
            .optimizableList.getOptimizable(0);
      }
      else
      {
        // If this is a SetOperatorNode then we may have pushed
        // predicates down to the children.  If that's the case
        // then we need to pass those predicates down as part
        // of the modifyAccessPaths call so that they can be
        // pushed one last time, in prep for generation.
        if (this instanceof SetOperatorNode)
        {
          SetOperatorNode setOp = (SetOperatorNode)this;
          leftResultSet = leftResultSet.modifyAccessPaths(
            setOp.getLeftOptPredicateList());
        }
        else
          leftResultSet = leftResultSet.modifyAccessPaths();
      }
    }
    if (!rightModifyAccessPathsDone)
    {
      if (rightOptimizer != null)
      {
        /* For the same reasons outlined above we need to make sure
         * we set rightResultSet to point to the *modified* right result
         * set node, which sits at position "0" in rightOptimizer's
         * list.
         */
        rightOptimizer.modifyAccessPaths();
        rightResultSet = (ResultSetNode)
          ((OptimizerImpl)rightOptimizer)
            .optimizableList.getOptimizable(0);
      }
      else
      {
        if (this instanceof SetOperatorNode) {
          SetOperatorNode setOp = (SetOperatorNode)this;
          rightResultSet = rightResultSet.modifyAccessPaths(
            setOp.getRightOptPredicateList());
        }
        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);
      optimizer.prepForNextRound();

      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.
   */
  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);
  }

  /**
   * @see ResultSetNode#adjustForSortElimination
   */
  void adjustForSortElimination()
  {
    leftResultSet.adjustForSortElimination();
    rightResultSet.adjustForSortElimination();
  }

  /**
   * 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.