[teiid-commits] teiid SVN: r2991 - in trunk: engine/src/main/java/org/teiid/query/optimizer/relational and 12 other directories.

teiid-commits at lists.jboss.org teiid-commits at lists.jboss.org
Tue Mar 15 21:26:46 EDT 2011


Author: shawkins
Date: 2011-03-15 21:26:46 -0400 (Tue, 15 Mar 2011)
New Revision: 2991

Added:
   trunk/test-integration/common/src/test/java/org/teiid/dqp/internal/process/TestCrossSourceStarJoin.java
Modified:
   trunk/build/kits/jboss-container/teiid-releasenotes.html
   trunk/engine/src/main/java/org/teiid/query/optimizer/relational/RelationalPlanner.java
   trunk/engine/src/main/java/org/teiid/query/optimizer/relational/plantree/NodeConstants.java
   trunk/engine/src/main/java/org/teiid/query/optimizer/relational/plantree/PlanNode.java
   trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/JoinRegion.java
   trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/NewCalculateCostUtil.java
   trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RuleChooseDependent.java
   trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RuleImplementJoinStrategy.java
   trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RuleMergeCriteria.java
   trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RulePlanJoins.java
   trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RulePushAggregates.java
   trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RulePushSelectCriteria.java
   trunk/engine/src/main/java/org/teiid/query/processor/relational/RelationalNode.java
   trunk/engine/src/main/java/org/teiid/query/rewriter/QueryRewriter.java
   trunk/engine/src/main/java/org/teiid/query/sql/lang/DependentSetCriteria.java
   trunk/engine/src/test/java/org/teiid/dqp/internal/process/TestDQPCore.java
   trunk/engine/src/test/java/org/teiid/query/optimizer/TestAggregatePushdown.java
   trunk/engine/src/test/java/org/teiid/query/optimizer/TestDependentJoins.java
   trunk/engine/src/test/java/org/teiid/query/optimizer/TestOptimizer.java
   trunk/engine/src/test/java/org/teiid/query/optimizer/TestPartitionedJoinPlanning.java
   trunk/engine/src/test/java/org/teiid/query/optimizer/relational/rules/TestRuleChooseDependent.java
   trunk/engine/src/test/java/org/teiid/query/processor/TestVirtualDepJoin.java
   trunk/engine/src/test/java/org/teiid/query/processor/xml/TestXMLPlanningEnhancements.java
   trunk/engine/src/test/java/org/teiid/query/unittest/RealMetadataFactory.java
   trunk/test-integration/common/src/test/java/org/teiid/dqp/internal/process/TestTPCR.java
Log:
TEIID-1508 TEIID-239 considering dependent joins earlier in planning and enhancing the costing logic 

Modified: trunk/build/kits/jboss-container/teiid-releasenotes.html
===================================================================
--- trunk/build/kits/jboss-container/teiid-releasenotes.html	2011-03-11 17:39:21 UTC (rev 2990)
+++ trunk/build/kits/jboss-container/teiid-releasenotes.html	2011-03-16 01:26:46 UTC (rev 2991)
@@ -40,6 +40,7 @@
 	<LI><B>Optional Join Enhancements</B> - the optional join hint no longer requires the use of ANSI joins and can will not remove optional bridging tables that are used by two other tables that are required.
 	<LI><B>InterSystems Cache</B> - InterSystems Cache database translator is now available to use as supported source under Teiid.
 	<LI><B>userRequestSourceConcurrency</B> - was added to control the number of concurrent source queries allowed for each user request.
+	<LI><B>Dependent Join Improvements</B> - dependent join analysis and costing in general was improved to consider dependent joins earlier in planning.
 </UL>
 
 <h2><a name="Compatibility">Compatibility Issues</a></h2>

Modified: trunk/engine/src/main/java/org/teiid/query/optimizer/relational/RelationalPlanner.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/optimizer/relational/RelationalPlanner.java	2011-03-11 17:39:21 UTC (rev 2990)
+++ trunk/engine/src/main/java/org/teiid/query/optimizer/relational/RelationalPlanner.java	2011-03-16 01:26:46 UTC (rev 2991)
@@ -389,10 +389,10 @@
             rules.push(RuleConstants.IMPLEMENT_JOIN_STRATEGY);
         }
         
+        rules.push(RuleConstants.CALCULATE_COST);
+        
         rules.push(RuleConstants.ASSIGN_OUTPUT_ELEMENTS);
         
-        rules.push(RuleConstants.CALCULATE_COST);
-        
         if (hints.hasLimit) {
             rules.push(RuleConstants.PUSH_LIMIT);
         }

Modified: trunk/engine/src/main/java/org/teiid/query/optimizer/relational/plantree/NodeConstants.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/optimizer/relational/plantree/NodeConstants.java	2011-03-11 17:39:21 UTC (rev 2990)
+++ trunk/engine/src/main/java/org/teiid/query/optimizer/relational/plantree/NodeConstants.java	2011-03-16 01:26:46 UTC (rev 2991)
@@ -139,6 +139,7 @@
         EST_DEP_JOIN_COST,   // Float value that represents the estimated cost of a dependent join (the join strategy for this could be Nested Loop or Merge)
         EST_JOIN_COST,       // Float value that represents the estimated cost of a merge join (the join strategy for this could be Nested Loop or Merge)
         EST_CARDINALITY,     // Float represents the estimated cardinality (amount of rows) produced by this node
+        EST_COL_STATS,
         EST_SELECTIVITY,     // Float that represents the selectivity of a criteria node 
         
         // Tuple limit and offset

Modified: trunk/engine/src/main/java/org/teiid/query/optimizer/relational/plantree/PlanNode.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/optimizer/relational/plantree/PlanNode.java	2011-03-11 17:39:21 UTC (rev 2990)
+++ trunk/engine/src/main/java/org/teiid/query/optimizer/relational/plantree/PlanNode.java	2011-03-16 01:26:46 UTC (rev 2991)
@@ -170,11 +170,11 @@
         return nodeProperties.get(propertyID);
     }
 
