Package org.teiid.query.optimizer.relational.rules

Source Code of org.teiid.query.optimizer.relational.rules.NewCalculateCostUtil$DependentCostAnalysis

/*
* JBoss, Home of Professional Open Source.
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.  Some portions may be licensed
* to Red Hat, Inc. under one or more contributor license agreements.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/

package org.teiid.query.optimizer.relational.rules;

import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import org.teiid.api.exception.query.QueryMetadataException;
import org.teiid.api.exception.query.QueryPlannerException;
import org.teiid.common.buffer.BufferManager;
import org.teiid.core.TeiidComponentException;
import org.teiid.core.types.DataTypeManager;
import org.teiid.logging.LogConstants;
import org.teiid.logging.LogManager;
import org.teiid.query.QueryPlugin;
import org.teiid.query.metadata.QueryMetadataInterface;
import org.teiid.query.optimizer.capabilities.CapabilitiesFinder;
import org.teiid.query.optimizer.relational.RelationalPlanner;
import org.teiid.query.optimizer.relational.plantree.NodeConstants;
import org.teiid.query.optimizer.relational.plantree.NodeEditor;
import org.teiid.query.optimizer.relational.plantree.PlanNode;
import org.teiid.query.optimizer.relational.plantree.NodeConstants.Info;
import org.teiid.query.resolver.util.ResolverUtil;
import org.teiid.query.sql.lang.AbstractSetCriteria;
import org.teiid.query.sql.lang.CompareCriteria;
import org.teiid.query.sql.lang.CompoundCriteria;
import org.teiid.query.sql.lang.Criteria;
import org.teiid.query.sql.lang.DependentSetCriteria;
import org.teiid.query.sql.lang.IsNullCriteria;
import org.teiid.query.sql.lang.JoinType;
import org.teiid.query.sql.lang.MatchCriteria;
import org.teiid.query.sql.lang.NotCriteria;
import org.teiid.query.sql.lang.PredicateCriteria;
import org.teiid.query.sql.lang.SetCriteria;
import org.teiid.query.sql.lang.SetQuery;
import org.teiid.query.sql.lang.SubquerySetCriteria;
import org.teiid.query.sql.lang.SetQuery.Operation;
import org.teiid.query.sql.symbol.Constant;
import org.teiid.query.sql.symbol.ElementSymbol;
import org.teiid.query.sql.symbol.Expression;
import org.teiid.query.sql.symbol.GroupSymbol;
import org.teiid.query.sql.symbol.SingleElementSymbol;
import org.teiid.query.sql.util.SymbolMap;
import org.teiid.query.sql.visitor.AggregateSymbolCollectorVisitor;
import org.teiid.query.sql.visitor.ElementCollectorVisitor;
import org.teiid.query.sql.visitor.EvaluatableVisitor;
import org.teiid.query.sql.visitor.GroupsUsedByElementsVisitor;
import org.teiid.query.util.CommandContext;

/**
* @since 4.3
*/
public class NewCalculateCostUtil {

    public static final int UNKNOWN_JOIN_SCALING = 20;
  public static final float UNKNOWN_VALUE = -1;
   
    // the following variables are used to hold cost estimates (roughly in milliseconds)
    private final static float compareTime = .0001f; //TODO: a better estimate would be based upon the number of conjuncts
    private final static float readTime = .001f; //TODO: should come from the connector
    private final static float procNewRequestTime = 1; //TODO: should come from the connector
   
    enum Stat {
      NDV,
      NNV
    }
   
    public static class DependentCostAnalysis {
      Float[] maxNdv;
      Float[] expectedNdv;
      Float expectedCardinality;
    }

    @SuppressWarnings("serial")
  private static class ColStats extends LinkedHashMap<Expression, float[]> {
      @Override
      public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append('{');
       