-    public void setProperty(NodeConstants.Info propertyID, Object value) {
+    public Object setProperty(NodeConstants.Info propertyID, Object value) {
         if(nodeProperties == null) {
             nodeProperties = new HashMap<NodeConstants.Info, Object>();
         }    
-        nodeProperties.put(propertyID, value);
+        return nodeProperties.put(propertyID, value);
     }
 
     public Object removeProperty(Object propertyID) {

Modified: trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/JoinRegion.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/JoinRegion.java	2011-03-11 17:39:21 UTC (rev 2990)
+++ trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/JoinRegion.java	2011-03-16 01:26:46 UTC (rev 2991)
@@ -23,6 +23,7 @@
 package org.teiid.query.optimizer.relational.rules;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -34,12 +35,15 @@
 import java.util.Set;
 
 import org.teiid.api.exception.query.QueryMetadataException;
+import org.teiid.api.exception.query.QueryPlannerException;
 import org.teiid.core.TeiidComponentException;
 import org.teiid.metadata.FunctionMethod.PushDown;
 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.PlanNode;
+import org.teiid.query.optimizer.relational.plantree.NodeConstants.Info;
 import org.teiid.query.resolver.util.AccessPattern;
 import org.teiid.query.sql.lang.CompareCriteria;
 import org.teiid.query.sql.lang.CompoundCriteria;
@@ -51,6 +55,7 @@
 import org.teiid.query.sql.visitor.ElementCollectorVisitor;
 import org.teiid.query.sql.visitor.FunctionCollectorVisitor;
 import org.teiid.query.sql.visitor.GroupsUsedByElementsVisitor;
+import org.teiid.query.util.CommandContext;
 
 
 /**
@@ -87,6 +92,8 @@
     private Map<ElementSymbol, Set<Collection<GroupSymbol>>> dependentCriteriaElements;
     private Map<PlanNode, Set<PlanNode>> critieriaToSourceMap;
     
+    private HashMap<List<Object>, Float> depCache;
+    
     public PlanNode getJoinRoot() {
         return joinRoot;
     }
@@ -224,7 +231,7 @@
         }
         this.joinRoot = root;
     }
-    
+        
     /**
      * Will provide an estimate of cost by summing the estimated tuples flowing through
      * each intermediate join. 
@@ -234,8 +241,9 @@
      * @return
      * @throws TeiidComponentException 
      * @throws QueryMetadataException 
+     * @throws QueryPlannerException 
      */
-    public double scoreRegion(Object[] joinOrder, QueryMetadataInterface metadata) throws QueryMetadataException, TeiidComponentException {
+    public double scoreRegion(Object[] joinOrder, int startIndex, QueryMetadataInterface metadata, CapabilitiesFinder capFinder, CommandContext context) throws QueryMetadataException, TeiidComponentException, QueryPlannerException {
         List<Map.Entry<PlanNode, PlanNode>> joinSourceEntries = new ArrayList<Map.Entry<PlanNode, PlanNode>>(joinSourceNodes.entrySet());
         double totalIntermediatCost = 0;
         double cost = 1;
@@ -249,19 +257,25 @@
             Map.Entry<PlanNode, PlanNode> entry = joinSourceEntries.get(source.intValue());
             PlanNode joinSourceRoot = entry.getValue();
             
-            //check to make sure that this group ordering satisfies the access patterns
-            if (!this.unsatisfiedAccessPatterns.isEmpty() || this.containsNestedTable) {
-                PlanNode joinSource = entry.getKey();
-                
-                Collection<GroupSymbol> requiredGroups = (Collection<GroupSymbol>)joinSource.getProperty(NodeConstants.Info.REQUIRED_ACCESS_PATTERN_GROUPS);
-                
-                if (requiredGroups != null && !groups.containsAll(requiredGroups)) {
-                    return Double.MAX_VALUE;
-                }
-            }
+            if (startIndex == 0) {
+	            //check to make sure that this group ordering satisfies the access patterns
+	            if (!this.unsatisfiedAccessPatterns.isEmpty() || this.containsNestedTable) {
+	                PlanNode joinSource = entry.getKey();
+	                
+	                Collection<GroupSymbol> requiredGroups = (Collection<GroupSymbol>)joinSource.getProperty(NodeConstants.Info.REQUIRED_ACCESS_PATTERN_GROUPS);
+	                
+	                if (requiredGroups != null && !groups.containsAll(requiredGroups)) {
+	                    return Double.MAX_VALUE;
+	                }
+	            }
+            } 
             
             groups.addAll(joinSourceRoot.getGroups());
             
+            if (startIndex > 0) {
+            	continue;
+            }
+            
             float sourceCost = ((Float)joinSourceRoot.getProperty(NodeConstants.Info.EST_CARDINALITY)).floatValue();
             
             List<PlanNode> applicableCriteria = null;
@@ -280,7 +294,7 @@
                 	sourceCost = (float)cost;
                 	criteria.removeAll(applicableCriteria);
 	            	applicableCriteria = null;
-            		if (NewCalculateCostUtil.usesKey(cc, metadata)) {
+            		if (NewCalculateCostUtil.usesKey(cc, metadata) || (i == 1 && joinSourceRoot.hasBooleanProperty(Info.MAKE_DEP) && !joinSourceRoot.hasBooleanProperty(Info.MAKE_NOT_DEP))) {
     	            	sourceCost = Math.min(UNKNOWN_TUPLE_EST, sourceCost * Math.min(NewCalculateCostUtil.UNKNOWN_JOIN_SCALING, sourceCost));
             		} else {
     	            	sourceCost = Math.min(UNKNOWN_TUPLE_EST, sourceCost * Math.min(NewCalculateCostUtil.UNKNOWN_JOIN_SCALING * 2, sourceCost));
@@ -288,6 +302,25 @@
                 }
             } else if (Double.isInfinite(sourceCost) || Double.isNaN(sourceCost)) {
             	return Double.MAX_VALUE;
+            } else if (i == 1 && applicableCriteria != null && !applicableCriteria.isEmpty()) {
+            	List<Object> key = Arrays.asList(joinOrder[0], joinOrder[1]);
+            	Float depJoinCost = null;
+            	if (depCache != null && depCache.containsKey(key)) {
+        			depJoinCost = depCache.get(key);
+            	} else {
+	            	Integer indIndex = (Integer)joinOrder[0];
+	            	Map.Entry<PlanNode, PlanNode> indEntry = joinSourceEntries.get(indIndex.intValue());
+	                PlanNode possibleInd = indEntry.getValue();
+	                
+	                depJoinCost = getDepJoinCost(metadata, capFinder, context, possibleInd, applicableCriteria, joinSourceRoot);
+	                if (depCache == null) {
+	                	depCache = new HashMap<List<Object>, Float>();
+	                }
+	                depCache.put(key, depJoinCost);
+            	}
+                if (depJoinCost != null) {
+                	sourceCost = depJoinCost;
+                }
             }
         
             cost *= sourceCost;
@@ -306,6 +339,34 @@
         
         return totalIntermediatCost;
     }
+
+	private Float getDepJoinCost(QueryMetadataInterface metadata,
+			CapabilitiesFinder capFinder, CommandContext context,
+			PlanNode indNode, List<PlanNode> applicableCriteria,
+			PlanNode depNode) throws QueryMetadataException,
+			TeiidComponentException, QueryPlannerException {
+		if (depNode.hasBooleanProperty(Info.MAKE_NOT_DEP)) {
+			return null;
+		}
+		
+		float indCost = indNode.getCardinality();
+		
+		if (indCost == NewCalculateCostUtil.UNKNOWN_VALUE) {
+			return null;
+		}
+		
+		List<Criteria> crits = new ArrayList<Criteria>(applicableCriteria.size());
+		for (PlanNode planNode : applicableCriteria) {
+			crits.add((Criteria) planNode.getProperty(NodeConstants.Info.SELECT_CRITERIA));
+		}
+		List<Expression> leftExpressions = new LinkedList<Expression>();
+		List<Expression> rightExpressions = new LinkedList<Expression>();
+		RuleChooseJoinStrategy.separateCriteria(indNode.getGroups(), depNode.getGroups(), leftExpressions, rightExpressions, crits, new LinkedList<Criteria>());
+		if (leftExpressions.isEmpty()) {
+			return null;
+		}
+		return NewCalculateCostUtil.computeCostForDepJoin(indNode, depNode, leftExpressions, rightExpressions, metadata, capFinder, context);
+	}
     
     /**
      *  Returns true if every element in an unsatisfied access pattern can be satisfied by the current join criteria

Modified: trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/NewCalculateCostUtil.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/NewCalculateCostUtil.java	2011-03-11 17:39:21 UTC (rev 2990)
+++ trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/NewCalculateCostUtil.java	2011-03-16 01:26:46 UTC (rev 2991)
@@ -31,11 +31,15 @@
 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;
@@ -43,16 +47,18 @@
 import org.teiid.logging.LogManager;
 import org.teiid.query.QueryPlugin;
 import org.teiid.query.metadata.QueryMetadataInterface;
-import org.teiid.query.metadata.SupportConstants;
 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.processor.relational.JoinNode.JoinStrategyType;
+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;
@@ -61,12 +67,14 @@
 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;
@@ -81,11 +89,41 @@
 	public static final float UNKNOWN_VALUE = -1;
     
     // the following variables are used to hold cost estimates (roughly in milliseconds)
-    private final static float compareTime = .05f; //TODO: a better estimate would be based upon the number of conjuncts
+    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 = 100; //TODO: should come from the connector
-    private final static float procMoreRequestTime = 15; //TODO: should come from the connector
+    private final static float procNewRequestTime = 1; //TODO: should come from the connector
     
+    private enum Stat {
+    	NDV,
+    	NNV
+    }
+
+    @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
@@ -97,22 +135,24 @@
     static float computeCostForTree(PlanNode node, QueryMetadataInterface metadata) 
         throws QueryMetadataException, TeiidComponentException {
 
-        Float cost = (Float) node.getProperty(NodeConstants.Info.EST_CARDINALITY);
+    	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
-        if(cost == null) {
-        	for (PlanNode child : node.getChildren()) {
-        		computeCostForTree(child, metadata);
-            }
+    	boolean updated = false;
+    	for (PlanNode child : node.getChildren()) {
+    		updated |= updateCardinality(child, metadata);
+        }
+        if(cost == null || updated) {
             computeNodeCost(node, metadata);
-            cost = (Float) node.getProperty(NodeConstants.Info.EST_CARDINALITY); 
+            return true;
         }
-        
-        if(cost != null) {
-            return cost.floatValue();
-        } 
-        
-        return UNKNOWN_VALUE;
+        return false;
     }
    
     /**
@@ -144,7 +184,7 @@
 
             case NodeConstants.Types.GROUP:
             	if (!node.hasCollectionProperty(NodeConstants.Info.GROUP_COLS)) {
-            		setCardinalityEstimate(node, 1f);
+            		setCardinalityEstimate(node, 1f, true, metadata);
             	} else {
             		estimateNodeCost(node, (List)node.getProperty(NodeConstants.Info.GROUP_COLS), metadata);
             	}
@@ -155,11 +195,11 @@
                 //Simply record the cost of the only child
                 PlanNode child = node.getFirstChild();
                 Float childCost = (Float)child.getProperty(NodeConstants.Info.EST_CARDINALITY);
-                setCardinalityEstimate(node, childCost);
+                setCardinalityEstimate(node, childCost, true, metadata);
                 break;
             }
             case NodeConstants.Types.NULL:
-                setCardinalityEstimate(node, 0f);
+                setCardinalityEstimate(node, 0f, true, metadata);
                 break;
 
             case NodeConstants.Types.PROJECT:
@@ -172,7 +212,7 @@
                 } else {
                     childCost = 1f;
                 }
-                setCardinalityEstimate(node, childCost);
+                setCardinalityEstimate(node, childCost, true, metadata);
                 break;
             }
             case NodeConstants.Types.SET_OP: 
@@ -202,7 +242,7 @@
                         cost = new Float(limitCost);
                     }
                 } 
-                setCardinalityEstimate(node, cost);
+                setCardinalityEstimate(node, cost, true, metadata);
                 break;
             }
         }
@@ -223,6 +263,13 @@
 			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) {
@@ -242,7 +289,7 @@
 			break;
 		default: //union
 			if (leftCost != UNKNOWN_VALUE && rightCost != UNKNOWN_VALUE) {
-		        if (!node.hasBooleanProperty(NodeConstants.Info.USE_ALL)) {
+		        if (distinct) {
 		        	cost = Math.max(leftCost, rightCost) + .5f * Math.min(leftCost, rightCost);
 		        } else {
 		            cost = rightCost + leftCost;
@@ -250,8 +297,7 @@
 			}
 			break;
 		}
-		
-		setCardinalityEstimate(node, new Float(cost));
+		return cost;
 	}
 
 	private static float getDistinctEstimate(PlanNode node,
@@ -264,11 +310,17 @@
 		return cost;
 	}
 
-    private static void setCardinalityEstimate(PlanNode node, Float bestEstimate) {
+    private static void setCardinalityEstimate(PlanNode node, Float bestEstimate, boolean setColEstimates, QueryMetadataInterface metadata) throws QueryMetadataException, TeiidComponentException {
         if (bestEstimate == null){
         	bestEstimate = Float.valueOf(UNKNOWN_VALUE);
         }
-        node.setProperty(NodeConstants.Info.EST_CARDINALITY, bestEstimate);
+        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);
+        }
     }
 
     /**
@@ -286,7 +338,7 @@
         float childCost2 = child2.getCardinality();
         
         if (childCost1 == UNKNOWN_VALUE || childCost2 == UNKNOWN_VALUE) {
-        	setCardinalityEstimate(node, null);
+        	setCardinalityEstimate(node, null, true, metadata);
         	return;
         }
 
@@ -303,19 +355,17 @@
         }
         
         Float cost = null;
-        if (JoinType.JOIN_CROSS.equals(joinType)){
-            cost = new Float(baseCost);
+        if (JoinType.JOIN_CROSS.equals(joinType) || JoinType.JOIN_INNER.equals(joinType)){
+            cost = baseCost;
         } else if (JoinType.JOIN_FULL_OUTER.equals(joinType)) {
-            cost = new Float(Math.max((childCost1+childCost2),baseCost));
+            cost = Math.max((childCost1+childCost2),baseCost);
         } else if (JoinType.JOIN_LEFT_OUTER.equals(joinType)) {
-            cost = new Float(Math.max(childCost1,baseCost));
-        } else if (JoinType.JOIN_RIGHT_OUTER.equals(joinType)) {
-            cost = new Float(Math.max(childCost2,baseCost));
-        } else if (JoinType.JOIN_INNER.equals(joinType)) {
-            cost = new Float(baseCost);
+            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);
+        setCardinalityEstimate(node, cost, true, metadata);
     }
 
     /**
@@ -332,8 +382,87 @@
         //Get list of conjuncts
         Criteria selectCriteria = (Criteria)node.getProperty(NodeConstants.Info.SELECT_CRITERIA);
         float newCost = recursiveEstimateCostOfCriteria(childCost, node, selectCriteria, metadata);
-        setCardinalityEstimate(node, new Float(newCost));
+        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);
+        			}
+    				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);
+    				}
+        		}
+    		}
+    		newColStats.put(expr, newStats);
+		}
+    	node.setProperty(Info.EST_COL_STATS, newColStats);
+    }
 
     /**
      * For a source node, the cost is basically the cardinality of the source
@@ -353,6 +482,26 @@
         	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();
@@ -361,10 +510,74 @@
                 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));
-    } 
+        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);
+            		}
+            		if (nnv != UNKNOWN_VALUE) {
+            			nnv *= cardinality / Math.max(1, groupCardinality);
+            		}
+            	}
+			}
+			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 
@@ -381,12 +594,12 @@
         float childCost = child.getCardinality();
         
         if(childCost == UNKNOWN_VALUE) {
-            setCardinalityEstimate(node, null);
+            setCardinalityEstimate(node, null, true, metadata);
             return;
         }
 
         Float newCost = getDistinctEstimate(node, expressions, metadata, childCost);
-        setCardinalityEstimate(node, newCost);
+        setCardinalityEstimate(node, newCost, true, metadata);
     }
 
     private static Float getDistinctEstimate(PlanNode node,
@@ -402,7 +615,7 @@
         if (usesKey(elements, metadata)) {
         	return new Float(childCost);
         }
-        float ndvCost = getNDV(elems, node, childCost, metadata);
+        float ndvCost = getStat(Stat.NDV, elems, node, childCost, metadata);
         if(ndvCost == UNKNOWN_VALUE) {
             ndvCost = childCost;
         }
@@ -411,7 +624,39 @@
         return newCost;
     }
     
-    static float recursiveEstimateCostOfCriteria(float childCost, PlanNode currentNode, Criteria crit, QueryMetadataInterface metadata)
+    private static float getStat(Stat stat, Collection<? extends Expression> elems, PlanNode node,
+    		float cardinality, QueryMetadataInterface metadata) throws QueryMetadataException, TeiidComponentException {
+        float result = 0;
+        for (Expression expression : elems) {
+        	ColStats colStats = null;
+	    	if (node.getChildCount() == 0) {
+	    		colStats = createColStats(node, metadata, cardinality);
+	    	} else {
+		    	for (PlanNode child : node.getChildren()) {
+			    	colStats = (ColStats) child.getProperty(Info.EST_COL_STATS);
+			        if (colStats == null) {
+			        	continue;
+			        }
+					float[] stats = colStats.get(expression);
+					if (stats != null) {
+						break;
+					}
+					colStats = null;
+		    	}
+	    	}
+	    	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; 
@@ -552,7 +797,7 @@
         boolean multiGroup = groups.size() > 1;
         
         float cost = childCost;
-        float ndv = getNDV(elements, currentNode, childCost, metadata);
+        float ndv = getStat(Stat.NDV, elements, currentNode, childCost, metadata);
         
         boolean unknownChildCost = childCost == UNKNOWN_VALUE;
         boolean usesKey = usesKey(elements, metadata);
@@ -623,7 +868,7 @@
         } else if(predicateCriteria instanceof IsNullCriteria) {
             IsNullCriteria isNullCriteria = (IsNullCriteria)predicateCriteria;
 
-            float nnv = getNNV(elements, currentNode, childCost, metadata);
+            float nnv = getStat(Stat.NNV, elements, currentNode, childCost, metadata);
             if (nnv == UNKNOWN_VALUE) {
             	if (unknownChildCost) {
             		return UNKNOWN_VALUE;
@@ -684,7 +929,7 @@
             return childCost/3;
         }
         ElementSymbol element = (ElementSymbol)compCrit.getLeftExpression();
-        Class dataType = compCrit.getRightExpression().getType();
+        Class<?> dataType = compCrit.getRightExpression().getType();
     
         String max = (String)metadata.getMaximumValue(element.getMetadataID());
         String min = (String)metadata.getMinimumValue(element.getMetadataID());
@@ -777,10 +1022,10 @@
      */
     public static boolean usesKey(Collection<? extends SingleElementSymbol> allElements, QueryMetadataInterface metadata)
         throws QueryMetadataException, TeiidComponentException {
-    	return usesKey(allElements, null, metadata);
+    	return usesKey(allElements, null, metadata, true);
     }
     
-    public static boolean usesKey(Collection<? extends SingleElementSymbol> allElements, Set<GroupSymbol> groups, QueryMetadataInterface metadata)
+    public static boolean usesKey(Collection<? extends SingleElementSymbol> allElements, Set<GroupSymbol> groups, QueryMetadataInterface metadata, boolean unique)
     throws QueryMetadataException, TeiidComponentException {
         
         if(allElements == null || allElements.size() == 0) { 
@@ -813,7 +1058,13 @@
             List<Object> elements = entry.getValue();
             
             // Look up keys
-            Collection keys = metadata.getUniqueKeysInGroup(group.getMetadataID());
+            Collection keys = null;
+            if (unique) {
+            	keys = metadata.getUniqueKeysInGroup(group.getMetadataID());
+            } else {
+            	keys = metadata.getIndexesInGroup(group.getMetadataID());
+            }
+            
             if(keys != null && keys.size() > 0) { 
                 // For each key, get key elements
                 Iterator keyIter = keys.iterator();
@@ -828,126 +1079,8 @@
         }
         
         return false; 
-    }
+    }    
     
-    /**
-     * Get the scaled max ndv for a set of elements.
-     * 
-     * NOTE: this is not a good approximation over unions, joins, grouping, etc.
-     */
-    private static float getNDV(Collection<ElementSymbol> elements, PlanNode current, float cardinality, QueryMetadataInterface metadata) 
-        throws QueryMetadataException, TeiidComponentException {
-        float result = UNKNOWN_VALUE;
-        
-    	for (ElementSymbol elementSymbol : elements) {
-            Object elemID = elementSymbol.getMetadataID();
-            float ndv = metadata.getDistinctValues(elemID);
-            if (ndv == UNKNOWN_VALUE) {
-                if (metadata.isVirtualGroup(elementSymbol.getGroupSymbol().getMetadataID()) && !metadata.isProcedure(elementSymbol.getGroupSymbol().getMetadataID())) {
-            		PlanNode sourceNode = FrameUtil.findOriginatingNode(current, new HashSet<GroupSymbol>(Arrays.asList(elementSymbol.getGroupSymbol())));
-            		if (sourceNode != null) {
-	        			SymbolMap symbolMap = (SymbolMap)sourceNode.getProperty(NodeConstants.Info.SYMBOL_MAP);
-	        			//symbolMap will be null for table functions
-	        			if (symbolMap != null) {
-		        			Expression expr = symbolMap.getMappedExpression(elementSymbol);
-		        			ndv = getNDV(ElementCollectorVisitor.getElements(expr, true), sourceNode.getFirstChild(), cardinality, metadata);
-	        			}
-            		}
-            	}
-            } else if (cardinality != UNKNOWN_VALUE) {
-            	int groupCardinality = metadata.getCardinality(elementSymbol.getGroupSymbol().getMetadataID());
-            	if (groupCardinality != UNKNOWN_VALUE && groupCardinality > cardinality) {
-        			ndv *= cardinality / Math.max(1, groupCardinality);
-            	}
-            }
-            result = Math.max(result, ndv);
-		}
-        return result;
-    }
-    
-    /**
-     * Get the scaled max nnv for a set of elements.
-     * 
-     * NOTE: assumes that the expression does not allow nulls
-     */
-    private static float getNNV(Collection<ElementSymbol> elements, PlanNode current, float cardinality, QueryMetadataInterface metadata) 
-        throws QueryMetadataException, TeiidComponentException {
-        float result = 0;
-    	for (ElementSymbol elementSymbol : elements) {
-            Object elemID = elementSymbol.getMetadataID();
-            float nnv = metadata.getNullValues(elemID);
-            if (nnv == UNKNOWN_VALUE) {
-            	if (!metadata.elementSupports(elemID, SupportConstants.Element.NULL) 
-            			&& !metadata.elementSupports(elemID, SupportConstants.Element.NULL_UNKNOWN)) {
-            		nnv = 0;
-            	} else if (metadata.isVirtualGroup(elementSymbol.getGroupSymbol().getMetadataID()) && !metadata.isProcedure(elementSymbol.getGroupSymbol().getMetadataID())) {
-            		PlanNode sourceNode = FrameUtil.findOriginatingNode(current, new HashSet<GroupSymbol>(Arrays.asList(elementSymbol.getGroupSymbol())));
-            		if (sourceNode != null) {
-	        			SymbolMap symbolMap = (SymbolMap)sourceNode.getProperty(NodeConstants.Info.SYMBOL_MAP);
-	        			Expression expr = symbolMap.getMappedExpression(elementSymbol);
-	        			nnv = getNNV(ElementCollectorVisitor.getElements(expr, true), sourceNode.getFirstChild(), cardinality, metadata);
-            		}
-            	}
-            	if (nnv == UNKNOWN_VALUE) {
-            		return UNKNOWN_VALUE;
-            	}
-            } else if (cardinality != UNKNOWN_VALUE) {
-            	int groupCardinality = metadata.getCardinality(elementSymbol.getGroupSymbol().getMetadataID());
-            	if (groupCardinality != UNKNOWN_VALUE) {
-            		nnv *= cardinality / Math.max(1, groupCardinality);
-            	}
-            }
-            result = Math.max(result, nnv);
-		}
-        return result;
-    }
-    
-    /**
-     * Computes the cost of a Merge Join
-     */
-    public static float computeCostForJoin(PlanNode leftChildNode, PlanNode rightChildNode, JoinStrategyType joinStrategy, QueryMetadataInterface metadata, CommandContext context) 
-        throws TeiidComponentException, QueryMetadataException {
-        
-        float leftChildCardinality = computeCostForTree(leftChildNode, metadata);
-        float rightChildCardinality = computeCostForTree(rightChildNode, metadata);
-        
-        boolean merge = JoinStrategyType.MERGE.equals(joinStrategy);
-                
-        // If either cardinality is unknown, we return unknown
-        if(leftChildCardinality == UNKNOWN_VALUE || rightChildCardinality == UNKNOWN_VALUE) {
-            return UNKNOWN_VALUE;
-        }
-        
-        float numberComparisons = merge?(leftChildCardinality + rightChildCardinality):(leftChildCardinality * rightChildCardinality);
-        
-        float connectorBatchSize = BufferManager.DEFAULT_CONNECTOR_BATCH_SIZE;
-        if(context != null) {
-            connectorBatchSize = context.getConnectorBatchSize(); 
-        }
-        
-        float totalReadTime = (leftChildCardinality + rightChildCardinality) * readTime;
-        float totalCompareTime = numberComparisons * compareTime;
-        float totalProcMoreRequestLeftTime = (float)Math.floor(leftChildCardinality/connectorBatchSize)*procMoreRequestTime;
-        float totalProcMoreRequestRightTime = (float)Math.floor(rightChildCardinality/connectorBatchSize)*procMoreRequestTime;
-        
-        float cost = (totalReadTime+
-                                           totalCompareTime+
-                                           totalProcMoreRequestLeftTime+
-                                           totalProcMoreRequestRightTime);
-        
-        if (merge) {
-            cost += (leftChildCardinality*safeLog(leftChildCardinality) + rightChildCardinality*safeLog(rightChildCardinality)) * readTime;
-        }
-        
-        if (isPhysicalSource(rightChildNode)) {
-            cost += procNewRequestTime;
-        }
-        if (isPhysicalSource(leftChildNode)) {
-            cost += procNewRequestTime;
-        }
-        return cost;
-    }
-
     private static float safeLog(float x) {
         return (float)Math.max(1, Math.log(x));
     }
@@ -958,10 +1091,11 @@
      * 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 float computeCostForDepJoin(PlanNode joinNode, boolean leftIndependent, JoinStrategyType joinStrategy, QueryMetadataInterface metadata, CapabilitiesFinder capFinder, CommandContext context) 
-        throws TeiidComponentException, QueryMetadataException {
+    public static Float 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();
@@ -969,139 +1103,176 @@
         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));
         
-        // compute the cost for both trees, if we don't come up with a valid cost for both we have to return unknown
+        return computeCostForDepJoin(independentNode, dependentNode,
+				independentExpressions, dependentExpressions, metadata,
+				capFinder, context);
+    }
+
+	public static Float 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);
         
-        float indSymbolNDV = getNDV(independentNode, independentExpressions, metadata, independentCardinality, true);
-        float depSymbolNDV = getNDV(dependentNode, dependentExpressions, metadata, dependentCardinality, false);
+        if (independentCardinality == UNKNOWN_VALUE || dependentCardinality == UNKNOWN_VALUE) {
+        	return null;
+        }
         
-        //If either cardinality is unknown, we return unknown
-        if(indSymbolNDV == UNKNOWN_VALUE || depSymbolNDV == UNKNOWN_VALUE || independentCardinality == UNKNOWN_VALUE || dependentCardinality == UNKNOWN_VALUE) {
-            return UNKNOWN_VALUE;
-        }
-
-        float connectorBatchSize = BufferManager.DEFAULT_CONNECTOR_BATCH_SIZE;
         float processorBatchSize = BufferManager.DEFAULT_PROCESSOR_BATCH_SIZE;
         if(context != null) {
-            connectorBatchSize = context.getConnectorBatchSize();
             processorBatchSize = context.getProcessorBatchSize();
         }
+
+        RulePushSelectCriteria rpsc = new RulePushSelectCriteria();
+        rpsc.setCreatedNodes(new LinkedList<PlanNode>());
         
-        float setCriteriaBatchSize = indSymbolNDV;
+		for (int i = 0; i < independentExpressions.size(); i++) {
+			Expression indExpr = (Expression)independentExpressions.get(i);
+			Collection<ElementSymbol> indElements = ElementCollectorVisitor.getElements(indExpr, true);
+			float indSymbolNDV = getStat(Stat.NDV, indElements, independentNode, independentCardinality, metadata);
+			boolean usesIndKey = usesKey(dependentNode, indElements, metadata);
+			if (indSymbolNDV == UNKNOWN_VALUE) {
+				if (!usesIndKey) {
+					indSymbolNDV = Math.max(1, independentCardinality/2);
+				} else {
+					indSymbolNDV = independentCardinality;
+				}
+			}
+			Expression depExpr = (Expression)dependentExpressions.get(i);
+			
+			LinkedList<PlanNode> critNodes = new LinkedList<PlanNode>();
+			LinkedList<PlanNode> initialTargets = new LinkedList<PlanNode>();
+			
+			LinkedList<Expression> depExpressions = new LinkedList<Expression>();
+			LinkedList<PlanNode> targets = new LinkedList<PlanNode>();
+			critNodes.add(RelationalPlanner.createSelectNode(new DependentSetCriteria(depExpr, null), false));
+			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
+			}
+			
+			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 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) {
+								if (!usesIndKey) {
+									indSymbolOrigNDV = Math.max(1, indCardinalityOrig / 3);
+								} else {
+									indSymbolOrigNDV = indCardinalityOrig;
+								}
+							} 
+						}
+						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 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
+		        
+		        if (relativeCost + relativeComparisonCost + relativeReadCost < 0) {
+		        	return scaledCardinality;
+		        }
+			}
+		}
         