        int j = 0;
        for (Iterator<Entry<Expression, float[]>> i = this.entrySet().iterator(); i.hasNext();) {
            Entry<Expression, float[]> e = i.next();
            sb.append(e.getKey());
            sb.append('=');
            sb.append(Arrays.toString(e.getValue()));
            j++;
            if (i.hasNext()) {
              sb.append(", "); //$NON-NLS-1$
              if (j > 3) {
                  sb.append("..."); //$NON-NLS-1$
                  break;
                }
            }
        }
        return sb.append('}').toString();
      }
    }
       
    /**
     * Calculate cost of a node and all children, recursively from the bottom up.
     * @param node
     * @param metadata
     * @return Cost computed at the passed node
     * @throws QueryMetadataException
     * @throws TeiidComponentException
     */
    static float computeCostForTree(PlanNode node, QueryMetadataInterface metadata)
        throws QueryMetadataException, TeiidComponentException {

      updateCardinality(node, metadata);
       
        return node.getCardinality();
    }
   
    static boolean updateCardinality(PlanNode node, QueryMetadataInterface metadata) throws QueryMetadataException, TeiidComponentException {
      Float cost = (Float) node.getProperty(NodeConstants.Info.EST_CARDINALITY);

      // check if already computed
      boolean updated = false;
      for (PlanNode child : node.getChildren()) {
        updated |= updateCardinality(child, metadata);
        }
        if(cost == null || updated) {
            computeNodeCost(node, metadata);
            return true;
        }
        return false;
    }
  
    /**
     * This method attempts to estimate a cost for each type of node.
     * @param node
     * @param metadata
     * @throws QueryMetadataException
     * @throws TeiidComponentException
     */
    private static void computeNodeCost(PlanNode node, QueryMetadataInterface metadata)
        throws QueryMetadataException, TeiidComponentException {
       
        switch(node.getType()) {
            case NodeConstants.Types.SOURCE:
                estimateSourceNodeCost(node, metadata);
                break;

            case NodeConstants.Types.SELECT:
                estimateSelectNodeCost(node, metadata);
                break;
                          
            case NodeConstants.Types.JOIN:
                estimateJoinNodeCost(node, metadata);
                break;

            case NodeConstants.Types.DUP_REMOVE:
                estimateNodeCost(node, FrameUtil.findTopCols(node), metadata);
                break;

            case NodeConstants.Types.GROUP:
              if (!node.hasCollectionProperty(NodeConstants.Info.GROUP_COLS)) {
                setCardinalityEstimate(node, 1f, true, metadata);
              } else {
                estimateNodeCost(node, (List)node.getProperty(NodeConstants.Info.GROUP_COLS), metadata);
              }
                break;
            case NodeConstants.Types.ACCESS:
            case NodeConstants.Types.SORT:
            {
                //Simply record the cost of the only child
                PlanNode child = node.getFirstChild();
                Float childCost = (Float)child.getProperty(NodeConstants.Info.EST_CARDINALITY);
                setCardinalityEstimate(node, childCost, true, metadata);
                break;
            }
            case NodeConstants.Types.NULL:
                setCardinalityEstimate(node, 0f, true, metadata);
                break;

            case NodeConstants.Types.PROJECT:
            {
                Float childCost = null;
                //Simply record the cost of the only child
                if (node.getChildCount() != 0) {
                    PlanNode child = node.getFirstChild();
                    childCost = (Float)child.getProperty(NodeConstants.Info.EST_CARDINALITY);
                } else {
                    childCost = 1f;
                }
                setCardinalityEstimate(node, childCost, true, metadata);
                break;
            }
            case NodeConstants.Types.SET_OP:
            {
                estimateSetOpCost(node, metadata);
                break;
            }
            case NodeConstants.Types.TUPLE_LIMIT:
            {
                PlanNode child = node.getFirstChild();
                float childCost = child.getCardinality();
               
                Expression offset = (Expression)node.getProperty(NodeConstants.Info.OFFSET_TUPLE_COUNT);
                Float cost = childCost;
               
                if (childCost != UNKNOWN_VALUE && offset instanceof Constant) {
                    float offsetCost = childCost - ((Number)((Constant)offset).getValue()).floatValue();
                    cost = new Float((offsetCost < 0) ? 0 : offsetCost);
                }
               
                Expression limit = (Expression)node.getProperty(NodeConstants.Info.MAX_TUPLE_LIMIT);
                if (limit instanceof Constant) {
                    float limitCost = ((Number)((Constant)limit).getValue()).floatValue();
                    if (cost.floatValue() != UNKNOWN_VALUE) {
                        cost = new Float(Math.min(limitCost, cost.floatValue()));
                    } else {
                        cost = new Float(limitCost);
                    }
                }
                setCardinalityEstimate(node, cost, true, metadata);
                break;
            }
        }
    }

  private static void estimateSetOpCost(PlanNode node,
      QueryMetadataInterface metadata) throws QueryMetadataException,
      TeiidComponentException {
    float cost = 0;
   
    SetQuery.Operation op = (SetQuery.Operation)node.getProperty(NodeConstants.Info.SET_OPERATION);

    float leftCost = (Float)node.getFirstChild().getProperty(NodeConstants.Info.EST_CARDINALITY);
    float rightCost = (Float)node.getLastChild().getProperty(NodeConstants.Info.EST_CARDINALITY);
   
    if (!node.hasBooleanProperty(NodeConstants.Info.USE_ALL)) {
      leftCost = getDistinctEstimate(node.getFirstChild(), metadata, leftCost);
      rightCost = getDistinctEstimate(node.getLastChild(), metadata, rightCost);
    }
   
    cost = getCombinedSetEstimate(op, leftCost, rightCost, !node.hasBooleanProperty(NodeConstants.Info.USE_ALL));
   
    setCardinalityEstimate(node, new Float(cost), true, metadata);
  }

  private static float getCombinedSetEstimate(SetQuery.Operation op, float leftCost, float rightCost, boolean distinct) {
    float cost;
    cost = leftCost;
   
    switch (op) {
    case EXCEPT:
      if (leftCost != UNKNOWN_VALUE && rightCost != UNKNOWN_VALUE) {
          cost = Math.max(1, leftCost - .5f * rightCost);
        }
      break;
    case INTERSECT:
      if (rightCost != UNKNOWN_VALUE) {
        if (leftCost != UNKNOWN_VALUE) {
            cost = .5f * Math.min(leftCost, rightCost);
        } else {
          cost = rightCost;
        }
      }
      break;
    default: //union
      if (leftCost != UNKNOWN_VALUE && rightCost != UNKNOWN_VALUE) {
            if (distinct) {
              cost = Math.max(leftCost, rightCost) + .5f * Math.min(leftCost, rightCost);
            } else {
                cost = rightCost + leftCost;
            }
      }
      break;
    }
    return cost;
  }

  private static float getDistinctEstimate(PlanNode node,
      QueryMetadataInterface metadata, float cost)
      throws QueryMetadataException, TeiidComponentException {
    PlanNode projectNode = NodeEditor.findNodePreOrder(node, NodeConstants.Types.PROJECT);
    if (projectNode != null) {
      cost = getNDVEstimate(node.getParent(), metadata, cost, (List)projectNode.getProperty(NodeConstants.Info.PROJECT_COLS), false);
    }
    return cost;
  }

    private static void setCardinalityEstimate(PlanNode node, Float bestEstimate, boolean setColEstimates, QueryMetadataInterface metadata) throws QueryMetadataException, TeiidComponentException {
        if (bestEstimate == null){
          bestEstimate = Float.valueOf(UNKNOWN_VALUE);
        }
        Float lastEstimate = (Float)node.setProperty(NodeConstants.Info.EST_CARDINALITY, bestEstimate);
        if (node.getParent() != null && (lastEstimate == null || !lastEstimate.equals(bestEstimate))) {
          node.getParent().setProperty(Info.EST_CARDINALITY, null);
        }
        if (setColEstimates) {
          setColStatEstimates(node, bestEstimate, metadata);
        }
    }

    /**
     * Method estimateJoinNodeCost.
     * @param node
     * @param metadata
     */
    private static void estimateJoinNodeCost(PlanNode node, QueryMetadataInterface metadata)
        throws QueryMetadataException, TeiidComponentException {

        Iterator<PlanNode> children = node.getChildren().iterator();
        PlanNode child1 = children.next();
        float childCost1 = child1.getCardinality();
        PlanNode child2 = children.next();
        float childCost2 = child2.getCardinality();
       
        if (childCost1 == UNKNOWN_VALUE || childCost2 == UNKNOWN_VALUE) {
          setCardinalityEstimate(node, null, true, metadata);
          return;
        }

        JoinType joinType = (JoinType)node.getProperty(NodeConstants.Info.JOIN_TYPE);
        List joinCriteria = (List) node.getProperty(NodeConstants.Info.JOIN_CRITERIA);
       
        float baseCost = childCost1 * childCost2;

        if (joinCriteria != null && !joinCriteria.isEmpty()) {
        Criteria crit = Criteria.combineCriteria(joinCriteria);
        //TODO: we may be able to get a fairly accurate join estimate if the
        //unknown side is being joined with a key
          baseCost = recursiveEstimateCostOfCriteria(baseCost, node, crit, metadata);
        }
       
        Float cost = null;
        if (JoinType.JOIN_CROSS.equals(joinType) || JoinType.JOIN_INNER.equals(joinType)){
            cost = baseCost;
        } else if (JoinType.JOIN_FULL_OUTER.equals(joinType)) {
            cost = Math.max((childCost1+childCost2),baseCost);
        } else if (JoinType.JOIN_LEFT_OUTER.equals(joinType)) {
            cost = Math.max(childCost1,baseCost);
        } else if (JoinType.JOIN_SEMI.equals(joinType) || JoinType.JOIN_ANTI_SEMI.equals(joinType)) {
          cost = Math.min(childCost1, baseCost);
        }
       
        setCardinalityEstimate(node, cost, true, metadata);
    }

    /**
     * Estimate the cost of a selection.  This is not easy to do without information
     * about the value count for each relation attribute. 
     * @param metadata
     */
    private static void estimateSelectNodeCost(PlanNode node, QueryMetadataInterface metadata)
        throws QueryMetadataException, TeiidComponentException {

        PlanNode child = node.getFirstChild();
        float childCost = child.getCardinality();
       
        //Get list of conjuncts
        Criteria selectCriteria = (Criteria)node.getProperty(NodeConstants.Info.SELECT_CRITERIA);
        float newCost = recursiveEstimateCostOfCriteria(childCost, node, selectCriteria, metadata);
        setCardinalityEstimate(node, newCost, true, metadata);
    }
   
    private static void setColStatEstimates(PlanNode node, float cardinality, QueryMetadataInterface metadata) throws QueryMetadataException, TeiidComponentException {
      if (cardinality == UNKNOWN_VALUE) {
        return;
      }
      ColStats colStats = null;
      ColStats colStatsOther = null;
      float childCardinality = UNKNOWN_VALUE;
      if (node.getChildCount() > 0) {
        childCardinality = node.getFirstChild().getCardinality();
        colStats = (ColStats) node.getFirstChild().getProperty(Info.EST_COL_STATS);
      }
      float otherChildCardinality = UNKNOWN_VALUE;
      List<? extends Expression> outputColsOther = null;
      if (node.getChildCount() > 1) {
        otherChildCardinality = node.getLastChild().getCardinality();
        colStatsOther = (ColStats) node.getLastChild().getProperty(Info.EST_COL_STATS);
        outputColsOther = getOutputCols(node.getLastChild(), metadata);
      }
      SetQuery.Operation setOp = (Operation) node.getProperty(Info.SET_OPERATION);
      List<? extends Expression> outputCols = getOutputCols(node, metadata);
      ColStats newColStats = new ColStats();
      for (int i = 0; i < outputCols.size(); i++) {
        Expression expr = outputCols.get(i);
        float[] newStats = new float[2];
        Arrays.fill(newStats, UNKNOWN_VALUE);
        if (childCardinality == UNKNOWN_VALUE || (setOp != null && (colStats == null || colStatsOther == null))) {
          //base case - cannot determine, just assume unique rows
            newStats[Stat.NDV.ordinal()] = cardinality;
            newStats[Stat.NNV.ordinal()] = 0;
        } else if (setOp != null) {
          //set op
        float[] stats = colStats.get(expr);
        float[] statsOther = colStatsOther.get(outputColsOther.get(i));
        newStats[Stat.NDV.ordinal()] = getCombinedSetEstimate(setOp, stats[Stat.NDV.ordinal()], statsOther[Stat.NDV.ordinal()], true);
            newStats[Stat.NNV.ordinal()] = getCombinedSetEstimate(setOp, stats[Stat.NNV.ordinal()], statsOther[Stat.NNV.ordinal()], !node.hasBooleanProperty(NodeConstants.Info.USE_ALL));
        } else {
          //all other cases - join is the only multi-node case here
          float[] stats = null;
          float origCardinality = childCardinality;
          if (colStats != null) {
            stats = colStats.get(expr);
          }
          if (stats == null && colStatsOther != null) {
            origCardinality = otherChildCardinality;
            stats = colStatsOther.get(expr);
          }
          if (stats == null) {
              if (node.getType() == NodeConstants.Types.PROJECT) {
                Collection<SingleElementSymbol> elems = new HashSet<SingleElementSymbol>();
                AggregateSymbolCollectorVisitor.getAggregates(expr, elems, elems);
                newStats[Stat.NDV.ordinal()] = getStat(Stat.NDV, elems, node, childCardinality, metadata);
                newStats[Stat.NNV.ordinal()] = getStat(Stat.NNV, elems, node, childCardinality, metadata);
              } else {
                //TODO: use a better estimate for new aggs
                if (node.hasProperty(Info.GROUP_COLS)) {
                    newStats[Stat.NDV.ordinal()] = cardinality / 3;
                } else {
                  newStats[Stat.NDV.ordinal()] = cardinality;
                }
                    newStats[Stat.NNV.ordinal()] = UNKNOWN_VALUE;
              }
            } else {
              if (node.getType() == NodeConstants.Types.DUP_REMOVE || node.getType() == NodeConstants.Types.GROUP) {
                //don't scale down
                newStats[Stat.NDV.ordinal()] = stats[Stat.NDV.ordinal()];
              } else if (stats[Stat.NDV.ordinal()] != UNKNOWN_VALUE) {
              newStats[Stat.NDV.ordinal()] = stats[Stat.NDV.ordinal()]*Math.min(1, cardinality/origCardinality);
              newStats[Stat.NDV.ordinal()] = Math.max(1, newStats[Stat.NDV.ordinal()]);
              }
            if (stats[Stat.NNV.ordinal()] != UNKNOWN_VALUE) {
              //TODO: this is an under estimate for the inner side of outer joins
                newStats[Stat.NNV.ordinal()] = stats[Stat.NNV.ordinal()]*Math.min(1, cardinality/origCardinality);
                newStats[Stat.NNV.ordinal()] = Math.max(1, newStats[Stat.NNV.ordinal()]);
            }
            }
        }
        newColStats.put(expr, newStats);
    }
      node.setProperty(Info.EST_COL_STATS, newColStats);
    }

    /**
     * For a source node, the cost is basically the cardinality of the source
     * (if it is known).
     * @param node
     * @param metadata
     * @throws QueryMetadataException
     * @throws TeiidComponentException
     */
    private static void estimateSourceNodeCost(PlanNode node, QueryMetadataInterface metadata)
        throws QueryMetadataException, TeiidComponentException {
       
        float cost = UNKNOWN_VALUE;
        if(node.getChildCount() > 0) {
          SymbolMap references = (SymbolMap)node.getProperty(NodeConstants.Info.CORRELATED_REFERENCES);
          //only cost non-correlated TODO: a better estimate for correlated
          if (references == null) {
              PlanNode child = node.getFirstChild();
              cost = child.getCardinality();
              SymbolMap symbolMap = (SymbolMap)node.getProperty(NodeConstants.Info.SYMBOL_MAP);
              if (symbolMap != null) {
                ColStats colStats = (ColStats) child.getProperty(Info.EST_COL_STATS);
                if (colStats != null) {
                  List<? extends Expression> outputCols = getOutputCols(node, metadata);
                  ColStats newColStats = new ColStats();
                  for (Expression expr : outputCols) {
                    if (!(expr instanceof ElementSymbol)) {
                      continue;
                    }
                    ElementSymbol es = (ElementSymbol)expr;
                    Expression ex = symbolMap.getMappedExpression(es);
              newColStats.put(es, colStats.get(ex));
            }
                node.setProperty(Info.EST_COL_STATS, newColStats);
                } else {
                  colStats = createColStats(node, metadata, cost);
                node.setProperty(Info.EST_COL_STATS, colStats);
                }
              }
          }
        }else {
            GroupSymbol group = node.getGroups().iterator().next();
            float cardinality = metadata.getCardinality(group.getMetadataID());
            if (cardinality <= QueryMetadataInterface.UNKNOWN_CARDINALITY){
                cardinality = UNKNOWN_VALUE;
            }
            cost = cardinality;
            if (!node.hasProperty(Info.ATOMIC_REQUEST)) {
              ColStats colStats = createColStats(node, metadata, cost);
          node.setProperty(Info.EST_COL_STATS, colStats);
            }
        }
       
        setCardinalityEstimate(node, new Float(cost), false, metadata);
    }

  private static ColStats createColStats(PlanNode node,
      QueryMetadataInterface metadata, float cardinality)
      throws QueryMetadataException, TeiidComponentException {
    ColStats colStats = new ColStats();
    List<? extends Expression> outputCols = getOutputCols(node, metadata);
    for (Expression expr : outputCols) {
      if (!(expr instanceof ElementSymbol)) {
        continue;
      }
      ElementSymbol es = (ElementSymbol)expr;
      float[] vals = new float[2];
      float ndv = metadata.getDistinctValues(es.getMetadataID());
      float nnv = metadata.getNullValues(es.getMetadataID());
      if (cardinality != UNKNOWN_VALUE) {
              int groupCardinality = metadata.getCardinality(es.getGroupSymbol().getMetadataID());
              if (groupCardinality != UNKNOWN_VALUE && groupCardinality > cardinality) {
                if (ndv != UNKNOWN_VALUE) {
                  ndv *= cardinality / Math.max(1, groupCardinality);
                  ndv = Math.max(ndv, 1);
                }
                if (nnv != UNKNOWN_VALUE) {
                  nnv *= cardinality / Math.max(1, groupCardinality);
                  nnv = Math.max(nnv, 1);
                }
              }
      }
      vals[Stat.NDV.ordinal()] = ndv;
      vals[Stat.NNV.ordinal()] = nnv;
      colStats.put(es, vals);
    }
    return colStats;
  }

  private static List<? extends Expression> getOutputCols(PlanNode node,
      QueryMetadataInterface metadata) throws QueryMetadataException,
      TeiidComponentException {
    List<Expression> outputCols =(List<Expression>)node.getProperty(Info.OUTPUT_COLS);
    if (outputCols != null) {
      return outputCols;
    }
    PlanNode projectNode = NodeEditor.findNodePreOrder(node,
        NodeConstants.Types.PROJECT | NodeConstants.Types.GROUP
        | NodeConstants.Types.SOURCE | NodeConstants.Types.JOIN
        | NodeConstants.Types.NULL);
    if (projectNode != null) {
      node = projectNode;
    }
   
    if (node.getType() == NodeConstants.Types.PROJECT) {
      return (List<? extends Expression>) node.getProperty(NodeConstants.Info.PROJECT_COLS);
    } else if (node.getType() == NodeConstants.Types.GROUP) {
      LinkedList<Expression> result = new LinkedList<Expression>(RulePushAggregates.collectAggregates(node));
      result.addAll((Collection<? extends Expression>) node.getProperty(Info.GROUP_COLS));
      return result;
    }
    LinkedList<ElementSymbol> elements = new LinkedList<ElementSymbol>();
    for (GroupSymbol group : node.getGroups()) {
      elements.addAll(ResolverUtil.resolveElementsInGroup(group, metadata));
    }
    return elements;
  }
   
    /**
     * For a Group or Dup Removal node, the cost is basically the smaller of the largest NDV of the
     * selected columns and cost of the child node (if it is known).
     * @param node
     * @param metadata
     * @throws QueryMetadataException
     * @throws TeiidComponentException
     */
    private static void estimateNodeCost(PlanNode node, List expressions, QueryMetadataInterface metadata)
        throws QueryMetadataException, TeiidComponentException {
       
        PlanNode child = node.getFirstChild();
        float childCost = child.getCardinality();
       
        if(childCost == UNKNOWN_VALUE) {
            setCardinalityEstimate(node, null, true, metadata);
            return;
        }

        float cardinality = getNDVEstimate(node, metadata, childCost, expressions, true);
        setCardinalityEstimate(node, cardinality, true, metadata);
    }

    static float getStat(Stat stat, Collection<? extends Expression> elems, PlanNode node,
        float cardinality, QueryMetadataInterface metadata) throws QueryMetadataException, TeiidComponentException {
        float result = 1;
        int branch = 0;
        boolean branchFound = false;
        for (Expression expression : elems) {
          ColStats colStats = null;
        if (node.getChildCount() == 0) {
          colStats = createColStats(node, metadata, cardinality);
        } else {
          for (int i = branch; i < node.getChildCount(); i++) {
            PlanNode child = node.getChildren().get(i);
            colStats = (ColStats) child.getProperty(Info.EST_COL_STATS);
              if (colStats == null) {
                continue;
              }
          float[] stats = colStats.get(expression);
          if (stats != null) {
            if (node.getType() == NodeConstants.Types.SET_OP) {
              branch = i;
              branchFound = true;
            }
            break;
          }
          colStats = null;
          if (branchFound) {
            break;
          }
          }
        }
        if (colStats == null) {
          return UNKNOWN_VALUE;
        }
        float[] stats = colStats.get(expression);
      if (stats == null || stats[stat.ordinal()] == UNKNOWN_VALUE) {
        return UNKNOWN_VALUE;
      }
      result = Math.max(result, stats[stat.ordinal()]);
      }
    return result;
  }

  static float recursiveEstimateCostOfCriteria(float childCost, PlanNode currentNode, Criteria crit, QueryMetadataInterface metadata)
        throws QueryMetadataException, TeiidComponentException {
       
        float cost = childCost;
        if(crit instanceof CompoundCriteria) {
            CompoundCriteria compCrit = (CompoundCriteria) crit;
            if (compCrit.getOperator() == CompoundCriteria.OR) {
                cost = 0;
            }
            if (usesKey(compCrit, metadata)) {
                return 1;
            }
            for (Criteria critPart : compCrit.getCriteria()) {
                float nextCost = recursiveEstimateCostOfCriteria(childCost, currentNode, critPart, metadata);
               
                if(compCrit.getOperator() == CompoundCriteria.AND) {
                    if (nextCost == UNKNOWN_VALUE) {
                        continue;
                    }
                    if (childCost != UNKNOWN_VALUE) {
                        cost *= nextCost/childCost;
                    } else {
                        if (cost == UNKNOWN_VALUE) {
                            cost = nextCost;
                        } else {
                            cost = Math.min(cost, nextCost);
                        }
                    }
                    if (cost <= 1) {
                        return 1;
                    }
                } else {
                    if (nextCost == UNKNOWN_VALUE) {
                        return childCost;
                    }
                    //this assumes that all disjuncts are completely disjoint
                    cost += nextCost;
                    if (childCost != UNKNOWN_VALUE) {
                        cost = Math.min(cost, childCost);
                    }
                }
            }
            if (cost == UNKNOWN_VALUE) {
                return childCost;
            }
        } else if(crit instanceof NotCriteria){
            if (childCost == UNKNOWN_VALUE) {
                return UNKNOWN_VALUE;
            }
            float nextCost = recursiveEstimateCostOfCriteria(childCost, currentNode, ((NotCriteria)crit).getCriteria(), metadata);
            if (nextCost == UNKNOWN_VALUE){
                return childCost;
            }  
            cost -= nextCost;
        } else {
            cost = estimatePredicateCost(childCost, currentNode, (PredicateCriteria) crit, metadata);
           
            if (cost == UNKNOWN_VALUE) {
                return childCost;
            }
        } 
       
        cost = Math.max(cost, 1);
       
        return cost;
    }
   
    /**
     * This method is a helper to examine whether a compound criteria covers
     * a compound key.  A "valid" criteria is
     * 1) a predicate criteria
     *   1a) not negated
     *   1b) with an equality operator if it is a compare criteria
     * b) or a compound criteria containing valid criteria and an "AND" operator
     * @param criteria
     * @param elements Collection to collect ElementSymbols in
     * @since 4.2
     */
    private static void collectElementsOfValidCriteria(Criteria criteria, Collection<ElementSymbol> elements) {
      
        if(criteria instanceof CompoundCriteria) {
            CompoundCriteria compCrit = (CompoundCriteria) criteria;
            Iterator<Criteria> iter = compCrit.getCriteria().iterator();
            boolean first = true;
            Collection<ElementSymbol> savedElements = elements;
            if(compCrit.getOperator() == CompoundCriteria.OR) {
              elements = new HashSet<ElementSymbol>();
            }
            while(iter.hasNext()) {
              if(compCrit.getOperator() == CompoundCriteria.AND || first) {
                collectElementsOfValidCriteria(iter.next(), elements);
                first = false;
              } else {
                HashSet<ElementSymbol> other = new HashSet<ElementSymbol>();
                collectElementsOfValidCriteria(iter.next(), other);
                elements.retainAll(other);
              }
            }
            if (compCrit.getOperator() == CompoundCriteria.OR) {
              savedElements.addAll(elements);
            }
        } else if(criteria instanceof CompareCriteria) {
            CompareCriteria compCrit = (CompareCriteria)criteria;
            if (compCrit.getOperator() == CompareCriteria.EQ){
                ElementCollectorVisitor.getElements(compCrit, elements);
            }                
        } else if(criteria instanceof MatchCriteria) {
            MatchCriteria matchCriteria = (MatchCriteria)criteria;
            if (!matchCriteria.isNegated()) {
                ElementCollectorVisitor.getElements(matchCriteria, elements);
            }

        } else if(criteria instanceof AbstractSetCriteria) {
            AbstractSetCriteria setCriteria = (AbstractSetCriteria)criteria;
            if (!setCriteria.isNegated()) {
                ElementCollectorVisitor.getElements(setCriteria.getExpression(), elements);
            }
        } else if(criteria instanceof IsNullCriteria) {
            IsNullCriteria isNullCriteria = (IsNullCriteria)criteria;
            if (!isNullCriteria.isNegated()) {
                ElementCollectorVisitor.getElements(isNullCriteria.getExpression(), elements);
            }
        }       
    }
   
    /**
     * @param childCost
     * @param predicateCriteria
     * @param metadata
     * @return
     * @since 4.3
     */
    private static float estimatePredicateCost(float childCost, PlanNode currentNode, PredicateCriteria predicateCriteria, QueryMetadataInterface metadata)
        throws QueryMetadataException, TeiidComponentException {
       
        Collection<ElementSymbol> elements = ElementCollectorVisitor.getElements(predicateCriteria, true);
       
        Collection<GroupSymbol> groups = GroupsUsedByElementsVisitor.getGroups(elements);
        boolean multiGroup = groups.size() > 1;
       
        float cost = childCost;
        float ndv = getStat(Stat.NDV, elements, currentNode, childCost, metadata);
       
        boolean unknownChildCost = childCost == UNKNOWN_VALUE;
        boolean usesKey = usesKey(elements, metadata);
       
        if (childCost == UNKNOWN_VALUE) {
            childCost = 1;
        }

        if (ndv == UNKNOWN_VALUE) {
            if (multiGroup) {
                if (usesKey) {
                    ndv = (float)Math.ceil(Math.sqrt(childCost));
                } else {
                    ndv = (float)Math.ceil(Math.sqrt(childCost)/4);
                }
            } else if (usesKey) {
                ndv = childCost;
            } else {
                ndv = (float)Math.ceil(Math.sqrt(childCost)/2);
            }
            ndv = Math.max(ndv, 1);
        }
               
        boolean isNegatedPredicateCriteria = false;
        if(predicateCriteria instanceof CompareCriteria) {
            CompareCriteria compCrit = (CompareCriteria) predicateCriteria;
                       
            if (compCrit.getOperator() == CompareCriteria.EQ || compCrit.getOperator() == CompareCriteria.NE){
                if (unknownChildCost && (!usesKey || multiGroup)) {
                    return UNKNOWN_VALUE;
                }
                cost = childCost / ndv;
                if (compCrit.getOperator() == CompareCriteria.NE) {
                    isNegatedPredicateCriteria = true;
                }
            } else { //GE, LE, GT, LT
                cost = getCostForComparison(childCost, metadata, compCrit, unknownChildCost);
            }
        } else if(predicateCriteria instanceof MatchCriteria) {
            MatchCriteria matchCriteria = (MatchCriteria)predicateCriteria;
            if (unknownChildCost) {
                return UNKNOWN_VALUE;
            }
            cost = estimateMatchCost(childCost, ndv, matchCriteria);
           
            isNegatedPredicateCriteria = matchCriteria.isNegated();

        } else if(predicateCriteria instanceof SetCriteria) {
            SetCriteria setCriteria = (SetCriteria) predicateCriteria;
            if (unknownChildCost) {
                return UNKNOWN_VALUE;
            }
            cost = childCost * setCriteria.getNumberOfValues() / ndv;
           
            isNegatedPredicateCriteria = setCriteria.isNegated();
           
        } else if(predicateCriteria instanceof SubquerySetCriteria) {
            SubquerySetCriteria setCriteria = (SubquerySetCriteria) predicateCriteria;
           
            // TODO - use inner ProcessorPlan cardinality estimates
            // to determine the estimated number of values
            if (unknownChildCost) {
                return UNKNOWN_VALUE;
            }
            cost = childCost / 3;
           
            isNegatedPredicateCriteria = setCriteria.isNegated();

        } else if(predicateCriteria instanceof IsNullCriteria) {
            IsNullCriteria isNullCriteria = (IsNullCriteria)predicateCriteria;

            float nnv = getStat(Stat.NNV, elements, currentNode, childCost, metadata);
            if (nnv == UNKNOWN_VALUE) {
              if (unknownChildCost) {
                return UNKNOWN_VALUE;
              }
                cost = childCost / ndv;
            } else {
                cost = nnv;
            }
           
            isNegatedPredicateCriteria = isNullCriteria.isNegated();
        } else if (predicateCriteria instanceof DependentSetCriteria) {
          DependentSetCriteria dsc = (DependentSetCriteria)predicateCriteria;
         
          if (unknownChildCost) {
                return UNKNOWN_VALUE;
            }
          if (dsc.getNdv() == UNKNOWN_VALUE) {
            return childCost / 3;
          }
         
          cost = childCost * dsc.getNdv() / ndv;
        }

        if (cost == UNKNOWN_VALUE) {
            return UNKNOWN_VALUE;
        }
       
        if (cost > childCost) {
            cost = childCost;
        }
       
        if (isNegatedPredicateCriteria) {
            // estimate for NOT in the predicate
            cost = (cost != UNKNOWN_VALUE)
                    ? Math.max( childCost - cost, 1)
                    : UNKNOWN_VALUE;
        }
               
        return cost;
    }

    /**
     * TODO: does not check for escape char
     * or if it will contain single match chars
     */
    private static float estimateMatchCost(float childCost,
                                           float ndv,
                                           MatchCriteria criteria) {
        Expression matchExpression = criteria.getRightExpression();
        if(matchExpression instanceof Constant && ((Constant)matchExpression).getType().equals(DataTypeManager.DefaultDataClasses.STRING)) {
            String compareValue = (String) ((Constant)matchExpression).getValue();
            if(compareValue != null && compareValue.indexOf('%') < 0) {
              return (childCost / 2) * (1 / 3f  + 1 / ndv); //without knowing length constraints we'll make an average guess
            }
        } else if (EvaluatableVisitor.willBecomeConstant(criteria.getLeftExpression())) {
            return childCost / ndv;
        }
        return childCost / 3;
    }

    private static float getCostForComparison(float childCost,
                                              QueryMetadataInterface metadata,
                                              CompareCriteria compCrit, boolean unknownChildCost) throws TeiidComponentException,
                                                                       QueryMetadataException {
        if (!(compCrit.getLeftExpression() instanceof ElementSymbol) || !(compCrit.getRightExpression() instanceof Constant)) {
            if (unknownChildCost) {
                return UNKNOWN_VALUE;
            }
            return childCost/3;
        }
        ElementSymbol element = (ElementSymbol)compCrit.getLeftExpression();
        Class<?> dataType = compCrit.getRightExpression().getType();
   
        String max = (String)metadata.getMaximumValue(element.getMetadataID());
        String min = (String)metadata.getMinimumValue(element.getMetadataID());
        if(max == null || min == null) {
            if (unknownChildCost) {
                return UNKNOWN_VALUE;
            }
            return childCost/3;
        }
        float cost = childCost;
        try{
            float maxValue = 0;
            float minValue = 0;

            Constant value = (Constant)compCrit.getRightExpression();
            float compareValue = 0;
          // Case 6257 - handling added for time and date.  If the max/min values are not
            // in the expected format, NumberFormatException is thrown and reverts to default costing.
            if(dataType.equals(DataTypeManager.DefaultDataClasses.TIMESTAMP)) {
                compareValue = ((Timestamp)value.getValue()).getTime();
                maxValue = Timestamp.valueOf(max).getTime();
                minValue = Timestamp.valueOf(min).getTime();
            } else if(dataType.equals(DataTypeManager.DefaultDataClasses.TIME)) {
                compareValue = ((Time)value.getValue()).getTime();
                maxValue = Time.valueOf(max).getTime();
                minValue = Time.valueOf(min).getTime();
            // (For date, our costing sets the max and min values using timestamp format)
            } else if(dataType.equals(DataTypeManager.DefaultDataClasses.DATE)) {
                compareValue = ((Date)value.getValue()).getTime();
                maxValue = Timestamp.valueOf(max).getTime();
                minValue = Timestamp.valueOf(min).getTime();
            } else {
              if(!Number.class.isAssignableFrom(dataType)) {
                    if (unknownChildCost) {
                        return UNKNOWN_VALUE;
                    }
                    return childCost/3;
                }
                compareValue = ((Number)value.getValue()).floatValue();
                maxValue = Integer.parseInt(max);
                minValue = Integer.parseInt(min);
            }
            float range = Math.max(maxValue - minValue, 1);
           
            float costMultiple = 1;
           
            if(compCrit.getOperator() == CompareCriteria.GT || compCrit.getOperator() == CompareCriteria.GE) {
              costMultiple = (maxValue - compareValue)/range;
                if (compareValue < 0 && maxValue < 0) {
                  costMultiple = (1 - costMultiple);
                }
            } else if(compCrit.getOperator() == CompareCriteria.LT || compCrit.getOperator() == CompareCriteria.LE) {
              costMultiple = (compareValue - minValue)/range;
                if (compareValue < 0 && minValue < 0) {
                  costMultiple = (1 - costMultiple);
                }
            }
            if (costMultiple > 1) {
              costMultiple = 1;
            } else if (costMultiple < 0) {
              costMultiple = 0;
            }
            cost = childCost * costMultiple;
        }catch(IllegalArgumentException e) {
            LogManager.logWarning(LogConstants.CTX_QUERY_PLANNER, e, QueryPlugin.Util.getString("NewCalculateCostUtil.badCost")); //$NON-NLS-1$
            // If we were unable to parse the timestamp we will revert to the divide by three estimate
            if (unknownChildCost) {
                return UNKNOWN_VALUE;
            }
            cost = childCost/3;
        }
        return cost;
    }
   
    static boolean usesKey(PlanNode planNode, Collection<? extends SingleElementSymbol> allElements, QueryMetadataInterface metadata) throws QueryMetadataException, TeiidComponentException {
      //TODO: key preserved joins should be marked
      return isSingleTable(planNode)
      && usesKey(allElements, metadata);
    }

  static boolean isSingleTable(PlanNode planNode) {
    return NodeEditor.findAllNodes(planNode, NodeConstants.Types.SOURCE, NodeConstants.Types.JOIN | NodeConstants.Types.SET_OP).size() == 1;
  }
   
    public static boolean usesKey(Criteria crit, QueryMetadataInterface metadata) throws QueryMetadataException, TeiidComponentException {
        HashSet<ElementSymbol> elements = new HashSet<ElementSymbol>();
        collectElementsOfValidCriteria(crit, elements);
        return usesKey(elements, metadata);
    }
   
    /**
     * TODO: this uses key check is not really accurate, it doesn't take into consideration where
     * we are in the plan.
     * if a key column is used after a non 1-1 join or a union all, then it may be non-unique.
     */
    public static boolean usesKey(Collection<? extends SingleElementSymbol> allElements, QueryMetadataInterface metadata)
        throws QueryMetadataException, TeiidComponentException {
      return usesKey(allElements, null, metadata, true);
    }

    public static boolean usesKey(Collection<? extends SingleElementSymbol> allElements, Set<GroupSymbol> groups, QueryMetadataInterface metadata, boolean unique)
    throws QueryMetadataException, TeiidComponentException {
    return getKeyUsed(allElements, groups, metadata, unique) != null;
    }
   
    public static Object getKeyUsed(Collection<? extends SingleElementSymbol> allElements, Set<GroupSymbol> groups, QueryMetadataInterface metadata, Boolean unique)
    throws QueryMetadataException, TeiidComponentException {
       
        if(allElements == null || allElements.size() == 0) {
            return null;
        }   
    
        // Sort elements into groups
        Map<GroupSymbol, List<Object>> groupMap = new HashMap<GroupSymbol, List<Object>>();
        for (SingleElementSymbol ses : allElements) {
          Expression ex = SymbolMap.getExpression(ses);
          if (!(ex instanceof ElementSymbol)) {
            continue; //TODO: function based indexes are possible, but we don't have the metadata
          }
          ElementSymbol element = (ElementSymbol)ex;
            GroupSymbol group = element.getGroupSymbol();
            if (groups != null && !groups.contains(group)) {
              continue;
            }
            List<Object> elements = groupMap.get(group);
            if(elements == null) {
                elements = new ArrayList<Object>();
                groupMap.put(group, elements);
            }
            elements.add(element.getMetadataID());
        }   
            
        // Walk through each group
        for (Map.Entry<GroupSymbol, List<Object>> entry : groupMap.entrySet()) {
            GroupSymbol group = entry.getKey();
            List<Object> elements = entry.getValue();
           
            // Look up keys
            Collection keys = null;
            if ((unique != null && unique) || unique == null) {
              keys = metadata.getUniqueKeysInGroup(group.getMetadataID());
            }
            if ((unique != null && !unique) || unique == null) {
              if (keys != null) {
                keys = new ArrayList<Object>(keys);
              } else {
                keys = new ArrayList<Object>(2);
              }
              keys.addAll(metadata.getIndexesInGroup(group.getMetadataID()));
            }
           
            if(keys != null && keys.size() > 0) {
                // For each key, get key elements
              for (Object key : keys) {
                    List keyElements = metadata.getElementIDsInKey(key);
                    if(elements.containsAll(keyElements)) {
                        // Used all elements of the key
                        return key;
                    }   
                }
            }                                   
        }
       
        return null;
    }   
   
    private static float safeLog(float x) {
        return (float)Math.max(1, Math.log(x));
    }
   
    /**
     * Computes the cost of a Dependent Join
     *
     * The worst possible cost will arise from a high independent ndv (many dependent sets) and a low dependent ndv (possibly many matches per set)
     *
     * This logic uses the same assumption as criteria in that ndv is used as a divisor of cardinality.
     * @throws QueryPlannerException
     *
     */
    public static DependentCostAnalysis computeCostForDepJoin(PlanNode joinNode, boolean leftIndependent, QueryMetadataInterface metadata, CapabilitiesFinder capFinder, CommandContext context)
        throws TeiidComponentException, QueryMetadataException, QueryPlannerException {
       
        PlanNode independentNode = leftIndependent?joinNode.getFirstChild():joinNode.getLastChild();
        PlanNode dependentNode = leftIndependent?joinNode.getLastChild():joinNode.getFirstChild();
       
        List independentExpressions = (List)(leftIndependent?joinNode.getProperty(NodeConstants.Info.LEFT_EXPRESSIONS):joinNode.getProperty(NodeConstants.Info.RIGHT_EXPRESSIONS));
        List dependentExpressions = (List)(leftIndependent?joinNode.getProperty(NodeConstants.Info.RIGHT_EXPRESSIONS):joinNode.getProperty(NodeConstants.Info.LEFT_EXPRESSIONS));
       
        return computeCostForDepJoin(independentNode, dependentNode,
        independentExpressions, dependentExpressions, metadata,
        capFinder, context);
    }
   
  public static DependentCostAnalysis computeCostForDepJoin(PlanNode independentNode,
      PlanNode dependentNode, List independentExpressions,
      List dependentExpressions, QueryMetadataInterface metadata,
      CapabilitiesFinder capFinder, CommandContext context)
      throws QueryMetadataException, TeiidComponentException, QueryPlannerException {

        float independentCardinality = computeCostForTree(independentNode, metadata);
        float dependentCardinality = computeCostForTree(dependentNode, metadata);

        DependentCostAnalysis dca = new DependentCostAnalysis();
        dca.maxNdv = new Float[independentExpressions.size()];
        dca.expectedNdv = new Float[independentExpressions.size()];

        if (independentCardinality == UNKNOWN_VALUE || dependentCardinality == UNKNOWN_VALUE) {
          return dca; //no cost information to be determined
        }
       
        float processorBatchSize = BufferManager.DEFAULT_PROCESSOR_BATCH_SIZE;
        if(context != null) {
            processorBatchSize = context.getProcessorBatchSize();
        }

        RulePushSelectCriteria rpsc = new RulePushSelectCriteria();
        rpsc.setCreatedNodes(new LinkedList<PlanNode>());
       
    for (int i = 0; i < independentExpressions.size(); i++) {
      Expression indExpr = (Expression)independentExpressions.get(i);
      Collection<ElementSymbol> indElements = ElementCollectorVisitor.getElements(indExpr, true);
      float indSymbolNDV = getNDVEstimate(independentNode, metadata, independentCardinality, indElements, true);
      Expression depExpr = (Expression)dependentExpressions.get(i);
     
      LinkedList<Expression> depExpressions = new LinkedList<Expression>();
      LinkedList<PlanNode> targets = determineTargets(dependentNode,
          metadata, capFinder, rpsc, depExpr, depExpressions);
     
      Iterator<Expression> exprIter = depExpressions.iterator();
      for (Iterator<PlanNode> targetIter = targets.iterator(); targetIter.hasNext();) {
        PlanNode target = targetIter.next();
        Expression targerDepExpr = exprIter.next();
        PlanNode accessNode = NodeEditor.findParent(target, NodeConstants.Types.ACCESS);

            float setCriteriaBatchSize = indSymbolNDV;
               
            if (accessNode != null) {
                setCriteriaBatchSize = CapabilitiesUtil.getMaxInCriteriaSize(RuleRaiseAccess.getModelIDFromAccess(accessNode, metadata), metadata, capFinder);
                if (setCriteriaBatchSize < 1) {
                    setCriteriaBatchSize = indSymbolNDV;
                } else {
                  int numberOfSets = CapabilitiesUtil.getMaxDependentPredicates(RuleRaiseAccess.getModelIDFromAccess(accessNode, metadata), metadata, capFinder);
                  if (numberOfSets > 0) {
                    setCriteriaBatchSize *= Math.max(1, numberOfSets /dependentExpressions.size()); //scale down to be conservative
                  }
                }
            } else if (indSymbolNDV > processorBatchSize) {
                //don't bother making a virtual join dependent if they are likely to be large
              //TODO: what operations are performed between origNode and dependentNode
              //TODO: we should be using a tree structure rather than just a value iterator
              continue;
            }
            if (target.hasBooleanProperty(Info.MAKE_NOT_DEP)) {
              continue;
            }
            Collection<ElementSymbol> depElems = ElementCollectorVisitor.getElements(targerDepExpr, true);
            while (target.getParent().getType() == NodeConstants.Types.SELECT) {
              target = target.getParent();
            }
            float depTargetCardinality = computeCostForTree(target, metadata);
            if (depTargetCardinality == UNKNOWN_VALUE) {
              continue;
            }
            float depSymbolNDV = getStat(Stat.NDV, depElems, target, depTargetCardinality, metadata);
            boolean usesKey = usesKey(dependentNode, depElems, metadata);
        if (depSymbolNDV == UNKNOWN_VALUE) {
          if (!usesKey) {
            //make an educated guess that this is a fk
            float indSymbolOrigNDV = indSymbolNDV;
            float indCardinalityOrig = independentCardinality;
            //TODO: we should probably dig deeper than this
            PlanNode indOrigNode = FrameUtil.findOriginatingNode(independentNode, GroupsUsedByElementsVisitor.getGroups(indElements));
            if (indOrigNode != null) {
              indCardinalityOrig = computeCostForTree(indOrigNode, metadata);
              indSymbolOrigNDV = getStat(Stat.NDV, indElements, indOrigNode, indCardinalityOrig, metadata);
              if (indSymbolOrigNDV == UNKNOWN_VALUE) {
                indSymbolOrigNDV = indCardinalityOrig * indSymbolNDV / independentCardinality;
              }
            }
            depSymbolNDV = Math.max((float)Math.pow(depTargetCardinality, .75), Math.min(indSymbolOrigNDV, depTargetCardinality));
          } else {
            depSymbolNDV = depTargetCardinality;
          }
        }
        boolean usesIndex = accessNode != null && usesKey;
        if (!usesKey && accessNode != null && target.getType() == NodeConstants.Types.SOURCE && target.getChildCount() == 0) {
          usesIndex = usesKey(depElems, target.getGroups(), metadata, false);
        }
            float[] estimates = estimateCost(accessNode, setCriteriaBatchSize, usesIndex, depTargetCardinality, indSymbolNDV, dependentCardinality, depSymbolNDV);
            if (estimates[1] < 0) {
              if (dca.expectedCardinality == null) {
                dca.expectedCardinality = estimates[0];
              } else {
                dca.expectedCardinality = Math.min(dca.expectedCardinality, estimates[0]);
              }
            }
            dca.expectedNdv[i] = indSymbolNDV;
            //use a quick binary search to find the max ndv
            float min = 0;
            float max = Math.max(Integer.MAX_VALUE, indSymbolNDV);
            for (int j = 0; j < 10; j++) {
              if (estimates[1] > 1) {
                max = indSymbolNDV;
                indSymbolNDV = (indSymbolNDV + min)/2;
              } else if (estimates[1] < 0) {
                min = indSymbolNDV;
                //we assume that values should be closer to the min side
                indSymbolNDV = Math.min(indSymbolNDV * 8 + 1, (indSymbolNDV + max)/2);
              } else {
                break;
              }
              estimates = estimateCost(accessNode, setCriteriaBatchSize, usesIndex, depTargetCardinality, indSymbolNDV, dependentCardinality, depSymbolNDV);
            }
            dca.maxNdv[i] = indSymbolNDV;
      }
    }
        return dca;
  }
 
  private static float[] estimateCost(PlanNode accessNode, float setCriteriaBatchSize, boolean usesIndex, float depTargetCardinality,
      float indSymbolNDV, float dependentCardinality, float depSymbolNDV) {
        float dependentAccessCardinality = Math.min(depTargetCardinality, depTargetCardinality * indSymbolNDV / depSymbolNDV);
        float scaledCardinality = Math.min(dependentCardinality, dependentCardinality * indSymbolNDV / depSymbolNDV);
    float numberComparisons = (usesIndex?safeLog(depTargetCardinality):depTargetCardinality) * (usesIndex?indSymbolNDV:safeLog(indSymbolNDV));
        float newDependentQueries = accessNode == null?0:(float)Math.ceil(indSymbolNDV / setCriteriaBatchSize);
       
        float relativeCost = newDependentQueries*procNewRequestTime;
        float relativeComparisonCost = (numberComparisons - safeLog(scaledCardinality) /*no longer needed by the join*/
            /*sort cost reduction, however it's always true if its on the source and using an index
              TODO: there are other cost reductions, which we could get by checking the other parent nodes */
          + (scaledCardinality*safeLog(scaledCardinality) - dependentCardinality*safeLog(dependentCardinality)))
          * compareTime;
        float relativeReadCost = (dependentAccessCardinality - depTargetCardinality)*readTime; //cardinality reductions
        return new float[] {scaledCardinality, relativeCost + relativeComparisonCost + relativeReadCost};
  }
 
  /**
   * For now we only consider a single target. In the future we may consider multiple.
   */
  private static LinkedList<PlanNode> determineTargets(
      PlanNode dependentNode, QueryMetadataInterface metadata,
      CapabilitiesFinder capFinder, RulePushSelectCriteria rpsc,
      Expression depExpr, LinkedList<Expression> depExpressions)
      throws QueryPlannerException, TeiidComponentException {
    LinkedList<PlanNode> targets = new LinkedList<PlanNode>();
    LinkedList<PlanNode> critNodes = new LinkedList<PlanNode>();
    critNodes.add(RelationalPlanner.createSelectNode(new DependentSetCriteria(depExpr, null), false));
    LinkedList<PlanNode> initialTargets = new LinkedList<PlanNode>();
    initialTargets.add(dependentNode);
    while (!critNodes.isEmpty()) {
      PlanNode critNode = critNodes.remove();
      PlanNode initial = initialTargets.remove();
      if (critNode.getGroups().isEmpty()) {
        //TODO: we need to project constants up through a plan to avoid this case
        continue;
      }
      PlanNode sourceNode = FrameUtil.findOriginatingNode(initial, critNode.getGroups());
      PlanNode target = sourceNode;
      if (initial != sourceNode) {
        target = rpsc.examinePath(initial, sourceNode, metadata, capFinder);         
      }
      if (target != sourceNode || (sourceNode.getType() == NodeConstants.Types.SOURCE && sourceNode.getChildCount() == 0)) {
        targets.add(target);
        DependentSetCriteria dsc = (DependentSetCriteria)critNode.getProperty(Info.SELECT_CRITERIA);
        depExpressions.add(dsc.getExpression());
        continue;
      }
      if (sourceNode.getType() == NodeConstants.Types.SOURCE) {
        PlanNode child = sourceNode.getFirstChild();
            child = FrameUtil.findOriginatingNode(child, child.getGroups());
            if (child != null && child.getType() == NodeConstants.Types.SET_OP) {
              targets.add(target);
          DependentSetCriteria dsc = (DependentSetCriteria)critNode.getProperty(Info.SELECT_CRITERIA);
          depExpressions.add(dsc.getExpression());
          //TODO: we need better handling for set op situations
          continue;
            }
        if (!rpsc.pushAcrossFrame(sourceNode, critNode, metadata)) {
          targets.add(target);
          DependentSetCriteria dsc = (DependentSetCriteria)critNode.getProperty(Info.SELECT_CRITERIA);
          depExpressions.add(dsc.getExpression());
        }
        List<PlanNode> createdNodes = rpsc.getCreatedNodes();
        for (PlanNode planNode : createdNodes) {
          critNodes.add(planNode);
          initialTargets.add(planNode.getFirstChild());
          NodeEditor.removeChildNode(planNode.getParent(), planNode);
        }
        rpsc.getCreatedNodes().clear();
      }
      //the source must be a null or project node, which we don't care about
    }
    return targets;
  }

  static float getNDVEstimate(PlanNode indNode,
      QueryMetadataInterface metadata, float cardinality,
      Collection<? extends SingleElementSymbol> elems, boolean useCardinalityIfUnknown) throws QueryMetadataException,
      TeiidComponentException {
    if (elems == null || elems.isEmpty()) {
      return cardinality;
    }
    float ndv = getStat(Stat.NDV, elems, indNode, cardinality, metadata);
    if (ndv == UNKNOWN_VALUE) {
      if (cardinality == UNKNOWN_VALUE) {
        return UNKNOWN_VALUE;
      }
      if (usesKey(indNode, elems, metadata)) {
        ndv = cardinality;
      } else if (useCardinalityIfUnknown) {
        ndv = cardinality/2;
      } else {
        return UNKNOWN_VALUE;
      }
    }
    return Math.max(1, ndv);
  }
   
}
TOP

Related Classes of org.teiid.query.optimizer.relational.rules.NewCalculateCostUtil$DependentCostAnalysis

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.