-        PlanNode node = FrameUtil.findJoinSourceNode(dependentNode);
-        
-        while (node != null && node.getType() != NodeConstants.Types.ACCESS) {
-            if (node.getType() == NodeConstants.Types.JOIN || node.getType() == NodeConstants.Types.SET_OP) {
-                node = null;
-                break;
-            }
-            node = node.getFirstChild();
-        }
-        
-        if (node != null) {
-            setCriteriaBatchSize = CapabilitiesUtil.getMaxInCriteriaSize(RuleRaiseAccess.getModelIDFromAccess(node, metadata), metadata, capFinder);
-            if (setCriteriaBatchSize < 1) {
-                setCriteriaBatchSize = indSymbolNDV;
-            }
-        } else {
-            //don't bother making a virtual join dependent if they are likely to be large
-            if (indSymbolNDV > Math.min(processorBatchSize, setCriteriaBatchSize)) {
-                return UNKNOWN_VALUE;
-            }
-        }
-        
-        independentNode.setProperty(NodeConstants.Info.EST_SET_SIZE, new Float(indSymbolNDV));
-        
-        //for non-partitioned joins the cardinality of the dependentaccess should never be greater than the dependent cardinality
-        //TODO: when partitioned joins are implemented, this logic will need updated
-        float dependentAccessCardinality = Math.min(dependentCardinality, dependentCardinality * indSymbolNDV / depSymbolNDV);
-        
-        boolean merge = false;
-        if (JoinStrategyType.MERGE.equals(joinStrategy)) {
-            merge = true;
-        } else if (!JoinStrategyType.NESTED_LOOP.equals(joinStrategy)) {
-            return UNKNOWN_VALUE;
-        }
-        
-        dependentNode.setProperty(NodeConstants.Info.EST_DEP_CARDINALITY, new Float(dependentAccessCardinality));
-        
-        float numberComparisons = merge?(independentCardinality + dependentAccessCardinality):(independentCardinality * dependentAccessCardinality);
-        
-        //account for sorting
-        float totalLoadDataTime = independentCardinality * safeLog(independentCardinality) * readTime;
-        if (merge) {
-            totalLoadDataTime += dependentAccessCardinality * safeLog(dependentAccessCardinality) * readTime;
-        }
-        
-        //the independentCardinality is doubled to account for the dependent setup time, which re-reads the independent values
-        float totalReadTime = (2*independentCardinality + dependentAccessCardinality) * readTime;
-        float totalCompareTime = numberComparisons * compareTime;
-        float totalProcMoreRequestLeftTime = (float)Math.floor(independentCardinality / connectorBatchSize) * procMoreRequestTime;
-        float newDependentQueries = (float)Math.ceil(indSymbolNDV / setCriteriaBatchSize);
-        float totalProcMoreRequestRightTime = Math.max(dependentAccessCardinality / connectorBatchSize - newDependentQueries, 0) * procMoreRequestTime;
-        
-        float cost = (totalLoadDataTime +
-                                           totalReadTime+
-                                           totalCompareTime+
-                                           totalProcMoreRequestLeftTime+
-                                           totalProcMoreRequestRightTime);
-       
-        if (isPhysicalSource(independentNode)) {
-            cost += procNewRequestTime; //independent query latency
-        }
-        /*estimate for dependent query latencies
-         *NOTE: the initial latency estimate can be made significantly larger for queries against large dependent sets,
-         *which is consistent with observed behavior in which criteria-less queries outperform those with in criteria
-         *with 1000 entries.  
-         */        
-        if (isPhysicalSource(dependentNode)) {
-            cost += newDependentQueries * (procNewRequestTime * 
-                            Math.max(safeLog(dependentCardinality) - 10, 1) * 
-                            Math.max(safeLog(Math.min(dependentCardinality, Math.min(setCriteriaBatchSize, indSymbolNDV))) - 2, 1)
-                            ); 
-        }
-        
-        return cost;
-    }
+        return null;
+	}
     
-    private static boolean isPhysicalSource(PlanNode node) {
-        node = FrameUtil.findJoinSourceNode(node);
-        if (node != null) {
-            return node.getType() == NodeConstants.Types.ACCESS;
-        }
-        return false;
-    }
-    
-    private static float getNDV(PlanNode node,
-                                   List expressions,
-                                   QueryMetadataInterface metadata,
-                                   float nodeCardinality, boolean independent) throws QueryMetadataException,
-                                                      TeiidComponentException {
-        float result = UNKNOWN_VALUE;
-        for(Iterator iter = expressions.iterator(); iter.hasNext();) {
-            Expression expr = (Expression)iter.next();
-            Collection<ElementSymbol> symbols = ElementCollectorVisitor.getElements(expr, true);
-            
-            float currentSymbolNDV = getNDV(symbols, node, nodeCardinality, metadata);                
-            
-            if(currentSymbolNDV == UNKNOWN_VALUE) { 
-                if (usesKey(symbols, metadata)) {
-                    return nodeCardinality;
-                } 
-                if (independent) {
-                    currentSymbolNDV = nodeCardinality / 2;
-                } else {
-                    currentSymbolNDV = nodeCardinality / 4;
-                }
-            }
-            
-            if(result == UNKNOWN_VALUE || currentSymbolNDV > result) {
-                result = currentSymbolNDV;
-            }
-        }
-        return Math.max(1, Math.min(nodeCardinality, result));
-    }
-    
 }

Modified: trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RuleChooseDependent.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RuleChooseDependent.java	2011-03-11 17:39:21 UTC (rev 2990)
+++ trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RuleChooseDependent.java	2011-03-16 01:26:46 UTC (rev 2991)
@@ -40,7 +40,6 @@
 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.processor.relational.JoinNode.JoinStrategyType;
 import org.teiid.query.sql.lang.DependentSetCriteria;
 import org.teiid.query.sql.lang.JoinType;
 import org.teiid.query.sql.symbol.Expression;
@@ -81,29 +80,24 @@
             
             boolean bothCandidates = entry.leftCandidate&&entry.rightCandidate;
             
-            JoinStrategyType joinStrategy = (JoinStrategyType)joinNode.getProperty(NodeConstants.Info.JOIN_STRATEGY);
-            
             PlanNode chosenNode = chooseDepWithoutCosting(sourceNode, bothCandidates?siblingNode:null, analysisRecord);
             if(chosenNode != null) {
                 pushCriteria |= markDependent(chosenNode, joinNode);
                 continue;
             }   
             
-            float depJoinCost = NewCalculateCostUtil.computeCostForDepJoin(joinNode, !entry.leftCandidate, joinStrategy, metadata, capFinder, context);
+            boolean useDepJoin = NewCalculateCostUtil.computeCostForDepJoin(joinNode, !entry.leftCandidate, metadata, capFinder, context) != null;
             PlanNode dependentNode = sourceNode;
-            PlanNode independentNode = siblingNode;
             
-            if (bothCandidates) {
-                float siblingDepJoinCost = NewCalculateCostUtil.computeCostForDepJoin(joinNode, true, joinStrategy, metadata, capFinder, context);
-                if (siblingDepJoinCost != NewCalculateCostUtil.UNKNOWN_VALUE && (siblingDepJoinCost < depJoinCost || depJoinCost == NewCalculateCostUtil.UNKNOWN_VALUE)) {
+            if (bothCandidates && !useDepJoin) {
+                useDepJoin = NewCalculateCostUtil.computeCostForDepJoin(joinNode, true, metadata, capFinder, context) != null;
+                if (useDepJoin) {
                     dependentNode = siblingNode;
-                    depJoinCost = siblingDepJoinCost;
-                    independentNode = sourceNode;
                 }
             }
             
-            if (depJoinCost != NewCalculateCostUtil.UNKNOWN_VALUE) {
-                pushCriteria |= decideForAgainstDependentJoin(depJoinCost, independentNode, dependentNode, joinNode, metadata, context);
+            if (useDepJoin) {
+                pushCriteria |= markDependent(dependentNode, joinNode);
             } else {
             	float sourceCost = NewCalculateCostUtil.computeCostForTree(sourceNode, metadata);
             	float siblingCost = NewCalculateCostUtil.computeCostForTree(siblingNode, metadata);
@@ -126,20 +120,6 @@
         return plan;
     }    
 
-    boolean decideForAgainstDependentJoin(float depJoinCost, PlanNode independentNode, PlanNode dependentNode, PlanNode joinNode, QueryMetadataInterface metadata, CommandContext context)
-        throws QueryMetadataException, TeiidComponentException {
-        JoinStrategyType joinStrategy = (JoinStrategyType)joinNode.getProperty(NodeConstants.Info.JOIN_STRATEGY);
-        joinNode.setProperty(NodeConstants.Info.EST_DEP_JOIN_COST, new Float(depJoinCost));
-
-        float joinCost = NewCalculateCostUtil.computeCostForJoin(independentNode, dependentNode, joinStrategy, metadata, context);
-        joinNode.setProperty(NodeConstants.Info.EST_JOIN_COST, new Float(joinCost));
-        if(depJoinCost < joinCost) {
-            return markDependent(dependentNode, joinNode);
-        }
-        
-        return false;
-    }
-        
     /**
      * Walk the tree pre-order, finding all access nodes that are candidates and
      * adding them to the matches list.

Modified: trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RuleImplementJoinStrategy.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RuleImplementJoinStrategy.java	2011-03-11 17:39:21 UTC (rev 2990)
+++ trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RuleImplementJoinStrategy.java	2011-03-16 01:26:46 UTC (rev 2991)
@@ -104,8 +104,8 @@
             	if (leftCost != NewCalculateCostUtil.UNKNOWN_VALUE && rightCost != NewCalculateCostUtil.UNKNOWN_VALUE 
             			&& (leftCost > context.getProcessorBatchSize() || rightCost > context.getProcessorBatchSize())) {
             		//we use a larger constant here to ensure that we don't unwisely prevent pushdown
-            		pushLeft = leftCost < context.getProcessorBatchSize() || leftCost / rightCost < 16;
-            		pushRight = rightCost < context.getProcessorBatchSize() || rightCost / leftCost < 16 || joinNode.getProperty(NodeConstants.Info.DEPENDENT_VALUE_SOURCE) != null;
+            		pushLeft = leftCost < context.getProcessorBatchSize() || leftCost / rightCost < 8;
+            		pushRight = rightCost < context.getProcessorBatchSize() || rightCost / leftCost < 8 || joinNode.getProperty(NodeConstants.Info.DEPENDENT_VALUE_SOURCE) != null;
             	}
             }            
 

Modified: trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RuleMergeCriteria.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RuleMergeCriteria.java	2011-03-11 17:39:21 UTC (rev 2990)
+++ trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RuleMergeCriteria.java	2011-03-16 01:26:46 UTC (rev 2991)
@@ -354,16 +354,6 @@
 		if (crit instanceof SubqueryCompareCriteria) {
 			SubqueryCompareCriteria scc = (SubqueryCompareCriteria)crit;
 		
-			/*if (scc.getCommand().getCorrelatedReferences() == null) {
-				RelationalPlan originalPlan = (RelationalPlan)scc.getCommand().getProcessorPlan();
-	            Number originalCardinality = originalPlan.getRootNode().getEstimateNodeCardinality();
-	            if (originalCardinality.floatValue() != NewCalculateCostUtil.UNKNOWN_VALUE 
-	            		&& originalCardinality.floatValue() < this.context.getProcessorBatchSize()) {
-	            	//this is small enough that it will effectively be a hash join
-	            	return current;
-	            }
-			}*/
-			
 			if (scc.getPredicateQuantifier() != SubqueryCompareCriteria.SOME
 					//TODO: could add an inline view if not a query
 					|| !(scc.getCommand() instanceof Query)) {
@@ -570,7 +560,7 @@
 		}
 		HashSet<GroupSymbol> keyPreservingGroups = new HashSet<GroupSymbol>();
 		ResolverUtil.findKeyPreserved(query, keyPreservingGroups, metadata);
-		return NewCalculateCostUtil.usesKey(expressions, keyPreservingGroups, metadata);			
+		return NewCalculateCostUtil.usesKey(expressions, keyPreservingGroups, metadata, true);			
 	}
 
 	private boolean hasCorrelatedReferences(LanguageObject object, SymbolMap correlatedReferences) {

Modified: trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RulePlanJoins.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RulePlanJoins.java	2011-03-11 17:39:21 UTC (rev 2990)
+++ trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RulePlanJoins.java	2011-03-16 01:26:46 UTC (rev 2991)
@@ -169,7 +169,7 @@
             
             joinRegion.initializeCostingInformation(metadata);
             
-            Object[] bestOrder = findBestJoinOrder(joinRegion, metadata);
+            Object[] bestOrder = findBestJoinOrder(joinRegion, metadata, capabilitiesFinder, context);
             
             //if no best order was found, just stick with how the user entered the query
             if (bestOrder == null) {
@@ -522,8 +522,9 @@
      * @param region
      * @param metadata
      * @return
+     * @throws QueryPlannerException 
      */
-    Object[] findBestJoinOrder(JoinRegion region, QueryMetadataInterface metadata) throws QueryMetadataException, TeiidComponentException {
+    Object[] findBestJoinOrder(JoinRegion region, QueryMetadataInterface metadata, CapabilitiesFinder capFinder, CommandContext context) throws QueryMetadataException, TeiidComponentException, QueryPlannerException {
         int regionCount = region.getJoinSourceNodes().size();
         
         List<Integer> orderList = new ArrayList<Integer>(regionCount);
@@ -549,7 +550,7 @@
         while(permIter.hasNext()) {
             Object[] order = (Object[]) permIter.next();
 
-            double score = region.scoreRegion(order, metadata);
+            double score = region.scoreRegion(order, 0, metadata, capFinder, context);
             if(score < bestSubScore) {
                 bestSubScore = score;
                 bestSubOrder = order;
@@ -568,7 +569,6 @@
         
         //remove the joins that have already been placed
         for(int i=0; i<bestSubOrder.length; i++) {
-            orderList.remove(bestSubOrder[i]);
             result[i] = (Integer)bestSubOrder[i];
         }
         
@@ -578,12 +578,12 @@
             List bestOrder = null;
 
             for (int i = 0; i < orderList.size(); i++) {
-                Integer index = (Integer)orderList.get(i);
+                Integer index = orderList.get(i);
                 
                 List order = new ArrayList(Arrays.asList(bestSubOrder));
                 order.add(index);
                 
-                double partialScore = region.scoreRegion(order.toArray(), metadata);
+                double partialScore = region.scoreRegion(order.toArray(), bestSubOrder.length, metadata, capFinder, context);
                 
                 if (partialScore < bestPartialScore) {
                     bestPartialScore = partialScore;

Modified: trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RulePushAggregates.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RulePushAggregates.java	2011-03-11 17:39:21 UTC (rev 2990)
+++ trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RulePushAggregates.java	2011-03-16 01:26:46 UTC (rev 2991)
@@ -45,6 +45,7 @@
 import org.teiid.query.function.FunctionLibrary;
 import org.teiid.query.metadata.QueryMetadataInterface;
 import org.teiid.query.metadata.TempMetadataAdapter;
+import org.teiid.query.metadata.TempMetadataID;
 import org.teiid.query.metadata.TempMetadataStore;
 import org.teiid.query.optimizer.capabilities.CapabilitiesFinder;
 import org.teiid.query.optimizer.capabilities.SourceCapabilities.Capability;
@@ -251,6 +252,7 @@
             ElementSymbol virtualElement = new ElementSymbol(virtualElementName);
             virtualElement.setGroupSymbol(virtualGroup);
             virtualElement.setType(symbol.getType());
+            virtualElement.setMetadataID(new TempMetadataID(virtualElementName, symbol.getType()));
             updatedVirturalElement.add(virtualElement);
 		}
 		SymbolMap newParentMap = SymbolMap.createSymbolMap(updatedVirturalElement, projectedViewSymbols);

Modified: trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RulePushSelectCriteria.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RulePushSelectCriteria.java	2011-03-11 17:39:21 UTC (rev 2990)
+++ trunk/engine/src/main/java/org/teiid/query/optimizer/relational/rules/RulePushSelectCriteria.java	2011-03-16 01:26:46 UTC (rev 2991)
@@ -60,6 +60,16 @@
 
 
 public final class RulePushSelectCriteria implements OptimizerRule {
+	
+	private List<PlanNode> createdNodes;
+	
+	public List<PlanNode> getCreatedNodes() {
+		return createdNodes;
+	}
+	
+	public void setCreatedNodes(List<PlanNode> createdNodes) {
+		this.createdNodes = createdNodes;
+	}
     
 	/**
 	 * Execute the rule as described in the class comments.
@@ -286,8 +296,9 @@
                     if (!RuleRaiseAccess.canRaiseOverSelect(currentNode, metadata, capFinder, critNode, null)) {
                         return currentNode;
                     }
-                    
-                    satisfyAccessPatterns(critNode, currentNode);
+                    if (this.createdNodes == null) {
+                    	satisfyAccessPatterns(critNode, currentNode);
+                    }
 
                     if (critNode.hasBooleanProperty(NodeConstants.Info.IS_DEPENDENT_SET) 
                     		&& CapabilitiesUtil.getMaxInCriteriaSize(RuleRaiseAccess.getModelIDFromAccess(currentNode, metadata), metadata, capFinder) > 0) {
@@ -302,7 +313,7 @@
 				}
 			} else if(currentNode.getType() == NodeConstants.Types.JOIN) {
 				//pushing below a join is not necessary under an access node
-				if (NodeEditor.findParent(currentNode, NodeConstants.Types.ACCESS) != null) {
+				if (this.createdNodes == null && NodeEditor.findParent(currentNode, NodeConstants.Types.ACCESS) != null) {
 					return currentNode;
 				}
 				
@@ -477,6 +488,9 @@
         if(critNode.hasBooleanProperty(NodeConstants.Info.IS_DEPENDENT_SET)) {
             copyNode.setProperty(NodeConstants.Info.IS_DEPENDENT_SET, Boolean.TRUE);
         }
+        if (createdNodes != null) {
+        	createdNodes.add(copyNode);
+        }
 	    return copyNode;
 	}
 

Modified: trunk/engine/src/main/java/org/teiid/query/processor/relational/RelationalNode.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/processor/relational/RelationalNode.java	2011-03-11 17:39:21 UTC (rev 2990)
+++ trunk/engine/src/main/java/org/teiid/query/processor/relational/RelationalNode.java	2011-03-16 01:26:46 UTC (rev 2991)
@@ -421,7 +421,7 @@
         for (int row = batch.getBeginRow(); row <= batch.getEndRow(); row++) {
         	str.append("\t").append(row).append(": ").append(batch.getTuple(row)).append("\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 
         }
-        LogManager.logDetail(org.teiid.logging.LogConstants.CTX_DQP, str.toString());
+        LogManager.logTrace(org.teiid.logging.LogConstants.CTX_DQP, str.toString());
     }
 
     // =========================================================================

Modified: trunk/engine/src/main/java/org/teiid/query/rewriter/QueryRewriter.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/rewriter/QueryRewriter.java	2011-03-11 17:39:21 UTC (rev 2990)
+++ trunk/engine/src/main/java/org/teiid/query/rewriter/QueryRewriter.java	2011-03-16 01:26:46 UTC (rev 2991)
@@ -703,7 +703,7 @@
 				continue;
 			}
 			ResolverUtil.findKeyPreserved(query, keyPreservingGroups, metadata);
-			if (!NewCalculateCostUtil.usesKey(plannedResult.leftExpressions, keyPreservingGroups, metadata)) {
+			if (!NewCalculateCostUtil.usesKey(plannedResult.leftExpressions, keyPreservingGroups, metadata, true)) {
 				continue;
 			}
 			crits.remove();

Modified: trunk/engine/src/main/java/org/teiid/query/sql/lang/DependentSetCriteria.java
===================================================================
--- trunk/engine/src/main/java/org/teiid/query/sql/lang/DependentSetCriteria.java	2011-03-11 17:39:21 UTC (rev 2990)
+++ trunk/engine/src/main/java/org/teiid/query/sql/lang/DependentSetCriteria.java	2011-03-16 01:26:46 UTC (rev 2991)
@@ -127,7 +127,9 @@
         }
 
         DependentSetCriteria criteriaCopy = new DependentSetCriteria(copy, id);
-        criteriaCopy.setValueExpression((Expression) getValueExpression().clone());
+        if (this.valueExpression != null) {
+        	criteriaCopy.setValueExpression((Expression) getValueExpression().clone());
+        }
         criteriaCopy.id = this.id;
         return criteriaCopy;
     }

Modified: trunk/engine/src/test/java/org/teiid/dqp/internal/process/TestDQPCore.java
===================================================================
--- trunk/engine/src/test/java/org/teiid/dqp/internal/process/TestDQPCore.java	2011-03-11 17:39:21 UTC (rev 2990)
+++ trunk/engine/src/test/java/org/teiid/dqp/internal/process/TestDQPCore.java	2011-03-16 01:26:46 UTC (rev 2991)
@@ -332,7 +332,7 @@
 
         Future<ResultsMessage> message = core.executeRequest(reqMsg.getExecutionId(), reqMsg);
         assertNotNull(core.getClientState(String.valueOf(sessionid), false));
-        ResultsMessage results = message.get(500000, TimeUnit.MILLISECONDS);
+        ResultsMessage results = message.get(5000, TimeUnit.MILLISECONDS);
         core.terminateSession(String.valueOf(sessionid));
         assertNull(core.getClientState(String.valueOf(sessionid), false));
         if (results.getException() != null) {

Modified: trunk/engine/src/test/java/org/teiid/query/optimizer/TestAggregatePushdown.java
===================================================================
--- trunk/engine/src/test/java/org/teiid/query/optimizer/TestAggregatePushdown.java	2011-03-11 17:39:21 UTC (rev 2990)
+++ trunk/engine/src/test/java/org/teiid/query/optimizer/TestAggregatePushdown.java	2011-03-16 01:26:46 UTC (rev 2991)
@@ -629,9 +629,9 @@
         ProcessorPlan plan = helpPlan(sql,  
                                       metadata,
                                       null, capFinder,
-                                      new String[] {"SELECT g_0.\"MONTH\", g_0.\"YEAR\" FROM msModel.\"TIME\" AS g_0 WHERE g_0.\"YEAR\" = '1999'", //$NON-NLS-1$
-                                                    "SELECT g_0.\"MONTH\" AS c_0, g_0.CITY AS c_1, SUM(g_0.SALES) AS c_2 FROM db2model.SALES AS g_0 WHERE (g_0.\"MONTH\" IN (<dependent values>)) AND (g_0.CITY IN (<dependent values>)) GROUP BY g_0.\"MONTH\", g_0.CITY ORDER BY c_0, c_1", //$NON-NLS-1$ 
-                                                    "SELECT g_0.CITY, g_0.REGION FROM oraclemodel.GEOGRAPHY AS g_0 WHERE g_0.REGION IN ('BORDEAUX', 'POLINESIA')"},  //$NON-NLS-1$
+                                      new String[] {"SELECT g_0.\"MONTH\" AS c_0, g_0.\"YEAR\" AS c_1 FROM msModel.\"TIME\" AS g_0 WHERE g_0.\"YEAR\" = '1999' ORDER BY c_0", //$NON-NLS-1$
+                                                    "SELECT g_0.\"MONTH\" AS c_0, g_0.CITY AS c_1, SUM(g_0.SALES) AS c_2 FROM db2model.SALES AS g_0 WHERE (g_0.\"MONTH\" IN (<dependent values>)) AND (g_0.CITY IN (<dependent values>)) GROUP BY g_0.\"MONTH\", g_0.CITY ORDER BY c_0", //$NON-NLS-1$ 
+                                                    "SELECT g_0.CITY AS c_0, g_0.REGION AS c_1 FROM oraclemodel.GEOGRAPHY AS g_0 WHERE g_0.REGION IN ('BORDEAUX', 'POLINESIA') ORDER BY c_0"},  //$NON-NLS-1$
                                       ComparisonMode.EXACT_COMMAND_STRING );
 
         checkNodeTypes(plan, new int[] {
@@ -641,8 +641,8 @@
                                         0,      // DependentProject
                                         0,      // DupRemove
                                         1,      // Grouping
-                                        1,      // NestedLoopJoinStrategy
-                                        1,      // MergeJoinStrategy
+                                        0,      // NestedLoopJoinStrategy
+                                        2,      // MergeJoinStrategy
                                         0,      // Null
                                         0,      // PlanExecution
                                         1,      // Project
@@ -677,9 +677,9 @@
         ProcessorPlan plan = helpPlan(sql,  
                                       metadata,
                                       null, capFinder,
-                                      new String[] {"SELECT g_0.\"MONTH\", g_0.\"YEAR\" FROM msModel.\"TIME\" AS g_0 WHERE g_0.\"YEAR\" = '1999'", //$NON-NLS-1$
-                                          "SELECT g_0.\"MONTH\" AS c_0, g_0.CITY AS c_1, SUM(g_0.SALES) AS c_2 FROM db2model.SALES AS g_0 WHERE (g_0.\"MONTH\" IN (<dependent values>)) AND (g_0.CITY IN (<dependent values>)) GROUP BY g_0.\"MONTH\", g_0.CITY ORDER BY c_0, c_1", //$NON-NLS-1$ 
-                                          "SELECT g_0.CITY, g_0.REGION FROM oraclemodel.GEOGRAPHY AS g_0 WHERE g_0.REGION IN ('BORDEAUX', 'POLINESIA')"},  //$NON-NLS-1$
+                                      new String[] {"SELECT g_0.\"MONTH\" AS c_0, g_0.\"YEAR\" AS c_1 FROM msModel.\"TIME\" AS g_0 WHERE g_0.\"YEAR\" = '1999' ORDER BY c_0", //$NON-NLS-1$
+                                          "SELECT g_0.\"MONTH\" AS c_0, g_0.CITY AS c_1, SUM(g_0.SALES) AS c_2 FROM db2model.SALES AS g_0 WHERE (g_0.\"MONTH\" IN (<dependent values>)) AND (g_0.CITY IN (<dependent values>)) GROUP BY g_0.\"MONTH\", g_0.CITY ORDER BY c_0", //$NON-NLS-1$ 
+                                          "SELECT g_0.CITY AS c_0, g_0.REGION AS c_1 FROM oraclemodel.GEOGRAPHY AS g_0 WHERE g_0.REGION IN ('BORDEAUX', 'POLINESIA') ORDER BY c_0"},  //$NON-NLS-1$
                                       ComparisonMode.EXACT_COMMAND_STRING );
 
         checkNodeTypes(plan, new int[] {
@@ -689,8 +689,8 @@
                                         0,      // DependentProject
                                         0,      // DupRemove
                                         1,      // Grouping
-                                        1,      // NestedLoopJoinStrategy
-                                        1,      // MergeJoinStrategy
+                                        0,      // NestedLoopJoinStrategy
+                                        2,      // MergeJoinStrategy
                                         0,      // Null
                                         0,      // PlanExecution
                                         1,      // Project

Modified: trunk/engine/src/test/java/org/teiid/query/optimizer/TestDependentJoins.java
===================================================================
--- trunk/engine/src/test/java/org/teiid/query/optimizer/TestDependentJoins.java	2011-03-11 17:39:21 UTC (rev 2990)
+++ trunk/engine/src/test/java/org/teiid/query/optimizer/TestDependentJoins.java	2011-03-16 01:26:46 UTC (rev 2991)
@@ -26,7 +26,6 @@
 
 import java.util.Collection;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.Set;
 
 import org.junit.Test;
@@ -59,11 +58,11 @@
         }
                                 
         // Collect all the group names (uppercase) for all the dependent groups in the plan
-        Set depGroups = new HashSet();
-        getDependentGroups(((RelationalPlan)plan).getRootNode(), depGroups);
+        Set<String> depGroups = new HashSet<String>();
+        getDependentGroups(((RelationalPlan)plan).getRootNode(), depGroups, true);
 
         // Check that all the expected groups exist in depGroups
-        Set expectedGroups = new HashSet();
+        Set<String> expectedGroups = new HashSet<String>();
         for(int i=0; i<groups.length; i++) {
             expectedGroups.add(groups[i].toUpperCase());    
         }        
@@ -71,15 +70,20 @@
         assertEquals("Expected groups were not made dependent", expectedGroups, depGroups);         //$NON-NLS-1$
     }
     
-    static void getDependentGroups(RelationalNode node, Set depGroups) {
-        if(node instanceof DependentAccessNode) {
-            DependentAccessNode accessNode = (DependentAccessNode)node;
+    static void getDependentGroups(RelationalNode node, Set<String> depGroups, boolean depdenent) {
+        if(node instanceof AccessNode) {
+        	if (node instanceof DependentAccessNode) {
+        		if (!depdenent) {
+        			return;
+        		}
+        	} else if (depdenent) {
+        		return;
+        	}
+            AccessNode accessNode = (AccessNode)node;
             Command depCommand = accessNode.getCommand();
-            Collection groupSymbols = GroupCollectorVisitor.getGroups(depCommand, true);
-            Iterator groupIter = groupSymbols.iterator();
-            while(groupIter.hasNext()) {
-                GroupSymbol group = (GroupSymbol) groupIter.next();
-                depGroups.add(group.getName().toUpperCase());    
+            Collection<GroupSymbol> groupSymbols = GroupCollectorVisitor.getGroups(depCommand, true);
+            for (GroupSymbol groupSymbol : groupSymbols) {
+        		depGroups.add(groupSymbol.getName().toUpperCase());
             }
         }
         
@@ -87,7 +91,7 @@
         RelationalNode[] children = node.getChildren();
         for(int i=0; i<children.length; i++) {
             if(children[i] != null) {
-                getDependentGroups(node.getChildren()[i], depGroups);                
+                getDependentGroups(node.getChildren()[i], depGroups, depdenent);                
             }
         }
     }
@@ -98,11 +102,11 @@
         }
                                 
         // Collect all the group names (uppercase) for all the dependent groups in the plan
-        Set notDepGroups = new HashSet();
-        getNotDependentGroups(((RelationalPlan)plan).getRootNode(), notDepGroups);
+        Set<String> notDepGroups = new HashSet<String>();
+        getDependentGroups(((RelationalPlan)plan).getRootNode(), notDepGroups, false);
 
         // Check that all the expected groups exist in depGroups
-        Set expectedGroups = new HashSet();
+        Set<String> expectedGroups = new HashSet<String>();
         for(int i=0; i<groups.length; i++) {
             expectedGroups.add(groups[i].toUpperCase());    
         }        
@@ -110,27 +114,6 @@
         assertEquals("Expected groups were made dependent", expectedGroups, notDepGroups);         //$NON-NLS-1$
     }
     
-    private void getNotDependentGroups(RelationalNode node, Set notDepGroups) {
-        if(node instanceof AccessNode && !(node instanceof DependentAccessNode)) {
-            AccessNode accessNode = (AccessNode)node;
-            Command depCommand = accessNode.getCommand();
-            Collection groupSymbols = GroupCollectorVisitor.getGroups(depCommand, true);
-            Iterator groupIter = groupSymbols.iterator();
-            while(groupIter.hasNext()) {
-                GroupSymbol group = (GroupSymbol) groupIter.next();
-                notDepGroups.add(group.getName().toUpperCase());    
-            }
-        }
-        
-        // Recurse through children
-        RelationalNode[] children = node.getChildren();
-        for(int i=0; i<children.length; i++) {
-            if(children[i] != null) {
-                getNotDependentGroups(node.getChildren()[i], notDepGroups);                
-            }
-        }
-    }
-    
     @Test public void testOptionMakeDep1() throws Exception {
 
         FakeCapabilitiesFinder capFinder = new FakeCapabilitiesFinder();
@@ -823,10 +806,13 @@
         capFinder.addCapabilities("BQT2", caps); //$NON-NLS-1$
 
         TransformationMetadata metadata = FakeMetadataFactory.exampleBQT();
-        FakeMetadataFactory.setCardinality("bqt1.smalla", 3000, metadata); //$NON-NLS-1$
+        FakeMetadataFactory.setCardinality("bqt1.smalla", 1000, metadata); //$NON-NLS-1$
         FakeMetadataFactory.setCardinality("bqt2.smalla", 10000, metadata); //$NON-NLS-1$
         Column fmo = (Column)metadata.getElementID("bqt1.smalla.intnum");
 		fmo.setDistinctValues(1000);
+        Column floatnum = (Column)metadata.getElementID("bqt1.smalla.floatnum");
+        floatnum.setDistinctValues(800);
+
         ProcessorPlan plan = TestOptimizer.helpPlan(
             "SELECT max(a.stringkey) from bqt1.smalla a, bqt2.smalla a2, bqt1.smalla a1 where a.intnum = a2.intnum and a1.stringnum = a2.stringnum and a.floatnum = a1.floatnum",  //$NON-NLS-1$
             metadata,
@@ -852,5 +838,5 @@
         });             
 
     } 
-
+    
 }

Modified: trunk/engine/src/test/java/org/teiid/query/optimizer/TestOptimizer.java
===================================================================
--- trunk/engine/src/test/java/org/teiid/query/optimizer/TestOptimizer.java	2011-03-11 17:39:21 UTC (rev 2990)
+++ trunk/engine/src/test/java/org/teiid/query/optimizer/TestOptimizer.java	2011-03-16 01:26:46 UTC (rev 2991)
@@ -3287,7 +3287,7 @@
             0,      // Null
             0,      // PlanExecution
             1,      // Project
-            1,      // Select
+            0,      // Select
             0,      // Sort
             0       // UnionAll
         });         

Modified: trunk/engine/src/test/java/org/teiid/query/optimizer/TestPartitionedJoinPlanning.java
===================================================================
--- trunk/engine/src/test/java/org/teiid/query/optimizer/TestPartitionedJoinPlanning.java	2011-03-11 17:39:21 UTC (rev 2990)
+++ trunk/engine/src/test/java/org/teiid/query/optimizer/TestPartitionedJoinPlanning.java	2011-03-16 01:26:46 UTC (rev 2991)
@@ -53,7 +53,7 @@
 
         FakeMetadataFacade metadata = FakeMetadataFactory.example1();
         FakeMetadataObject g1 = metadata.getStore().findObject("pm1.g1", FakeMetadataObject.GROUP); //$NON-NLS-1$
-        g1.putProperty(FakeMetadataObject.Props.CARDINALITY, BufferManager.DEFAULT_PROCESSOR_BATCH_SIZE);
+        g1.putProperty(FakeMetadataObject.Props.CARDINALITY, BufferManager.DEFAULT_PROCESSOR_BATCH_SIZE * 2);
         FakeMetadataObject g2 = metadata.getStore().findObject("pm1.g2", FakeMetadataObject.GROUP); //$NON-NLS-1$
         g2.putProperty(FakeMetadataObject.Props.CARDINALITY, BufferManager.DEFAULT_PROCESSOR_BATCH_SIZE * 16);
     

Modified: trunk/engine/src/test/java/org/teiid/query/optimizer/relational/rules/TestRuleChooseDependent.java
===================================================================
--- trunk/engine/src/test/java/org/teiid/query/optimizer/relational/rules/TestRuleChooseDependent.java	2011-03-11 17:39:21 UTC (rev 2990)
+++ trunk/engine/src/test/java/org/teiid/query/optimizer/relational/rules/TestRuleChooseDependent.java	2011-03-16 01:26:46 UTC (rev 2991)
@@ -22,6 +22,8 @@
 
 package org.teiid.query.optimizer.relational.rules;
 
+import static org.junit.Assert.*;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -30,6 +32,7 @@
 import java.util.LinkedList;
 import java.util.List;
 
+import org.junit.Test;
 import org.teiid.api.exception.query.QueryMetadataException;
 import org.teiid.api.exception.query.QueryPlannerException;
 import org.teiid.core.TeiidComponentException;
@@ -40,10 +43,6 @@
 import org.teiid.query.optimizer.relational.plantree.NodeConstants;
 import org.teiid.query.optimizer.relational.plantree.NodeFactory;
 import org.teiid.query.optimizer.relational.plantree.PlanNode;
-import org.teiid.query.optimizer.relational.rules.FrameUtil;
-import org.teiid.query.optimizer.relational.rules.RuleChooseDependent;
-import org.teiid.query.optimizer.relational.rules.RuleChooseJoinStrategy;
-import org.teiid.query.optimizer.relational.rules.RulePlaceAccess;
 import org.teiid.query.processor.relational.JoinNode.JoinStrategyType;
 import org.teiid.query.rewriter.QueryRewriter;
 import org.teiid.query.sql.lang.CompareCriteria;
@@ -65,11 +64,8 @@
 import org.teiid.query.unittest.FakeMetadataObject;
 import org.teiid.query.util.CommandContext;
 
-import junit.framework.TestCase;
+public class TestRuleChooseDependent {
 
-
-public class TestRuleChooseDependent extends TestCase {
-
     /* Make Left Side Dependent */
     private static final int LEFT_SIDE = 1;
     /* Make Right Side Dependent */
@@ -79,12 +75,6 @@
 
     private FakeMetadataFacade metadata = FakeMetadataFactory.example1Cached();
     
-    // ################################## FRAMEWORK ################################
-    
-    public TestRuleChooseDependent(String name) { 
-        super(name);
-    }    
-
     // ################################## TEST HELPERS ################################
     
     public PlanNode createAccessNode(Collection groupSymbols) {
@@ -413,7 +403,7 @@
     
     // ################################## ACTUAL TESTS ################################
     
-    public void testValidJoin1() {
+    @Test public void testValidJoin1() {
         PlanNode accessNode = NodeFactory.getNewNode(NodeConstants.Types.ACCESS);                       
         accessNode.addGroup(getPhysicalGroup(1));
                         
@@ -424,7 +414,7 @@
         helpTestValidJoin(joinNode, accessNode, false);
     }
 
-    public void testValidJoin2() {
+    @Test public void testValidJoin2() {
         PlanNode accessNode = NodeFactory.getNewNode(NodeConstants.Types.ACCESS);                       
         accessNode.addGroup(getPhysicalGroup(1));
                         
@@ -436,7 +426,7 @@
         helpTestValidJoin(joinNode, accessNode, false);
     }
 
-    public void testValidJoin3() {
+    @Test public void testValidJoin3() {
         PlanNode accessNode1 = NodeFactory.getNewNode(NodeConstants.Types.ACCESS);                       
         PlanNode accessNode2 = NodeFactory.getNewNode(NodeConstants.Types.ACCESS);                       
         accessNode1.addGroup(getPhysicalGroup(1));                
@@ -453,7 +443,7 @@
         helpTestValidJoin(joinNode, accessNode1, true);
     }
 
-    public void testValidJoin4() {
+    @Test public void testValidJoin4() {
         PlanNode accessNode1 = NodeFactory.getNewNode(NodeConstants.Types.ACCESS);                       
         PlanNode accessNode2 = NodeFactory.getNewNode(NodeConstants.Types.ACCESS);                       
                         
@@ -468,7 +458,7 @@
         helpTestValidJoin(joinNode, accessNode2, false);
     }
 
-    public void testValidJoin5() {
+    @Test public void testValidJoin5() {
         PlanNode accessNode1 = NodeFactory.getNewNode(NodeConstants.Types.ACCESS);                       
         PlanNode accessNode2 = NodeFactory.getNewNode(NodeConstants.Types.ACCESS);                       
                         
@@ -483,7 +473,7 @@
         helpTestValidJoin(joinNode, accessNode1, false);
     }
 
-    public void testValidJoin6() {
+    @Test public void testValidJoin6() {
         PlanNode accessNode1 = NodeFactory.getNewNode(NodeConstants.Types.ACCESS);                       
         PlanNode accessNode2 = NodeFactory.getNewNode(NodeConstants.Types.ACCESS);
         accessNode1.addGroup(getPhysicalGroup(1));                
@@ -504,7 +494,7 @@
      * Tests that heuristics will take a primary key in the atomic criteria into account when 
      * making a dependent join.
      */
-    public void testChooseKey() throws Exception {
+    @Test public void testChooseKey() throws Exception {
         //override default metadata
         this.metadata = FakeMetadataFactory.example4();
         
@@ -529,7 +519,7 @@
     /**
      * Neither side should be chosen since the left side lacks cardinality information and the right is not strong
      */
-    public void testChooseKey2() throws Exception {
+    @Test public void testChooseKey2() throws Exception {
         //override default metadata
         this.metadata = FakeMetadataFactory.example4();
         
@@ -583,7 +573,7 @@
      * Tests that heuristics will take cardinality of a group into account when 
      * making a dependent join.
      */
-    public void testCardinality() throws Exception {
+    @Test public void testCardinality() throws Exception {
         //override default metadata
         this.metadata = FakeMetadataFactory.example4();
         
@@ -614,7 +604,7 @@
      * making a dependent join, and that this information supercedes a key
      * in the atomic criteria.
      */
-    public void testCardinalityAndKey() throws Exception {
+    @Test public void testCardinalityAndKey() throws Exception {
         //override default metadata
         this.metadata = FakeMetadataFactory.example4();
         
@@ -640,7 +630,7 @@
         helpTestChooseSiblingAndMarkDependent(group2, atomicCrit2, group1, atomicCrit1, crits, expected);        
     }
     
-    public void testCardinalityAndKeyNestedLoop() throws Exception {
+    @Test public void testCardinalityAndKeyNestedLoop() throws Exception {
         //override default metadata
         this.metadata = FakeMetadataFactory.example4();
         
@@ -666,7 +656,7 @@
         helpTestChooseSiblingAndMarkDependent(group2, atomicCrit2, group1, atomicCrit1, crits, expected);        
     }
     
-    public void testRejectDependentJoin() throws Exception {
+    @Test public void testRejectDependentJoin() throws Exception {
         //override default metadata
         this.metadata = FakeMetadataFactory.example4();
         
@@ -692,7 +682,7 @@
      * Tests that join side with larger cardinality will still have a lower
      * cost computed because it has a criteria including a primary key
      */
-    public void testCardinalityWithKeyCrit() throws Exception {
+    @Test public void testCardinalityWithKeyCrit() throws Exception {
         //override default metadata
         this.metadata = FakeMetadataFactory.example4();
         
@@ -720,7 +710,7 @@
      * Tests that join side with larger cardinality will still have a lower
      * cost computed because it has a criteria including a primary key
      */
-    public void testCardinalityWithKeyCompoundCritAND() throws Exception {
+    @Test public void testCardinalityWithKeyCompoundCritAND() throws Exception {
         //override default metadata
         this.metadata = FakeMetadataFactory.example4();
         
@@ -752,7 +742,7 @@
      * cost computed because it has a criteria including a primary key.
      * Defect 8445
      */
-    public void testCardinalityWithKeyCompoundCritOR() throws Exception {
+    @Test public void testCardinalityWithKeyCompoundCritOR() throws Exception {
         //override default metadata
         this.metadata = FakeMetadataFactory.example4();
         
@@ -782,7 +772,7 @@
     /**
      * Tests SetCriteria against a key column in the atomic criteria
      */
-    public void testCardinalityWithKeySetCrit() throws Exception {
+    @Test public void testCardinalityWithKeySetCrit() throws Exception {
         //override default metadata
         this.metadata = FakeMetadataFactory.example4();
         
@@ -813,7 +803,7 @@
     /**
      * Tests SetCriteria in the atomic criteria
      */
-    public void testCardinalityWithKeyMatchCrit() throws Exception {
+    @Test public void testCardinalityWithKeyMatchCrit() throws Exception {
         //override default metadata
         this.metadata = FakeMetadataFactory.example4();
         
@@ -840,7 +830,7 @@
     /**
      * Tests SetCriteria in the atomic criteria
      */
-    public void testCardinalityWithKeyIsNullCrit() throws Exception {
+    @Test public void testCardinalityWithKeyIsNullCrit() throws Exception {
         //override default metadata
         this.metadata = FakeMetadataFactory.example4();
         
@@ -867,7 +857,7 @@
     /**
      * Tests NotCriteria in the atomic criteria
      */
-    public void testCardinalityWithKeyNotCrit() throws Exception {
+    @Test public void testCardinalityWithKeyNotCrit() throws Exception {
         //override default metadata
         this.metadata = FakeMetadataFactory.example4();
         
@@ -896,7 +886,7 @@
      * Tests that join side with larger cardinality will still have a lower
      * cost computed because it has a criteria including a primary key
      */
-    public void testCardinalityWithKeyComplexCrit() throws Exception {
+    @Test public void testCardinalityWithKeyComplexCrit() throws Exception {
         //override default metadata
         this.metadata = FakeMetadataFactory.example4();
         
@@ -925,7 +915,7 @@
         helpTestChooseSiblingAndMarkDependent(group2, atomicCrit2, group1, atomicCrit1, crits, expected);        
     }  
 
-    public void testCardinalityWithKeyComplexCrit2() throws Exception {
+    @Test public void testCardinalityWithKeyComplexCrit2() throws Exception {
         //override default metadata
         this.metadata = FakeMetadataFactory.example4();
         
@@ -955,7 +945,7 @@
         helpTestChooseSiblingAndMarkDependent(group2, atomicCrit2, group1, atomicCrit1, crits, expected);        
     } 
 
-    public void testCardinalityWithKeyComplexCrit3() throws Exception {
+    @Test public void testCardinalityWithKeyComplexCrit3() throws Exception {
         //override default metadata
         this.metadata = FakeMetadataFactory.example4();
         
@@ -989,7 +979,7 @@
      * Tests that join side with larger cardinality and non-key criteria
      * will be made dependent
      */
-    public void testCardinalityWithNonKeyCrit() throws Exception {
+    @Test public void testCardinalityWithNonKeyCrit() throws Exception {
         //override default metadata
         this.metadata = FakeMetadataFactory.example4();
         
@@ -1018,7 +1008,7 @@
      * Tests that join side with larger cardinality will still have a lower
      * cost computed because it has a criteria including a primary key
      */
-    public void testCardinalityWithCriteriaAndJoin() throws Exception {
+    @Test public void testCardinalityWithCriteriaAndJoin() throws Exception {
         //override default metadata
         this.metadata = FakeMetadataFactory.example4();
         
@@ -1069,7 +1059,7 @@
          expected, 1000, 1);        
     }    
 
-    public void testCardinalityWithAtomicCrossJoin() throws Exception {
+    @Test public void testCardinalityWithAtomicCrossJoin() throws Exception {
         //override default metadata
         this.metadata = FakeMetadataFactory.example4();
         
@@ -1098,7 +1088,7 @@
         //atomic Join criteria 2
         List atomicJoinCrits2 = Collections.EMPTY_LIST; //INDICATES CROSS JOIN
         
-        int expected = RIGHT_SIDE;
+        int expected = NEITHER_SIDE;
 
         helpTestChooseSiblingAndMarkDependent(
          group1, 
@@ -1115,7 +1105,7 @@
          expected, 1000, 1E5);        
     } 
     
-    public void testCardinalityWithAtomicCrossJoin2() throws Exception {
+    @Test public void testCardinalityWithAtomicCrossJoin2() throws Exception {
         //override default metadata
         this.metadata = FakeMetadataFactory.example4();
         

Modified: trunk/engine/src/test/java/org/teiid/query/processor/TestVirtualDepJoin.java
===================================================================
--- trunk/engine/src/test/java/org/teiid/query/processor/TestVirtualDepJoin.java	2011-03-11 17:39:21 UTC (rev 2990)
+++ trunk/engine/src/test/java/org/teiid/query/processor/TestVirtualDepJoin.java	2011-03-16 01:26:46 UTC (rev 2991)
@@ -247,6 +247,7 @@
         Command command = TestProcessor.helpParse(sql);   
         FakeCapabilitiesFinder finder = new FakeCapabilitiesFinder();
         BasicSourceCapabilities caps = TestOptimizer.getTypicalCapabilities();
+        caps.setCapabilitySupport(Capability.QUERY_ORDERBY, false);
         caps.setCapabilitySupport(Capability.QUERY_FROM_JOIN_INNER, false);
         caps.setCapabilitySupport(Capability.CRITERIA_IN, setPushdown);    
         finder.addCapabilities("US", caps); //$NON-NLS-1$
@@ -255,8 +256,8 @@
         ProcessorPlan plan = TestProcessor.helpGetPlan(command, exampleVirtualDepJoin(), finder, context); 
          
         // Check plan contents
-        int selectCount = !setPushdown ? 2 : 0;
-        int accessCount = setPushdown ? 2 : 4;
+        int selectCount = !setPushdown ? 3 : 0;
+        int accessCount = setPushdown ? 1 : 4;
         int depAccessCount = 4 - accessCount;
         TestOptimizer.checkNodeTypes(plan, new int[] {
             accessCount,      // Access

Modified: trunk/engine/src/test/java/org/teiid/query/processor/xml/TestXMLPlanningEnhancements.java
===================================================================
--- trunk/engine/src/test/java/org/teiid/query/processor/xml/TestXMLPlanningEnhancements.java	2011-03-11 17:39:21 UTC (rev 2990)
+++ trunk/engine/src/test/java/org/teiid/query/processor/xml/TestXMLPlanningEnhancements.java	2011-03-16 01:26:46 UTC (rev 2991)
@@ -484,7 +484,7 @@
 
         // supply the costing information for OrdersC
         orders.putProperty(FakeMetadataObject.Props.CARDINALITY, BufferManager.DEFAULT_PROCESSOR_BATCH_SIZE - 1);
-        suppliers.putProperty(FakeMetadataObject.Props.CARDINALITY, RuleChooseDependent.DEFAULT_INDEPENDENT_CARDINALITY);        
+        suppliers.putProperty(FakeMetadataObject.Props.CARDINALITY, RuleChooseDependent.DEFAULT_INDEPENDENT_CARDINALITY - 1);        
 
         String expectedDoc = TestXMLProcessor.readFile("TestXMLProcessor-FullSuppliers.xml"); //$NON-NLS-1$
         

Modified: trunk/engine/src/test/java/org/teiid/query/unittest/RealMetadataFactory.java
===================================================================
--- trunk/engine/src/test/java/org/teiid/query/unittest/RealMetadataFactory.java	2011-03-11 17:39:21 UTC (rev 2990)
+++ trunk/engine/src/test/java/org/teiid/query/unittest/RealMetadataFactory.java	2011-03-16 01:26:46 UTC (rev 2991)
@@ -464,6 +464,9 @@
 		case Index:
 			group.getIndexes().add(key);
 			break;
+		case Unique:
+			group.getUniqueKeys().add(key);
+			break;
 		default:
 			throw new AssertionError("TODO");
 		}

Added: trunk/test-integration/common/src/test/java/org/teiid/dqp/internal/process/TestCrossSourceStarJoin.java
===================================================================
--- trunk/test-integration/common/src/test/java/org/teiid/dqp/internal/process/TestCrossSourceStarJoin.java	                        (rev 0)
+++ trunk/test-integration/common/src/test/java/org/teiid/dqp/internal/process/TestCrossSourceStarJoin.java	2011-03-16 01:26:46 UTC (rev 2991)
@@ -0,0 +1,111 @@
+/*
+ * 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.dqp.internal.process;
+
+import static org.teiid.query.unittest.RealMetadataFactory.*;
+
+import java.util.List;
+
+import org.junit.Test;
+import org.teiid.core.types.DataTypeManager;
+import org.teiid.dqp.internal.datamgr.CapabilitiesConverter;
+import org.teiid.metadata.Column;
+import org.teiid.metadata.KeyRecord;
+import org.teiid.metadata.MetadataStore;
+import org.teiid.metadata.Schema;
+import org.teiid.metadata.Table;
+import org.teiid.query.metadata.TransformationMetadata;
+import org.teiid.query.optimizer.TestOptimizer;
+import org.teiid.query.optimizer.TestOptimizer.ComparisonMode;
+import org.teiid.query.optimizer.capabilities.FakeCapabilitiesFinder;
+import org.teiid.query.unittest.RealMetadataFactory;
+import org.teiid.translator.jdbc.oracle.OracleExecutionFactory;
+import org.teiid.translator.jdbc.sybase.SybaseExecutionFactory;
+
+ at SuppressWarnings("nls")
+public class TestCrossSourceStarJoin {
+	
+    @Test public void testCrossSourceStartJoin() throws Exception {
+        String sql = "select p.Description, sum(AMOUNT) from s3 p, s2 c, s1 b, o1 f " +
+        		"where p.PRODUCTID = f.PRODUCT and c.CurrencyCode = f.CURRENCY and b.BOOKID = f.BOOK and b.Name = 'xyz' and c.Name = 'abc' Group by p.Description";
+        
+        MetadataStore metadataStore = new MetadataStore();
+    	
+        Schema oracle = createPhysicalModel("oracle", metadataStore); //$NON-NLS-1$
+        Schema sybase = createPhysicalModel("sybase", metadataStore); //$NON-NLS-1$
+        
+        // Create physical groups
+        Table f = createPhysicalGroup("o1", oracle); //$NON-NLS-1$
+        f.setCardinality(5276965);
+        Table b = createPhysicalGroup("s1", sybase); //$NON-NLS-1$
+        b.setCardinality(141496);
+        Table c = createPhysicalGroup("s2", sybase); //$NON-NLS-1$
+        c.setCardinality(228);
+        Table p = createPhysicalGroup("s3", sybase); //$NON-NLS-1$
+        p.setCardinality(200);
+        
+        List<Column> f_cols = createElements(f,
+                new String[] { "PRODUCT", "CURRENCY", "BOOK", "AMOUNT"}, //$NON-NLS-1$
+                new String[] { DataTypeManager.DefaultDataTypes.STRING, DataTypeManager.DefaultDataTypes.INTEGER, DataTypeManager.DefaultDataTypes.STRING, DataTypeManager.DefaultDataTypes.BIG_DECIMAL});
+
+        f_cols.get(0).setDistinctValues(200);
+        f_cols.get(1).setDistinctValues(228);
+        f_cols.get(2).setDistinctValues(141496);
+        createKey(KeyRecord.Type.Index, "idx_p", f, f_cols.subList(0, 1));
+        createKey(KeyRecord.Type.Index, "idx_c", f, f_cols.subList(1, 2));
+        createKey(KeyRecord.Type.Index, "idx_b", f, f_cols.subList(2, 3));
+        
+        List<Column> b_cols = createElements(b,
+                new String[] { "BOOKID", "Name"}, //$NON-NLS-1$
+                new String[] { DataTypeManager.DefaultDataTypes.STRING, DataTypeManager.DefaultDataTypes.STRING});
+        
+        createKey(KeyRecord.Type.Primary, "pk", b, b_cols.subList(0, 1));
+        //createKey(KeyRecord.Type.Unique, "uk", b, b_cols.subList(1, 2));
+
+        List<Column> c_cols = createElements(c,
+                new String[] { "Name", "CurrencyCode"}, //$NON-NLS-1$
+                new String[] { DataTypeManager.DefaultDataTypes.STRING, DataTypeManager.DefaultDataTypes.INTEGER});
+        
+        createKey(KeyRecord.Type.Primary, "pk", c, c_cols.subList(1, 2));
+        //createKey(KeyRecord.Type.Unique, "uk", c, c_cols.subList(0, 1));
+
+        List<Column> p_cols = createElements(p,
+                new String[] { "PRODUCTID", "Description"}, //$NON-NLS-1$
+                new String[] { DataTypeManager.DefaultDataTypes.STRING, DataTypeManager.DefaultDataTypes.STRING});
+        
+        createKey(KeyRecord.Type.Primary, "pk", p, p_cols.subList(0, 1));
+
+        FakeCapabilitiesFinder finder = new FakeCapabilitiesFinder();
+        finder.addCapabilities("oracle", CapabilitiesConverter.convertCapabilities(new OracleExecutionFactory())); //$NON-NLS-1$
+        finder.addCapabilities("sybase", CapabilitiesConverter.convertCapabilities(new SybaseExecutionFactory())); //$NON-NLS-1$
+
+        TransformationMetadata metadata = RealMetadataFactory.createTransformationMetadata(metadataStore, "star");
+        
+        TestOptimizer.helpPlan(sql, metadata, new String[] {
+        		"SELECT g_0.CurrencyCode AS c_0 FROM sybase.s2 AS g_0 WHERE g_0.Name = 'abc' ORDER BY c_0", 
+        		"SELECT g_0.BOOKID FROM sybase.s1 AS g_0 WHERE g_0.Name = 'xyz'", 
+        		"SELECT g_0.PRODUCTID AS c_0, g_0.Description AS c_1 FROM sybase.s3 AS g_0 ORDER BY c_0", 
+        		"SELECT g_0.CURRENCY AS c_0, g_0.PRODUCT AS c_1, g_0.BOOK AS c_2, SUM(g_0.AMOUNT) AS c_3 FROM oracle.o1 AS g_0 WHERE (g_0.CURRENCY IN (<dependent values>)) AND (g_0.PRODUCT IN (<dependent values>)) AND (g_0.BOOK IN (<dependent values>)) GROUP BY g_0.CURRENCY, g_0.PRODUCT, g_0.BOOK ORDER BY c_0 NULLS FIRST"
+        }, finder, ComparisonMode.EXACT_COMMAND_STRING);
+    } 
+
+}


Property changes on: trunk/test-integration/common/src/test/java/org/teiid/dqp/internal/process/TestCrossSourceStarJoin.java
___________________________________________________________________
Added: svn:mime-type
   + text/plain

Modified: trunk/test-integration/common/src/test/java/org/teiid/dqp/internal/process/TestTPCR.java
===================================================================
--- trunk/test-integration/common/src/test/java/org/teiid/dqp/internal/process/TestTPCR.java	2011-03-11 17:39:21 UTC (rev 2990)
+++ trunk/test-integration/common/src/test/java/org/teiid/dqp/internal/process/TestTPCR.java	2011-03-16 01:26:46 UTC (rev 2991)
@@ -127,7 +127,7 @@
         List[] sqlServerExpected =
             new List[] { Arrays.asList(new Object[] { new Integer(5), new Integer(12), new Long(5) } ),
                          Arrays.asList(new Object[] { new Integer(5), new Integer(13), new Long(5) } )};
-        dataMgr.addData("SELECT g_0.O_CUSTKEY AS c_0, g_0.O_ORDERKEY AS c_1, g_0.O_CUSTKEY AS c_2 FROM TPCR_SQLS.ORDERS AS g_0 WHERE g_0.O_ORDERDATE < {ts'1992-01-02 00:00:00.0'} ORDER BY c_2", //$NON-NLS-1$
+        dataMgr.addData("SELECT g_0.O_CUSTKEY AS c_0, g_0.O_ORDERKEY AS c_1, g_0.O_CUSTKEY AS c_2 FROM TPCR_SQLS.ORDERS AS g_0 WHERE (g_0.O_ORDERDATE < {ts'1992-01-02 00:00:00.0'}) AND (g_0.O_CUSTKEY IN (5, 6)) ORDER BY c_2", //$NON-NLS-1$
                         sqlServerExpected);
         
         List[] expected =
@@ -167,7 +167,7 @@
         List[] sqlServerExpected =
             new List[] { Arrays.asList(new Object[] { new Integer(5), new Integer(12), new Long(5) } ),
                          Arrays.asList(new Object[] { new Integer(5), new Integer(13), new Long(5) } )};
-        dataMgr.addData("SELECT g_0.O_CUSTKEY AS c_0, g_0.O_ORDERKEY AS c_1, g_0.O_CUSTKEY AS c_2 FROM TPCR_SQLS.ORDERS AS g_0 WHERE g_0.O_ORDERDATE < {ts'1992-01-02 00:00:00.0'} ORDER BY c_2", //$NON-NLS-1$
+        dataMgr.addData("SELECT g_0.O_CUSTKEY AS c_0, g_0.O_ORDERKEY AS c_1, g_0.O_CUSTKEY AS c_2 FROM TPCR_SQLS.ORDERS AS g_0 WHERE (g_0.O_ORDERDATE < {ts'1992-01-02 00:00:00.0'}) AND (g_0.O_CUSTKEY IN (5, 6)) ORDER BY c_2", //$NON-NLS-1$
                         sqlServerExpected);
         
         List[] expected =



More information about the teiid-commits mailing list