[teiid-commits] teiid SVN: r762 - in trunk/engine/src: main/java/com/metamatrix/query/optimizer/relational/plantree and 3 other directories.

teiid-commits at lists.jboss.org teiid-commits at lists.jboss.org
Sun Apr 12 21:32:53 EDT 2009


Author: shawkins
Date: 2009-04-12 21:32:50 -0400 (Sun, 12 Apr 2009)
New Revision: 762

Added:
   trunk/engine/src/test/java/com/metamatrix/query/optimizer/TestSubqueryPushdown.java
Modified:
   trunk/engine/src/main/java/com/metamatrix/query/optimizer/relational/RelationalPlanner.java
   trunk/engine/src/main/java/com/metamatrix/query/optimizer/relational/plantree/PlanNode.java
   trunk/engine/src/main/java/com/metamatrix/query/optimizer/relational/rules/FrameUtil.java
   trunk/engine/src/main/java/com/metamatrix/query/optimizer/relational/rules/JoinUtil.java
   trunk/engine/src/main/java/com/metamatrix/query/optimizer/relational/rules/RulePushSelectCriteria.java
   trunk/engine/src/main/java/com/metamatrix/query/optimizer/relational/rules/RuleRaiseAccess.java
   trunk/engine/src/main/java/com/metamatrix/query/optimizer/relational/rules/RuleRemoveOptionalJoins.java
   trunk/engine/src/main/java/com/metamatrix/query/sql/visitor/ElementCollectorVisitor.java
   trunk/engine/src/test/java/com/metamatrix/query/optimizer/TestOptimizer.java
Log:
TEIID-370 adding the ability push subqueries through virtual groups.

Modified: trunk/engine/src/main/java/com/metamatrix/query/optimizer/relational/RelationalPlanner.java
===================================================================
--- trunk/engine/src/main/java/com/metamatrix/query/optimizer/relational/RelationalPlanner.java	2009-04-12 21:32:20 UTC (rev 761)
+++ trunk/engine/src/main/java/com/metamatrix/query/optimizer/relational/RelationalPlanner.java	2009-04-13 01:32:50 UTC (rev 762)
@@ -57,6 +57,7 @@
 import com.metamatrix.query.sql.symbol.SingleElementSymbol;
 import com.metamatrix.query.sql.visitor.CorrelatedReferenceCollectorVisitor;
 import com.metamatrix.query.sql.visitor.GroupCollectorVisitor;
+import com.metamatrix.query.sql.visitor.GroupsUsedByElementsVisitor;
 import com.metamatrix.query.sql.visitor.ValueIteratorProviderCollectorVisitor;
 import com.metamatrix.query.util.CommandContext;
 import com.metamatrix.query.util.ErrorMessageKeys;
@@ -203,6 +204,7 @@
                 node.setProperty(NodeConstants.Info.SUBQUERY_PLANS, plans);
                 node.setProperty(NodeConstants.Info.SUBQUERY_VALUE_PROVIDERS, subqueryContainers);
                 node.setProperty(NodeConstants.Info.CORRELATED_REFERENCES, correlatedReferences);
+                node.addGroups(GroupsUsedByElementsVisitor.getGroups(node.getCorrelatedReferenceElements()));
             }
         }
     }

Modified: trunk/engine/src/main/java/com/metamatrix/query/optimizer/relational/plantree/PlanNode.java
===================================================================
--- trunk/engine/src/main/java/com/metamatrix/query/optimizer/relational/plantree/PlanNode.java	2009-04-12 21:32:20 UTC (rev 761)
+++ trunk/engine/src/main/java/com/metamatrix/query/optimizer/relational/plantree/PlanNode.java	2009-04-13 01:32:50 UTC (rev 762)
@@ -25,6 +25,7 @@
 import java.util.*;
 
 import com.metamatrix.query.sql.symbol.*;
+import com.metamatrix.query.sql.visitor.ElementCollectorVisitor;
 
 public class PlanNode {
 
@@ -276,5 +277,20 @@
     	assert node.getChildCount() == 0;
 		node.addLastChild(this);
     }
+    
+    public Set<ElementSymbol> getCorrelatedReferenceElements() {
+        List<Reference> refs = (List<Reference>) this.getProperty(NodeConstants.Info.CORRELATED_REFERENCES);
         
+        if(refs == null || refs.isEmpty()) {
+            return Collections.emptySet();    
+        }
+        
+        HashSet<ElementSymbol> result = new HashSet<ElementSymbol>(refs.size());
+        for (Reference ref : refs) {
+            Expression expr = ref.getExpression();
+            ElementCollectorVisitor.getElements(expr, result);
+        }
+        return result;
+    }
+        
 }

Modified: trunk/engine/src/main/java/com/metamatrix/query/optimizer/relational/rules/FrameUtil.java
===================================================================
--- trunk/engine/src/main/java/com/metamatrix/query/optimizer/relational/rules/FrameUtil.java	2009-04-12 21:32:20 UTC (rev 761)
+++ trunk/engine/src/main/java/com/metamatrix/query/optimizer/relational/rules/FrameUtil.java	2009-04-13 01:32:50 UTC (rev 762)
@@ -110,7 +110,7 @@
         
     }
     
-    static boolean canConvertAccessPatterns(PlanNode sourceNode) throws QueryPlannerException {
+    static boolean canConvertAccessPatterns(PlanNode sourceNode) {
         List accessPatterns = (List)sourceNode.getProperty(NodeConstants.Info.ACCESS_PATTERNS);
         if (accessPatterns == null) {
             return true;
@@ -168,8 +168,21 @@
         throws QueryPlannerException {
 
         // Update groups for current node   
-        Set groups = node.getGroups();  
+        Set<GroupSymbol> groups = node.getGroups();  
+        
+        boolean hasOld = groups.remove(oldGroup);
 
+        int type = node.getType();
+
+        if(newGroup != null) {
+            if (!hasOld) {
+                return;
+            }
+            groups.add(newGroup);
+        } else if (type != NodeConstants.Types.SOURCE && type != NodeConstants.Types.ACCESS) {
+        	groups.clear();
+        }
+        
         // Convert expressions from correlated subquery references;
         // currently only for SELECT or PROJECT nodes
         List refs = (List)node.getProperty(NodeConstants.Info.CORRELATED_REFERENCES);
@@ -180,84 +193,64 @@
                 Expression expr = ref.getExpression();
                 Expression convertedExpr = convertExpression(expr, symbolMap);
                 ref.setExpression(convertedExpr);
+                if (newGroup == null) {
+                    GroupsUsedByElementsVisitor.getGroups(convertedExpr, groups);
+                }
             }
         }
-        
-        boolean hasOld = groups.remove(oldGroup);
-        
-        if(newGroup != null) {
-            if (!hasOld) {
-                return;
-            }
-            groups.add(newGroup);
-        }   
 
-        // Updated elements
-        List newElementSymbols = null;
-        if (newGroup == null) {
-            newElementSymbols = new ArrayList();
-        }
-
-        int type = node.getType();
         if(type == NodeConstants.Types.SELECT) { 
             Criteria crit = (Criteria) node.getProperty(NodeConstants.Info.SELECT_CRITERIA);
-            convertCriteria(crit, symbolMap);
+            crit = convertCriteria(crit, symbolMap);
+            node.setProperty(NodeConstants.Info.SELECT_CRITERIA, crit);
             
             if (newGroup == null) {
-                ElementCollectorVisitor.getElements(crit, newElementSymbols);
+                GroupsUsedByElementsVisitor.getGroups(crit, groups);
             }
                             
         } else if(type == NodeConstants.Types.PROJECT) {                    
-            List elements = (List) node.getProperty(NodeConstants.Info.PROJECT_COLS);           
-            List newElements = new ArrayList(elements.size());
+            List<SingleElementSymbol> elements = (List<SingleElementSymbol>) node.getProperty(NodeConstants.Info.PROJECT_COLS);           
             
-            Iterator elementIter = elements.iterator();
-            while(elementIter.hasNext()) { 
-                SingleElementSymbol symbol = (SingleElementSymbol) elementIter.next();
+            for (int i = 0; i < elements.size(); i++) {
+                SingleElementSymbol symbol = elements.get(i);
                 SingleElementSymbol mappedSymbol = convertSingleElementSymbol(symbol, symbolMap, true);
-                newElements.add(mappedSymbol);
+                elements.set(i, mappedSymbol);
                       
                 if (newGroup == null) {
-                    ElementCollectorVisitor.getElements(mappedSymbol, newElementSymbols);
+                    GroupsUsedByElementsVisitor.getGroups(mappedSymbol, groups);
                 }
             }
             
-            node.setProperty(NodeConstants.Info.PROJECT_COLS, newElements);
-            
         } else if(type == NodeConstants.Types.JOIN) { 
             // Convert join criteria property
-            List joinCrits = (List) node.getProperty(NodeConstants.Info.JOIN_CRITERIA);
-            if(joinCrits != null && joinCrits.size() > 0) { 
-                Iterator critIter = joinCrits.iterator();
-                while(critIter.hasNext()) { 
-                    Criteria crit = (Criteria) critIter.next();
-                    convertCriteria(crit, symbolMap);
+            List<Criteria> joinCrits = (List<Criteria>) node.getProperty(NodeConstants.Info.JOIN_CRITERIA);
+            if(joinCrits != null) { 
+            	for (int i = 0; i < joinCrits.size(); i++) {
+                    Criteria crit = joinCrits.get(i);
+                    crit = convertCriteria(crit, symbolMap);
                     
                     if (newGroup == null) {
-                        ElementCollectorVisitor.getElements(crit, newElementSymbols);
+                        GroupsUsedByElementsVisitor.getGroups(crit, groups);
                     }
+                    joinCrits.set(i, crit);
                 }   
             }
             
             convertAccessPatterns(symbolMap, node);
         
         } else if(type == NodeConstants.Types.SORT) { 
-            List elements = (List) node.getProperty(NodeConstants.Info.SORT_ORDER);         
-            List newElements = new ArrayList(elements.size());
+            List<SingleElementSymbol> elements = (List<SingleElementSymbol>) node.getProperty(NodeConstants.Info.SORT_ORDER);         
             
-            Iterator elementIter = elements.iterator();
-            while(elementIter.hasNext()) { 
-                SingleElementSymbol symbol = (SingleElementSymbol) elementIter.next();
+            for (int i = 0; i < elements.size(); i++) {
+                SingleElementSymbol symbol = elements.get(i);
                 SingleElementSymbol mappedSymbol = convertSingleElementSymbol(symbol, symbolMap, true);
-                newElements.add( mappedSymbol );
-                
+                elements.set(i, mappedSymbol);
+                      
                 if (newGroup == null) {
-                    ElementCollectorVisitor.getElements(mappedSymbol, newElementSymbols);
+                    GroupsUsedByElementsVisitor.getGroups(mappedSymbol, groups);
                 }
             }
             
-            node.setProperty(NodeConstants.Info.SORT_ORDER, newElements);   
-            
         } else if(type == NodeConstants.Types.GROUP) {  
             // Grouping columns 
             List groupCols = (List) node.getProperty(NodeConstants.Info.GROUP_COLS);
@@ -270,7 +263,7 @@
                     newGroupCols.add( mappedCol );
                     
                     if (newGroup == null) {
-                        ElementCollectorVisitor.getElements(mappedCol, newElementSymbols);
+                        GroupsUsedByElementsVisitor.getGroups(mappedCol, groups);
                     }
                 }                        
                 node.setProperty(NodeConstants.Info.GROUP_COLS, newGroupCols);            
@@ -278,10 +271,6 @@
         } else if (type == NodeConstants.Types.SOURCE || type == NodeConstants.Types.ACCESS) {
             convertAccessPatterns(symbolMap, node);
         }
-
-        if (newGroup == null) {
-            GroupsUsedByElementsVisitor.getGroups(newElementSymbols, groups);
-        }
     }
     
     static SingleElementSymbol convertSingleElementSymbol(SingleElementSymbol symbol, Map symbolMap, boolean shouldAlias) {
@@ -419,7 +408,7 @@
      * @param groups
      * @return
      */
-    static PlanNode findOriginatingNode(PlanNode root, Set groups) {
+    static PlanNode findOriginatingNode(PlanNode root, Set<GroupSymbol> groups) {
         return findOriginatingNode(root, groups, false);
     }
     
@@ -438,7 +427,7 @@
         return findOriginatingNode(root, root.getGroups(), true);
     }
     
-    private static PlanNode findOriginatingNode(PlanNode root, Set groups, boolean joinSource) {
+    private static PlanNode findOriginatingNode(PlanNode root, Set<GroupSymbol> groups, boolean joinSource) {
         boolean containsGroups = false;
         
     	if(root.getType() == NodeConstants.Types.NULL || root.getType() == NodeConstants.Types.SOURCE 

Modified: trunk/engine/src/main/java/com/metamatrix/query/optimizer/relational/rules/JoinUtil.java
===================================================================
--- trunk/engine/src/main/java/com/metamatrix/query/optimizer/relational/rules/JoinUtil.java	2009-04-12 21:32:20 UTC (rev 761)
+++ trunk/engine/src/main/java/com/metamatrix/query/optimizer/relational/rules/JoinUtil.java	2009-04-13 01:32:50 UTC (rev 762)
@@ -87,8 +87,8 @@
         PlanNode right = joinNode.getLastChild();
         right = FrameUtil.findJoinSourceNode(right);
         
-        Collection outerGroups = left.getGroups();
-        Collection innerGroups = right.getGroups();
+        Collection<GroupSymbol> outerGroups = left.getGroups();
+        Collection<GroupSymbol> innerGroups = right.getGroups();
         if (joinType == JoinType.JOIN_RIGHT_OUTER) {
             outerGroups = innerGroups;
             innerGroups = left.getGroups(); 
@@ -135,7 +135,7 @@
      *  given all null values for elements in the inner groups
      */
     public static boolean isNullDependent(QueryMetadataInterface metadata,
-                                            final Collection innerGroups,
+                                            final Collection<GroupSymbol> innerGroups,
                                             Criteria crit) {
         Criteria simplifiedCrit = (Criteria)replaceWithNullValues(innerGroups, crit);
         try {
@@ -148,7 +148,7 @@
     }
     
     public static boolean isNullDependent(QueryMetadataInterface metadata,
-                                          final Collection innerGroups,
+                                          final Collection<GroupSymbol> innerGroups,
                                           Expression expr) {
         Expression simplifiedExpression = (Expression)replaceWithNullValues(innerGroups, expr);
         try {
@@ -160,7 +160,7 @@
         return !QueryRewriter.isNull(simplifiedExpression);
     }
 
-    private static LanguageObject replaceWithNullValues(final Collection innerGroups,
+    private static LanguageObject replaceWithNullValues(final Collection<GroupSymbol> innerGroups,
                                                         LanguageObject obj) {
         ExpressionMappingVisitor emv = new ExpressionMappingVisitor(null) {
             
@@ -188,7 +188,7 @@
     }
 
     static JoinType getJoinTypePreventingCriteriaOptimization(PlanNode joinNode, PlanNode critNode) {
-        Set groups = critNode.getGroups();
+        Set<GroupSymbol> groups = critNode.getGroups();
         
         //special case for 0 group criteria
         if (groups.size() == 0) {

Modified: trunk/engine/src/main/java/com/metamatrix/query/optimizer/relational/rules/RulePushSelectCriteria.java
===================================================================
--- trunk/engine/src/main/java/com/metamatrix/query/optimizer/relational/rules/RulePushSelectCriteria.java	2009-04-12 21:32:20 UTC (rev 761)
+++ trunk/engine/src/main/java/com/metamatrix/query/optimizer/relational/rules/RulePushSelectCriteria.java	2009-04-13 01:32:50 UTC (rev 762)
@@ -26,6 +26,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.IdentityHashMap;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
@@ -59,10 +60,10 @@
 import com.metamatrix.query.sql.symbol.GroupSymbol;
 import com.metamatrix.query.sql.symbol.Reference;
 import com.metamatrix.query.sql.util.SymbolMap;
+import com.metamatrix.query.sql.util.ValueIteratorProvider;
 import com.metamatrix.query.sql.visitor.AggregateSymbolCollectorVisitor;
 import com.metamatrix.query.sql.visitor.ElementCollectorVisitor;
 import com.metamatrix.query.sql.visitor.GroupCollectorVisitor;
-import com.metamatrix.query.sql.visitor.GroupsUsedByElementsVisitor;
 import com.metamatrix.query.sql.visitor.ValueIteratorProviderCollectorVisitor;
 import com.metamatrix.query.util.CommandContext;
 import com.metamatrix.query.util.ErrorMessageKeys;
@@ -92,23 +93,20 @@
 		    movedAnyNode = false;
 
     		// Find criteria nodes that could be pushed
-    		List critNodes = new ArrayList();
-    		findCriteria(plan, critNodes, deadNodes, metadata, capFinder);
-
-    		// For each crit node, try to move toward the originating node
-    		Iterator nodeIter = critNodes.iterator();
-    		while(nodeIter.hasNext()) {
-    			PlanNode critNode = (PlanNode) nodeIter.next();
-   				pushTowardOriginatingNode(critNode, metadata, capFinder);
-			}
-
-            // For each crit node, try to move across the originating node
-			nodeIter = critNodes.iterator();
-    		while(nodeIter.hasNext()) {
-    			PlanNode critNode = (PlanNode) nodeIter.next();
+		    List<PlanNode> critNodes = NodeEditor.findAllNodes(plan, NodeConstants.Types.SELECT);
+		    Collections.reverse(critNodes);
+		    for (PlanNode critNode : critNodes) {
+    			boolean isPhantom = critNode.hasBooleanProperty(NodeConstants.Info.IS_PHANTOM);
+	            boolean isCopied = critNode.hasBooleanProperty(NodeConstants.Info.IS_COPIED);
+	            boolean isPushed = critNode.hasBooleanProperty(NodeConstants.Info.IS_PUSHED);
+	            if (isPhantom || isCopied || isPushed || deadNodes.contains(critNode)) {
+	            	continue;
+	            }
+	            pushTowardOriginatingNode(critNode, metadata, capFinder);
+	            
                 boolean moved = false;
                 
-                if(FrameUtil.hasSubquery(critNode) || critNode.getGroups().size() == 0) {
+                if(critNode.getGroups().isEmpty()) {
                     deadNodes.add(critNode);
                     continue;
                 }
@@ -152,6 +150,10 @@
      * @return
      */
     private boolean handleJoinCriteria(PlanNode joinNode, PlanNode critNode, QueryMetadataInterface metadata) {
+    	//we currently don't allow subqueries in join criteria
+    	if (FrameUtil.hasSubquery(critNode)) {
+    		return false;
+    	}
         JoinType jt = (JoinType)joinNode.getProperty(NodeConstants.Info.JOIN_TYPE);
         
         if (jt == JoinType.JOIN_CROSS || jt == JoinType.JOIN_INNER) {
@@ -194,26 +196,6 @@
         NodeEditor.removeChildNode(critNode.getParent(), critNode);
     }
 
-	 void findCriteria(PlanNode root, List foundNodes, Set deadNodes, QueryMetadataInterface metadata, CapabilitiesFinder capFinder)
-		throws QueryPlannerException, MetaMatrixComponentException {
-         
-		if( ! deadNodes.contains(root) &&
-		   	root.getType() == NodeConstants.Types.SELECT) {
-
-			boolean isPhantom = root.hasBooleanProperty(NodeConstants.Info.IS_PHANTOM);
-            boolean isCopied = root.hasBooleanProperty(NodeConstants.Info.IS_COPIED);
-            boolean isPushed = root.hasBooleanProperty(NodeConstants.Info.IS_PUSHED);
-            
-			if( !(isPhantom || isCopied || isPushed) ) {
-                foundNodes.add(0, root);                  
-			}
-		}
-
-		for (PlanNode child : root.getChildren()) {
-			findCriteria(child, foundNodes, deadNodes, metadata, capFinder);
-		}
-	}
-
     /**
      *  
      * @param critNode
@@ -229,15 +211,8 @@
         PlanNode sourceNode = null;
         
         Set<GroupSymbol> groups = critNode.getGroups();
-        
-        Set<GroupSymbol> correlatedReferenceGroups = getCorrelatedReferenceGroups(critNode);
-        
-        //check the correlation groups
-        if (!correlatedReferenceGroups.isEmpty()) {
-            groups = new HashSet<GroupSymbol>(groups);
-            groups.addAll(correlatedReferenceGroups);
-        }
-            
+                    
+        //check for an uncorrelated subquery
         if(groups.isEmpty() && FrameUtil.hasSubquery(critNode)) {
             Object modelID = getUniqueModel(critNode, metadata);            
             if(modelID != null) {
@@ -272,25 +247,6 @@
         NodeEditor.removeChildNode(critNode.getParent(), critNode);
         destination.addAsParent(critNode);
 	}
-    
-    static Set<GroupSymbol> getCorrelatedReferenceGroups(PlanNode critNode) {
-        List refs = (List) critNode.getProperty(NodeConstants.Info.CORRELATED_REFERENCES);
-        
-        // Check whether there are correlated subquery references
-        if(refs == null || refs.size() == 0) {
-            return Collections.emptySet();    
-        }
-        
-        // Get group used in correlated reference
-        HashSet<GroupSymbol> refGroups = new HashSet<GroupSymbol>(refs.size());
-        Iterator refIter = refs.iterator();
-        while(refIter.hasNext()) {
-            Reference ref = (Reference) refIter.next();
-            Expression expr = ref.getExpression();
-            GroupsUsedByElementsVisitor.getGroups(expr, refGroups);
-        }
-        return refGroups;
-    }
 
     private Object getUniqueModel(PlanNode critNode, QueryMetadataInterface metadata) 
     throws QueryMetadataException, MetaMatrixComponentException {
@@ -348,7 +304,7 @@
 		throws QueryPlannerException, MetaMatrixComponentException {
         
 		// Walk from source node up to critNode to build list of intervening nodes
-		Stack path = new Stack();
+		Stack<PlanNode> path = new Stack<PlanNode>();
 		PlanNode currentNode = sourceNode.getParent();
 		while(currentNode != critNode) {
 			path.push(currentNode);
@@ -357,7 +313,7 @@
 
 		// Examine path in reverse order (by popping stack)
 		while(! path.empty()) {
-			currentNode = (PlanNode) path.pop();
+			currentNode = path.pop();
             
 			// Look for situations where we don't allow SELECT to be pushed
 			if(currentNode.getType() == NodeConstants.Types.ACCESS) {
@@ -421,7 +377,9 @@
             PlanNode child = sourceNode.getFirstChild();
             child = FrameUtil.findOriginatingNode(child, child.getGroups());
             if (child != null && child.getType() == NodeConstants.Types.SET_OP) {
-                if (child == sourceNode.getFirstChild()) {
+            	//only allow criteria without subqueires - node cloning doesn't allow for the proper creation of 
+            	//multiple nodes with the same subqueries
+                if (child == sourceNode.getFirstChild() && !FrameUtil.hasSubquery(critNode)) {
                     return pushAcrossSetOp(critNode, child);
                 } 
                 //this could be an access node in the middle of the source and set op,
@@ -468,7 +426,7 @@
         
         SymbolMap symbolMap = (SymbolMap) sourceNode.getProperty(NodeConstants.Info.SYMBOL_MAP);
         
-        if (!createConvertedSelectNode(critNode, projectNode, symbolMap)) {
+        if (!createConvertedSelectNode(critNode, sourceNode.getGroups().iterator().next(), projectNode, symbolMap)) {
             return false;
         }
 		
@@ -552,22 +510,20 @@
 		copyNode.setProperty(NodeConstants.Info.SELECT_CRITERIA, copyCrit);
 		copyNode.addGroups(critNode.getGroups());
         // Copy subquery properties
-        Object subqueryPlans = critNode.getProperty(NodeConstants.Info.SUBQUERY_PLANS);     
+        List<RelationalPlan> subqueryPlans = (List<RelationalPlan>)critNode.getProperty(NodeConstants.Info.SUBQUERY_PLANS);     
         if(subqueryPlans != null) {
-            copyNode.setProperty(NodeConstants.Info.SUBQUERY_PLANS, subqueryPlans);
+            copyNode.setProperty(NodeConstants.Info.SUBQUERY_PLANS, new ArrayList<RelationalPlan>(subqueryPlans));
         }
-        Object subqueryValueProviders = critNode.getProperty(NodeConstants.Info.SUBQUERY_VALUE_PROVIDERS);      
+        List<ValueIteratorProvider> subqueryValueProviders = (List<ValueIteratorProvider>)critNode.getProperty(NodeConstants.Info.SUBQUERY_VALUE_PROVIDERS);      
         if(subqueryValueProviders != null) {
-            copyNode.setProperty(NodeConstants.Info.SUBQUERY_VALUE_PROVIDERS, subqueryValueProviders);
+            copyNode.setProperty(NodeConstants.Info.SUBQUERY_VALUE_PROVIDERS, new ArrayList<ValueIteratorProvider>(subqueryValueProviders));
         }
-        Object correlatedReferences = critNode.getProperty(NodeConstants.Info.CORRELATED_REFERENCES);       
+        List<Reference> correlatedReferences = (List<Reference>)critNode.getProperty(NodeConstants.Info.CORRELATED_REFERENCES);       
         if(correlatedReferences  != null) {
-            copyNode.setProperty(NodeConstants.Info.CORRELATED_REFERENCES, correlatedReferences);
+            copyNode.setProperty(NodeConstants.Info.CORRELATED_REFERENCES, new ArrayList<Reference>(correlatedReferences));
         }
-        
-        Object depCrits = critNode.getProperty(NodeConstants.Info.IS_DEPENDENT_SET);
-        if(depCrits != null) {
-            copyNode.setProperty(NodeConstants.Info.IS_DEPENDENT_SET, depCrits);
+        if(critNode.hasBooleanProperty(NodeConstants.Info.IS_DEPENDENT_SET)) {
+            copyNode.setProperty(NodeConstants.Info.IS_DEPENDENT_SET, Boolean.TRUE);
         }
 	    return copyNode;
 	}
@@ -577,7 +533,7 @@
         
         // Find source node above union and grab the symbol map
         PlanNode sourceNode = NodeEditor.findParent(setOp, NodeConstants.Types.SOURCE);
-        
+        GroupSymbol virtualGroup = sourceNode.getGroups().iterator().next();
         satisfyAccessPatterns(critNode, sourceNode);
         
         SymbolMap symbolMap = (SymbolMap) sourceNode.getProperty(NodeConstants.Info.SYMBOL_MAP);
@@ -593,7 +549,7 @@
         
         PlanNode firstBranchNode = NodeEditor.findNodePreOrder(firstChild, NodeConstants.Types.PROJECT);
         
-        if(createConvertedSelectNode(critNode, firstBranchNode, symbolMap)) {
+        if(createConvertedSelectNode(critNode, virtualGroup, firstBranchNode, symbolMap)) {
             movedCount++;
         }
 
@@ -611,7 +567,7 @@
             symbolMap = SymbolMap.createSymbolMap(sourceGroup, firstProjectCols, (List) projectNode.getProperty(NodeConstants.Info.PROJECT_COLS));
 		    
 			// Move the node
-			if(createConvertedSelectNode(critNode, projectNode, symbolMap)) {
+			if(createConvertedSelectNode(critNode, virtualGroup, projectNode, symbolMap)) {
                 movedCount++;
             }
 		}
@@ -626,7 +582,7 @@
         return false;
 	}
 
-	void collectUnionChildren(PlanNode unionNode, LinkedList unionChildren) {
+	void collectUnionChildren(PlanNode unionNode, LinkedList<PlanNode> unionChildren) {
 	    for (PlanNode child : unionNode.getChildren()) {
 	        if(child.getType() == NodeConstants.Types.SET_OP) {
 	            collectUnionChildren(child, unionChildren);
@@ -637,18 +593,43 @@
 	}
 
     private boolean createConvertedSelectNode(PlanNode critNode,
+    							   GroupSymbol sourceGroup,
                                    PlanNode projectNode,
                                    SymbolMap symbolMap) throws QueryPlannerException {
         // If projectNode has children, then it is from a SELECT without a FROM and the criteria should not be pushed
         if(projectNode.getChildCount() == 0) {
             return false;
         }
-        
+
         Criteria crit = (Criteria) critNode.getProperty(NodeConstants.Info.SELECT_CRITERIA);
+
+        Boolean conversionResult = checkConversion(symbolMap, ElementCollectorVisitor.getElements(crit, true));
         
-        boolean hasAggregate = false;
+        if (conversionResult == Boolean.FALSE) {
+        	return false; //not convertable
+        }
         
-        for (ElementSymbol element : ElementCollectorVisitor.getElements(crit, true)) {
+        if (FrameUtil.hasSubquery(critNode) 
+        		&& checkConversion(symbolMap, critNode.getCorrelatedReferenceElements()) != null) {
+    		return false; //not convertable, or has an aggregate for a correlated reference
+        }
+        
+        PlanNode copyNode = copyNode(critNode);
+
+        if (conversionResult == Boolean.TRUE) {
+            copyNode.setProperty(NodeConstants.Info.IS_HAVING, Boolean.TRUE);
+        }
+
+        FrameUtil.convertNode(copyNode, sourceGroup, null, symbolMap.asMap());        
+		projectNode.getFirstChild().addAsParent(copyNode);
+		return true;
+    }
+
+	private Boolean checkConversion(SymbolMap symbolMap,
+			Collection<ElementSymbol> elements) {
+		Boolean result = null;
+        
+        for (ElementSymbol element : elements) {
             Expression converted = symbolMap.getMappedExpression(element);
 
             if(converted == null) {
@@ -661,29 +642,11 @@
             }
             
             if (!AggregateSymbolCollectorVisitor.getAggregates(converted, false).isEmpty()) {
-                hasAggregate = true;
+                result = Boolean.TRUE;
             }
         }
-        
-        PlanNode copyNode = copyNode(critNode);
-
-        if (hasAggregate) {
-            copyNode.setProperty(NodeConstants.Info.IS_HAVING, Boolean.TRUE);
-        }
-
-        // Do symbol mapping to switch to new frames symbols
-        crit = FrameUtil.convertCriteria((Criteria)crit.clone(), symbolMap.asMap());
-        copyNode.setProperty(NodeConstants.Info.SELECT_CRITERIA, crit);
-
-        // Reset groups
-        copyNode.getGroups().clear();
-        copyNode.addGroups(GroupsUsedByElementsVisitor.getGroups(crit));
-                
-		// Insert new node
-		projectNode.getFirstChild().addAsParent(copyNode);
-		
-		return true;
-    }
+		return result;
+	}
     
 	public String toString() {
 		return "PushSelectCriteria"; //$NON-NLS-1$

Modified: trunk/engine/src/main/java/com/metamatrix/query/optimizer/relational/rules/RuleRaiseAccess.java
===================================================================
--- trunk/engine/src/main/java/com/metamatrix/query/optimizer/relational/rules/RuleRaiseAccess.java	2009-04-12 21:32:20 UTC (rev 761)
+++ trunk/engine/src/main/java/com/metamatrix/query/optimizer/relational/rules/RuleRaiseAccess.java	2009-04-13 01:32:50 UTC (rev 762)
@@ -354,19 +354,10 @@
             if(!(plan instanceof RelationalPlan)) {
                 return false;
             }
-            // We are expecting the following for an eligible subquery:
-            // 1. Plan should be Access, nothing else
-            // 2. Access should be returning a single column
-            // 3. should not be returning a constant or scalar function, 
-            //    only an element or aggregate  
-            // 4. Access node command should be a Query
-            // 5. Access node should be for the same model as critNode
-            // 6. If subquery has correlated references, model supports correlated
             
-            // Check that root node is a project
             RelationalPlan rplan = (RelationalPlan) plan;
             
-            // Check that the second node is an access node and that it has no children                
+            // Check that the plan is just an access node                
             RelationalNode accessNode = rplan.getRootNode();
             if(accessNode == null || ! (accessNode instanceof AccessNode) || accessNode.getChildren()[0] != null) {
                 return false;

Modified: trunk/engine/src/main/java/com/metamatrix/query/optimizer/relational/rules/RuleRemoveOptionalJoins.java
===================================================================
--- trunk/engine/src/main/java/com/metamatrix/query/optimizer/relational/rules/RuleRemoveOptionalJoins.java	2009-04-12 21:32:20 UTC (rev 761)
+++ trunk/engine/src/main/java/com/metamatrix/query/optimizer/relational/rules/RuleRemoveOptionalJoins.java	2009-04-13 01:32:50 UTC (rev 762)
@@ -131,7 +131,7 @@
                 if (isRoot) {
                     List columns = (List)node.getProperty(NodeConstants.Info.PROJECT_COLS);
                     ElementCollectorVisitor.getElements(columns, elements);
-                    collectCorrelatedReferences(node, elements);
+                    elements.addAll(node.getCorrelatedReferenceElements());
                 }
                 
                 break;
@@ -158,7 +158,7 @@
                 if (elements != null) {
                     Criteria crit = (Criteria)node.getProperty(NodeConstants.Info.SELECT_CRITERIA);
                     ElementCollectorVisitor.getElements(crit, elements);
-                    collectCorrelatedReferences(node, elements);
+                    elements.addAll(node.getCorrelatedReferenceElements());
                 }
                 break;
             }
@@ -199,19 +199,6 @@
         return false;
     }
 
-	private void collectCorrelatedReferences(PlanNode node,
-			Set<ElementSymbol> elements) {
-		List refs = (List)node.getProperty(NodeConstants.Info.CORRELATED_REFERENCES);
-		if (refs != null){
-		    Iterator refIter = refs.iterator();
-		    while (refIter.hasNext()) {
-		        Reference ref = (Reference)refIter.next();
-		        Expression expr = ref.getExpression();
-		        ElementCollectorVisitor.getElements(expr, elements);
-		    }
-		}
-	}
-
     /**
      * remove the optional node if possible
      */ 

Modified: trunk/engine/src/main/java/com/metamatrix/query/sql/visitor/ElementCollectorVisitor.java
===================================================================
--- trunk/engine/src/main/java/com/metamatrix/query/sql/visitor/ElementCollectorVisitor.java	2009-04-12 21:32:20 UTC (rev 761)
+++ trunk/engine/src/main/java/com/metamatrix/query/sql/visitor/ElementCollectorVisitor.java	2009-04-13 01:32:50 UTC (rev 762)
@@ -49,7 +49,7 @@
  */
 public class ElementCollectorVisitor extends LanguageVisitor {
 
-    private Collection elements;
+    private Collection<ElementSymbol> elements;
 
     /**
      * Construct a new visitor with the specified collection, which should
@@ -57,7 +57,7 @@
      * @param elements Collection to use for elements
      * @throws IllegalArgumentException If elements is null
      */
-	public ElementCollectorVisitor(Collection elements) {
+	public ElementCollectorVisitor(Collection<ElementSymbol> elements) {
         if(elements == null) {
             throw new IllegalArgumentException(QueryPlugin.Util.getString(ErrorMessageKeys.SQL_0021));
         }
@@ -69,7 +69,7 @@
      * after the visitor has been run on the language object tree.
      * @return Collection of {@link com.metamatrix.query.sql.symbol.ElementSymbol}
      */
-    public Collection getElements() {
+    public Collection<ElementSymbol> getElements() {
         return this.elements;
     }
 
@@ -109,7 +109,7 @@
      * @param obj Language object
      * @param elements Collection to collect elements in
      */
-    public static final void getElements(LanguageObject obj, Collection elements) {
+    public static final void getElements(LanguageObject obj, Collection<ElementSymbol> elements) {
     	if(obj == null) {
     		return;
     	}
@@ -117,7 +117,7 @@
         PreOrderNavigator.doVisit(obj, visitor);
     }
     
-    public static final void getElements(Collection<LanguageObject> objs, Collection elements) {
+    public static final void getElements(Collection<LanguageObject> objs, Collection<ElementSymbol> elements) {
     	if(objs == null) {
     		return;
     	}
@@ -149,15 +149,15 @@
      * subqueries of the query 
      * @return Collection of {@link com.metamatrix.query.sql.symbol.ElementSymbol}
      */
-    public static final Collection getElements(LanguageObject obj, boolean removeDuplicates, boolean useDeepIteration) {
+    public static final Collection<ElementSymbol> getElements(LanguageObject obj, boolean removeDuplicates, boolean useDeepIteration) {
         if(obj == null) {
-            return Collections.EMPTY_LIST;
+            return Collections.emptyList();
         }
-        Collection elements = null;
+        Collection<ElementSymbol> elements = null;
         if(removeDuplicates) {
-            elements = new HashSet();
+            elements = new HashSet<ElementSymbol>();
         } else {
-            elements = new ArrayList();
+            elements = new ArrayList<ElementSymbol>();
         }
         ElementCollectorVisitor visitor = null;
         if (useDeepIteration){

Modified: trunk/engine/src/test/java/com/metamatrix/query/optimizer/TestOptimizer.java
===================================================================
--- trunk/engine/src/test/java/com/metamatrix/query/optimizer/TestOptimizer.java	2009-04-12 21:32:20 UTC (rev 761)
+++ trunk/engine/src/test/java/com/metamatrix/query/optimizer/TestOptimizer.java	2009-04-13 01:32:50 UTC (rev 762)
@@ -1439,136 +1439,6 @@
             0       // UnionAll
         }); 
     } 
-
-    public void testCorrelatedSubquery1() {
-        ProcessorPlan plan = helpPlan("Select e1 from pm1.g1 where e1 in (select e1 FROM pm2.g1 WHERE pm1.g1.e2 = pm2.g1.e2)", example1(),  //$NON-NLS-1$
-            new String[] { "SELECT e1, pm1.g1.e2 FROM pm1.g1" }); //$NON-NLS-1$
-        checkNodeTypes(plan, new int[] {
-            1,      // Access
-            0,      // DependentAccess
-            1,      // DependentSelect
-            0,      // DependentProject
-            0,      // DupRemove
-            0,      // Grouping
-            0,      // NestedLoopJoinStrategy
-            0,      // MergeJoinStrategy
-            0,      // Null
-            0,      // PlanExecution
-            1,      // Project
-            0,      // Select
-            0,      // Sort
-            0       // UnionAll
-        }); 
-        
-        checkSubPlanCount(plan, 1);        
-    }
-
-    public void testCorrelatedSubquery2() {
-        ProcessorPlan plan = helpPlan("Select e1, (select e1 FROM pm2.g1 WHERE pm1.g1.e2 = pm2.g1.e2) from pm1.g1", example1(),  //$NON-NLS-1$
-            new String[] { "SELECT e1, pm1.g1.e2 FROM pm1.g1" }); //$NON-NLS-1$
-        checkNodeTypes(plan, new int[] {
-            1,      // Access
-            0,      // DependentAccess
-            0,      // DependentSelect
-            1,      // DependentProject
-            0,      // DupRemove
-            0,      // Grouping
-            0,      // NestedLoopJoinStrategy
-            0,      // MergeJoinStrategy
-            0,      // Null
-            0,      // PlanExecution
-            0,      // Project
-            0,      // Select
-            0,      // Sort
-            0       // UnionAll
-        }); 
-    }
-
-    public void testCorrelatedSubqueryVirtualLayer1() {
-        ProcessorPlan plan = helpPlan("Select e1 from vm1.g6 where e1 in (select e1 FROM pm2.g1 WHERE vm1.g6.e3 = pm2.g1.e2)", example1(),  //$NON-NLS-1$
-            new String[] { "SELECT e1 FROM pm1.g1" }); //$NON-NLS-1$
-        checkNodeTypes(plan, new int[] {
-            1,      // Access
-            0,      // DependentAccess
-            1,      // DependentSelect
-            0,      // DependentProject
-            0,      // DupRemove
-            0,      // Grouping
-            0,      // NestedLoopJoinStrategy
-            0,      // MergeJoinStrategy
-            0,      // Null
-            0,      // PlanExecution
-            1,      // Project
-            0,      // Select
-            0,      // Sort
-            0       // UnionAll
-        }); 
-    }
-
-    public void testCorrelatedSubqueryVirtualLayer2() {
-        ProcessorPlan plan = helpPlan("Select e1 from vm1.g6 where e1 in (select e1 FROM pm2.g1 WHERE vm1.g6.e4 = pm2.g1.e4)", example1(),  //$NON-NLS-1$
-            new String[] { "SELECT e1, e2, e4 FROM pm1.g1" }); //$NON-NLS-1$
-        checkNodeTypes(plan, new int[] {
-            1,      // Access
-            0,      // DependentAccess
-            1,      // DependentSelect
-            0,      // DependentProject
-            0,      // DupRemove
-            0,      // Grouping
-            0,      // NestedLoopJoinStrategy
-            0,      // MergeJoinStrategy
-            0,      // Null
-            0,      // PlanExecution
-            1,      // Project
-            0,      // Select
-            0,      // Sort
-            0       // UnionAll
-        }); 
-    }
-
-    public void testCorrelatedSubqueryVirtualLayer3() {
-        ProcessorPlan plan = helpPlan("Select e1, (select e1 FROM pm2.g1 WHERE vm1.g6.e4 = pm2.g1.e4) from vm1.g6", example1(),  //$NON-NLS-1$
-            new String[] { "SELECT e1, e2, e4 FROM pm1.g1" }); //$NON-NLS-1$
-        checkNodeTypes(plan, new int[] {
-            1,      // Access
-            0,      // DependentAccess
-            0,      // DependentSelect
-            1,      // DependentProject
-            0,      // DupRemove
-            0,      // Grouping
-            0,      // NestedLoopJoinStrategy
-            0,      // MergeJoinStrategy
-            0,      // Null
-            0,      // PlanExecution
-            0,      // Project
-            0,      // Select
-            0,      // Sort
-            0       // UnionAll
-        }); 
-        checkSubPlanCount(plan, 1);        
-    }
-
-    public void testCorrelatedSubqueryInTransformation2() {
-        String sql = "Select * from vm1.g20"; //$NON-NLS-1$
-        ProcessorPlan plan = helpPlan(sql, FakeMetadataFactory.example1Cached(), 
-            new String[] { "SELECT pm1.g1.e1, pm1.g1.e2, pm1.g1.e3, pm1.g1.e4 FROM pm1.g1" }); //$NON-NLS-1$
-        checkNodeTypes(plan, new int[] {
-            1,      // Access
-            0,      // DependentAccess
-            1,      // DependentSelect
-            0,      // DependentProject
-            0,      // DupRemove
-            0,      // Grouping
-            0,      // NestedLoopJoinStrategy
-            0,      // MergeJoinStrategy
-            0,      // Null
-            0,      // PlanExecution
-            1,      // Project
-            0,      // Select
-            0,      // Sort
-            0       // UnionAll
-        });         
-    }
     
     public void testTempGroup() {
         ProcessorPlan plan = helpPlan("select e1 from tm1.g1 where e1 = 'x'", FakeMetadataFactory.example1Cached(),  //$NON-NLS-1$
@@ -3163,455 +3033,6 @@
         checkNodeTypes(plan, FULL_PUSHDOWN); 
     }   
 
-    /**
-     * Check that subquery is not pushed if the subquery cannot all be pushed to the source.
-     */
-    public void testNoPushSubqueryInWhereClause1() {
-        FakeCapabilitiesFinder capFinder = new FakeCapabilitiesFinder();
-        BasicSourceCapabilities caps = getTypicalCapabilities();
-        caps.setCapabilitySupport(Capability.QUERY_WHERE_IN_SUBQUERY, true);
-        caps.setCapabilitySupport(Capability.QUERY_AGGREGATES, false);
-        capFinder.addCapabilities("pm1", caps); //$NON-NLS-1$
-
-        ProcessorPlan plan = helpPlan("Select e1 from pm1.g1 where e1 in (select max(e1) FROM pm1.g2)", example1(),  //$NON-NLS-1$
-            null, capFinder,
-            new String[] { "SELECT e1 FROM pm1.g1" }, SHOULD_SUCCEED); //$NON-NLS-1$ 
-        checkNodeTypes(plan, new int[] {
-            1,      // Access
-            0,      // DependentAccess
-            1,      // DependentSelect
-            0,      // DependentProject
-            0,      // DupRemove
-            0,      // Grouping
-            0,      // NestedLoopJoinStrategy
-            0,      // MergeJoinStrategy
-            0,      // Null
-            0,      // PlanExecution
-            1,      // Project
-            0,      // Select
-            0,      // Sort
-            0       // UnionAll
-        }); 
-    }
-
-    /**
-     * Check that subquery is not pushed if the subquery is from a different model
-     * than the outer query.
-     */
-    public void testNoPushSubqueryInWhereClause2() {
-        FakeCapabilitiesFinder capFinder = new FakeCapabilitiesFinder();
-        BasicSourceCapabilities caps = getTypicalCapabilities();
-        caps.setCapabilitySupport(Capability.QUERY_WHERE_IN_SUBQUERY, true);
-        capFinder.addCapabilities("pm1", caps); //$NON-NLS-1$
-        capFinder.addCapabilities("pm2", getTypicalCapabilities()); //$NON-NLS-1$
-
-        ProcessorPlan plan = helpPlan("Select e1 from pm1.g1 where e1 in (select e1 FROM pm2.g1)", example1(),  //$NON-NLS-1$
-            null, capFinder,
-            new String[] { "SELECT e1 FROM pm1.g1" }, SHOULD_SUCCEED); //$NON-NLS-1$ 
-        checkNodeTypes(plan, new int[] {
-            1,      // Access
-            0,      // DependentAccess
-            1,      // DependentSelect
-            0,      // DependentProject
-            0,      // DupRemove
-            0,      // Grouping
-            0,      // NestedLoopJoinStrategy
-            0,      // MergeJoinStrategy
-            0,      // Null
-            0,      // PlanExecution
-            1,      // Project
-            0,      // Select
-            0,      // Sort
-            0       // UnionAll
-        }); 
-    }
-    
-    /**
-     * Do not support XML query as subquery
-     * Check that subquery is not pushed if the subquery is not relational.
-     */
-    public void defer_testNoPushSubqueryInWhereClause3() {
-        FakeCapabilitiesFinder capFinder = new FakeCapabilitiesFinder();
-        BasicSourceCapabilities caps = new BasicSourceCapabilities();
-        caps.setCapabilitySupport(Capability.QUERY_WHERE, true);
-        caps.setCapabilitySupport(Capability.QUERY_WHERE_IN, true);
-        caps.setCapabilitySupport(Capability.QUERY_WHERE_IN_SUBQUERY, true);
-        capFinder.addCapabilities("pm1", caps); //$NON-NLS-1$
-        capFinder.addCapabilities("pm2", new BasicSourceCapabilities()); //$NON-NLS-1$
-
-        ProcessorPlan plan = helpPlan("Select e1 from pm1.g1 where e1 in (select * from xmltest.doc1)", FakeMetadataFactory.example1Cached(),  //$NON-NLS-1$
-            null, capFinder,
-            new String[] { "SELECT e1 FROM pm1.g1" }, SHOULD_SUCCEED); //$NON-NLS-1$ 
-        checkNodeTypes(plan, new int[] {
-            1,      // Access
-            0,      // DependentAccess
-            1,      // DependentSelect
-            0,      // DependentProject
-            0,      // DupRemove
-            0,      // Grouping
-            0,      // NestedLoopJoinStrategy
-            0,      // MergeJoinStrategy
-            0,      // Null
-            0,      // PlanExecution
-            1,      // Project
-            0,      // Select
-            0,      // Sort
-            0       // UnionAll
-        }); 
-    }
-
-    /**
-     * Check that subquery is not pushed if the subquery has a function that can't be pushed 
-     * in the SELECT clause
-     */
-    public void testNoPushSubqueryInWhereClause4() {
-        FakeCapabilitiesFinder capFinder = new FakeCapabilitiesFinder();
-        BasicSourceCapabilities caps = new BasicSourceCapabilities();
-        caps.setCapabilitySupport(Capability.QUERY_WHERE, true);
-        caps.setCapabilitySupport(Capability.QUERY_WHERE_IN, true);
-        caps.setCapabilitySupport(Capability.QUERY_WHERE_IN_SUBQUERY, true);
-        capFinder.addCapabilities("pm1", caps); //$NON-NLS-1$
-        capFinder.addCapabilities("pm2", new BasicSourceCapabilities()); //$NON-NLS-1$
-
-        ProcessorPlan plan = helpPlan("Select e1 from pm1.g1 where e1 in (SELECT ltrim(e1) FROM pm1.g2)", FakeMetadataFactory.example1Cached(),  //$NON-NLS-1$
-            null, capFinder,
-            new String[] { "SELECT e1 FROM pm1.g1" }, SHOULD_SUCCEED); //$NON-NLS-1$ 
-        checkNodeTypes(plan, new int[] {
-            1,      // Access
-            0,      // DependentAccess
-            1,      // DependentSelect
-            0,      // DependentProject
-            0,      // DupRemove
-            0,      // Grouping
-            0,      // NestedLoopJoinStrategy
-            0,      // MergeJoinStrategy
-            0,      // Null
-            0,      // PlanExecution
-            1,      // Project
-            0,      // Select
-            0,      // Sort
-            0       // UnionAll
-        }); 
-    }
-    
-    /**
-     * Check that subquery is not pushed if the subquery selects a constant value
-     */
-    public void testNoPushSubqueryInWhereClause5() {
-        FakeCapabilitiesFinder capFinder = new FakeCapabilitiesFinder();
-        BasicSourceCapabilities caps = new BasicSourceCapabilities();
-        caps.setCapabilitySupport(Capability.QUERY_WHERE, true);
-        caps.setCapabilitySupport(Capability.QUERY_WHERE_IN, true);
-        caps.setCapabilitySupport(Capability.QUERY_WHERE_IN_SUBQUERY, true);
-        capFinder.addCapabilities("pm1", caps); //$NON-NLS-1$
-        capFinder.addCapabilities("pm2", new BasicSourceCapabilities()); //$NON-NLS-1$
-
-        ProcessorPlan plan = helpPlan("Select e1 from pm1.g1 where e1 in (SELECT 'xyz' FROM pm1.g2)", FakeMetadataFactory.example1Cached(),  //$NON-NLS-1$
-            null, capFinder,
-            new String[] { "SELECT e1 FROM pm1.g1" }, SHOULD_SUCCEED); //$NON-NLS-1$ 
-        checkNodeTypes(plan, new int[] {
-            1,      // Access
-            0,      // DependentAccess
-            1,      // DependentSelect
-            0,      // DependentProject
-            0,      // DupRemove
-            0,      // Grouping
-            0,      // NestedLoopJoinStrategy
-            0,      // MergeJoinStrategy
-            0,      // Null
-            0,      // PlanExecution
-            1,      // Project
-            0,      // Select
-            0,      // Sort
-            0       // UnionAll
-        }); 
-    }
-    
-    /**
-     * Check that subquery is not pushed if the subquery does ORDER BY
-     */
-    public void testNoPushSubqueryInWhereClause6() {
-        FakeCapabilitiesFinder capFinder = new FakeCapabilitiesFinder();
-        BasicSourceCapabilities caps = new BasicSourceCapabilities();
-        caps.setCapabilitySupport(Capability.QUERY_WHERE, true);
-        caps.setCapabilitySupport(Capability.QUERY_WHERE_IN, true);
-        caps.setCapabilitySupport(Capability.QUERY_WHERE_IN_SUBQUERY, true);
-        capFinder.addCapabilities("pm1", caps); //$NON-NLS-1$
-        capFinder.addCapabilities("pm2", new BasicSourceCapabilities()); //$NON-NLS-1$
-
-        ProcessorPlan plan = helpPlan("Select e1 from pm1.g1 where e1 in (SELECT e1 FROM pm1.g2 ORDER BY e1)", FakeMetadataFactory.example1Cached(),  //$NON-NLS-1$
-            null, capFinder,
-            new String[] { "SELECT e1 FROM pm1.g1" }, SHOULD_SUCCEED); //$NON-NLS-1$ 
-        checkNodeTypes(plan, new int[] {
-            1,      // Access
-            0,      // DependentAccess
-            1,      // DependentSelect
-            0,      // DependentProject
-            0,      // DupRemove
-            0,      // Grouping
-            0,      // NestedLoopJoinStrategy
-            0,      // MergeJoinStrategy
-            0,      // Null
-            0,      // PlanExecution
-            1,      // Project
-            0,      // Select
-            0,      // Sort
-            0       // UnionAll
-        }); 
-    }
-    
-    /**
-     * Check that subquery is not pushed if the subquery has a function that can't be pushed 
-     * in the SELECT clause
-     */
-    public void testNoPushSubqueryInWhereClause7() {
-        FakeCapabilitiesFinder capFinder = new FakeCapabilitiesFinder();
-        BasicSourceCapabilities caps = new BasicSourceCapabilities();
-        caps.setCapabilitySupport(Capability.QUERY_WHERE, true);
-        caps.setCapabilitySupport(Capability.QUERY_WHERE_IN, true);
-        caps.setCapabilitySupport(Capability.QUERY_WHERE_IN_SUBQUERY, true);
-        caps.setCapabilitySupport(Capability.FUNCTION, true);
-        caps.setFunctionSupport("ltrim", true); //$NON-NLS-1$
-        capFinder.addCapabilities("pm1", caps); //$NON-NLS-1$
-        capFinder.addCapabilities("pm2", new BasicSourceCapabilities()); //$NON-NLS-1$
-
-        ProcessorPlan plan = helpPlan("Select e1 from pm1.g1 where e1 in (SELECT rtrim(ltrim(e1)) FROM pm1.g2)", FakeMetadataFactory.example1Cached(),  //$NON-NLS-1$
-            null, capFinder,
-            new String[] { "SELECT e1 FROM pm1.g1" }, SHOULD_SUCCEED); //$NON-NLS-1$ 
-        checkNodeTypes(plan, new int[] {
-            1,      // Access
-            0,      // DependentAccess
-            1,      // DependentSelect
-            0,      // DependentProject
-            0,      // DupRemove
-            0,      // Grouping
-            0,      // NestedLoopJoinStrategy
-            0,      // MergeJoinStrategy
-            0,      // Null
-            0,      // PlanExecution
-            1,      // Project
-            0,      // Select
-            0,      // Sort
-            0       // UnionAll
-        }); 
-        
-        checkSubPlanCount(plan, 1);
-
-    }
-
-    /**
-     * Check that subquery is not pushed if the subquery holds non-query access node.
-     */
-    public void testNoPushSubqueryInWhereClause8() {
-        FakeCapabilitiesFinder capFinder = new FakeCapabilitiesFinder();
-        BasicSourceCapabilities caps = new BasicSourceCapabilities();
-        caps.setCapabilitySupport(Capability.QUERY_WHERE, true);
-        caps.setCapabilitySupport(Capability.QUERY_WHERE_IN, true);
-        caps.setCapabilitySupport(Capability.QUERY_WHERE_IN_SUBQUERY, true);
-        capFinder.addCapabilities("pm1", caps); //$NON-NLS-1$
-        capFinder.addCapabilities("pm2", new BasicSourceCapabilities()); //$NON-NLS-1$
-
-        ProcessorPlan plan = helpPlan("Select e1 from pm1.g1 where e1 in (EXEC pm1.sqsp1())", FakeMetadataFactory.example1Cached(),  //$NON-NLS-1$
-            null, capFinder,
-            new String[] { "SELECT e1 FROM pm1.g1" }, SHOULD_SUCCEED); //$NON-NLS-1$ 
-        checkNodeTypes(plan, new int[] {
-            1,      // Access
-            0,      // DependentAccess
-            1,      // DependentSelect
-            0,      // DependentProject
-            0,      // DupRemove
-            0,      // Grouping
-            0,      // NestedLoopJoinStrategy
-            0,      // MergeJoinStrategy
-            0,      // Null
-            0,      // PlanExecution
-            1,      // Project
-            0,      // Select
-            0,      // Sort
-            0       // UnionAll
-        }); 
-    }
-
-    /**
-     * Check that subquery is not pushed if the subquery is correlated and correlated not supported
-     */
-    public void testNoPushSubqueryInWhereClause9() {
-        FakeCapabilitiesFinder capFinder = new FakeCapabilitiesFinder();
-        BasicSourceCapabilities caps = new BasicSourceCapabilities();
-        caps.setCapabilitySupport(Capability.QUERY_WHERE, true);
-        caps.setCapabilitySupport(Capability.QUERY_WHERE_IN, true);
-        caps.setCapabilitySupport(Capability.QUERY_WHERE_IN_SUBQUERY, true);
-        caps.setCapabilitySupport(Capability.QUERY_SUBQUERIES_CORRELATED, false);
-        capFinder.addCapabilities("pm1", caps); //$NON-NLS-1$
-
-        ProcessorPlan plan = helpPlan("Select e1 from pm1.g1 where e1 in (SELECT pm1.g2.e1 FROM pm1.g2 WHERE pm1.g2.e1 = pm1.g1.e1)", FakeMetadataFactory.example1Cached(),  //$NON-NLS-1$
-            null, capFinder,
-            new String[] { "SELECT e1 FROM pm1.g1" }, SHOULD_SUCCEED); //$NON-NLS-1$ 
-        checkNodeTypes(plan, new int[] {
-            1,      // Access
-            0,      // DependentAccess
-            1,      // DependentSelect
-            0,      // DependentProject
-            0,      // DupRemove
-            0,      // Grouping
-            0,      // NestedLoopJoinStrategy
-            0,      // MergeJoinStrategy
-            0,      // Null
-            0,      // PlanExecution
-            1,      // Project
-            0,      // Select
-            0,      // Sort
-            0       // UnionAll
-        }); 
-    }   
-    
-    public void testPushCorrelatedSubquery1() {
-        FakeCapabilitiesFinder capFinder = new FakeCapabilitiesFinder();
-        BasicSourceCapabilities caps = new BasicSourceCapabilities();
-        caps.setCapabilitySupport(Capability.QUERY_WHERE, true);
-        caps.setCapabilitySupport(Capability.QUERY_WHERE_COMPARE, true);
-        caps.setCapabilitySupport(Capability.QUERY_WHERE_COMPARE_EQ, true);
-        caps.setCapabilitySupport(Capability.QUERY_WHERE_QUANTIFIED_COMPARISON, true);
-        caps.setCapabilitySupport(Capability.QUERY_WHERE_QUANTIFIED_ALL, true);
-        caps.setCapabilitySupport(Capability.QUERY_WHERE_QUANTIFIED_SOME, true);
-        caps.setCapabilitySupport(Capability.QUERY_WHERE_IN, true);
-        caps.setCapabilitySupport(Capability.QUERY_WHERE_IN_SUBQUERY, true);
-        caps.setCapabilitySupport(Capability.QUERY_SUBQUERIES_SCALAR, true);
-        caps.setCapabilitySupport(Capability.QUERY_SUBQUERIES_CORRELATED, true);
-        caps.setCapabilitySupport(Capability.QUERY_AGGREGATES, true);
-        caps.setCapabilitySupport(Capability.QUERY_AGGREGATES_MAX, true);
-        caps.setCapabilitySupport(Capability.QUERY_FROM_GROUP_ALIAS, true);
-        capFinder.addCapabilities("BQT1", caps); //$NON-NLS-1$
-
-        ProcessorPlan plan = helpPlan("SELECT intkey FROM bqt1.smalla AS n WHERE intkey = (SELECT MAX(intkey) FROM bqt1.smallb AS s WHERE s.stringkey = n.stringkey )", FakeMetadataFactory.exampleBQTCached(),  //$NON-NLS-1$
-            null, capFinder,
-            new String[] { "SELECT intkey FROM bqt1.smalla AS n WHERE intkey = (SELECT MAX(intkey) FROM bqt1.smallb AS s WHERE s.stringkey = n.stringkey)" }, SHOULD_SUCCEED); //$NON-NLS-1$ 
-        checkNodeTypes(plan, FULL_PUSHDOWN); 
-    }   
-
-    public void testPushCorrelatedSubquery2() {
-        FakeCapabilitiesFinder capFinder = new FakeCapabilitiesFinder();
-        BasicSourceCapabilities caps = new BasicSourceCapabilities();
-        caps.setCapabilitySupport(Capability.QUERY_WHERE, true);
-        caps.setCapabilitySupport(Capability.QUERY_WHERE_AND, true);
-        caps.setCapabilitySupport(Capability.QUERY_WHERE_COMPARE, true);
-        caps.setCapabilitySupport(Capability.QUERY_WHERE_COMPARE_EQ, true);
-        caps.setCapabilitySupport(Capability.QUERY_WHERE_LIKE, true);
-        caps.setCapabilitySupport(Capability.QUERY_WHERE_QUANTIFIED_COMPARISON, true);
-        caps.setCapabilitySupport(Capability.QUERY_WHERE_QUANTIFIED_ALL, true);
-        caps.setCapabilitySupport(Capability.QUERY_WHERE_QUANTIFIED_SOME, true);
-        caps.setCapabilitySupport(Capability.QUERY_WHERE_IN, true);
-        caps.setCapabilitySupport(Capability.QUERY_WHERE_IN_SUBQUERY, true);
-        caps.setCapabilitySupport(Capability.QUERY_SUBQUERIES_SCALAR, true);
-        caps.setCapabilitySupport(Capability.QUERY_SUBQUERIES_CORRELATED, true);
-        caps.setCapabilitySupport(Capability.QUERY_AGGREGATES, true);
-        caps.setCapabilitySupport(Capability.QUERY_AGGREGATES_MAX, true);
-        caps.setCapabilitySupport(Capability.QUERY_FROM_GROUP_ALIAS, true);
-        caps.setCapabilitySupport(Capability.QUERY_FROM_JOIN, true);
-        caps.setCapabilitySupport(Capability.QUERY_FROM_JOIN_SELFJOIN, true);
-        caps.setCapabilitySupport(Capability.FUNCTION, true);
-        caps.setFunctionSupport(SourceSystemFunctions.CONCAT, true); //$NON-NLS-1$
-        caps.setFunctionSupport("convert", true); //$NON-NLS-1$
-        capFinder.addCapabilities("BQT1", caps); //$NON-NLS-1$
-
-        String sqlIn = 
-            "SELECT c37n.intkey " + //$NON-NLS-1$
-            "FROM bqt1.mediuma AS c37n, bqt1.smallb AS m37n " + //$NON-NLS-1$
-            "WHERE (m37n.stringkey LIKE '%0') AND " + //$NON-NLS-1$
-            "(c37n.stringkey = ('1' || (m37n.intkey || '0'))) AND " + //$NON-NLS-1$
-            "(c37n.datevalue = (" + //$NON-NLS-1$
-            "SELECT MAX(c37s.datevalue) " + //$NON-NLS-1$
-            "FROM bqt1.mediuma AS c37s, bqt1.smallb AS m37s " + //$NON-NLS-1$
-            "WHERE (m37s.stringkey LIKE '%0') AND " + //$NON-NLS-1$
-            "(c37s.stringkey = ('1' || (m37s.intkey || '0'))) AND " + //$NON-NLS-1$
-            "(m37s.stringkey = m37n.stringkey) ))"; //$NON-NLS-1$
-
-        String sqlOut = "SELECT c37n.intkey FROM bqt1.mediuma AS c37n, bqt1.smallb AS m37n WHERE (c37n.datevalue = (SELECT MAX(c37s.datevalue) FROM bqt1.mediuma AS c37s, bqt1.smallb AS m37s WHERE (c37s.stringkey = ('1' || (m37s.intkey || '0'))) AND (m37s.stringkey LIKE '%0') AND (m37s.stringkey = m37n.stringkey))) AND (c37n.stringkey = ('1' || (m37n.intkey || '0'))) AND (m37n.stringkey LIKE '%0')"; //$NON-NLS-1$
-
-        ProcessorPlan plan = helpPlan(sqlIn, FakeMetadataFactory.exampleBQTCached(),  
-            null, capFinder,
-            new String[] { sqlOut }, SHOULD_SUCCEED); 
-        checkNodeTypes(plan, FULL_PUSHDOWN); 
-    }   
-
-    public void testPushCorrelatedSubquery3() {
-        FakeCapabilitiesFinder capFinder = new FakeCapabilitiesFinder();
-        BasicSourceCapabilities caps = new BasicSourceCapabilities();
-        caps.setCapabilitySupport(Capability.QUERY_WHERE, true);
-        caps.setCapabilitySupport(Capability.QUERY_WHERE_AND, true);
-        caps.setCapabilitySupport(Capability.QUERY_WHERE_COMPARE, true);
-        caps.setCapabilitySupport(Capability.QUERY_WHERE_COMPARE_EQ, true);
-        caps.setCapabilitySupport(Capability.QUERY_WHERE_LIKE, true);
-        caps.setCapabilitySupport(Capability.QUERY_WHERE_QUANTIFIED_COMPARISON, true);
-        caps.setCapabilitySupport(Capability.QUERY_WHERE_QUANTIFIED_ALL, true);
-        caps.setCapabilitySupport(Capability.QUERY_WHERE_QUANTIFIED_SOME, true);
-        caps.setCapabilitySupport(Capability.QUERY_WHERE_IN, true);
-        caps.setCapabilitySupport(Capability.QUERY_WHERE_IN_SUBQUERY, true);
-        caps.setCapabilitySupport(Capability.QUERY_SUBQUERIES_SCALAR, true);
-        caps.setCapabilitySupport(Capability.QUERY_SUBQUERIES_CORRELATED, true);
-        caps.setCapabilitySupport(Capability.QUERY_AGGREGATES, true);
-        caps.setCapabilitySupport(Capability.QUERY_AGGREGATES_MAX, true);
-        caps.setCapabilitySupport(Capability.QUERY_FROM_GROUP_ALIAS, true);
-        caps.setCapabilitySupport(Capability.QUERY_FROM_JOIN, true);
-        caps.setCapabilitySupport(Capability.QUERY_FROM_JOIN_SELFJOIN, true);
-        caps.setCapabilitySupport(Capability.FUNCTION, true);
-        caps.setFunctionSupport("||", true); //$NON-NLS-1$
-        caps.setFunctionSupport("convert", true); //$NON-NLS-1$
-        capFinder.addCapabilities("BQT1", caps); //$NON-NLS-1$
-
-        String sqlIn = 
-            "SELECT intkey " + //$NON-NLS-1$
-            "FROM vqt.smalla AS e " + //$NON-NLS-1$
-            "WHERE (stringkey = 'VOD.L') AND " + //$NON-NLS-1$
-            "(datevalue = (" + //$NON-NLS-1$
-            "SELECT MAX(datevalue) " + //$NON-NLS-1$
-            "FROM vqt.smalla " + //$NON-NLS-1$
-            "WHERE (stringkey = e.stringkey) ))"; //$NON-NLS-1$
-
-        String sqlOut = 
-            "SELECT SmallA__1.IntKey FROM BQT1.SmallA AS SmallA__1 WHERE (SmallA__1.StringKey = 'VOD.L') AND (SmallA__1.DateValue = (SELECT MAX(BQT1.SmallA.DateValue) FROM BQT1.SmallA WHERE BQT1.SmallA.StringKey = SmallA__1.StringKey))"; //$NON-NLS-1$
-        
-        ProcessorPlan plan = helpPlan(sqlIn, FakeMetadataFactory.exampleBQTCached(),  
-            null, capFinder,
-            new String[] { sqlOut }, SHOULD_SUCCEED); 
-        checkNodeTypes(plan, FULL_PUSHDOWN); 
-    }  
-
-    /**
-     * Check that scalar subquery in select is pushed 
-     */
-    public void DEFER_testPushSubqueryInSelectClause1() {
-        FakeCapabilitiesFinder capFinder = new FakeCapabilitiesFinder();
-        BasicSourceCapabilities caps = new BasicSourceCapabilities();
-        caps.setCapabilitySupport(Capability.QUERY_WHERE, true);
-        caps.setCapabilitySupport(Capability.QUERY_WHERE_COMPARE, true);
-        caps.setCapabilitySupport(Capability.QUERY_WHERE_COMPARE_EQ, true);
-        caps.setCapabilitySupport(Capability.QUERY_FROM_GROUP_ALIAS, true);
-        caps.setCapabilitySupport(Capability.QUERY_SUBQUERIES_CORRELATED, true);
-        caps.setCapabilitySupport(Capability.QUERY_SUBQUERIES_SCALAR, true);
-        capFinder.addCapabilities("BQT1", caps); //$NON-NLS-1$
-
-        ProcessorPlan plan = helpPlan("SELECT stringkey, (SELECT intkey FROM BQT1.SmallA AS b WHERE Intnum = 22) FROM BQT1.SmallA", FakeMetadataFactory.exampleBQTCached(),  //$NON-NLS-1$
-            null, capFinder,
-            new String[] { "SELECT stringkey, (SELECT intkey FROM BQT1.SmallA AS b WHERE Intnum = 22) FROM BQT1.SmallA" }, SHOULD_SUCCEED); //$NON-NLS-1$ 
-        checkNodeTypes(plan, new int[] {
-            1,      // Access
-            0,      // DependentAccess
-            0,      // DependentSelect
-            0,      // DependentProject
-            0,      // DupRemove
-            0,      // Grouping
-            0,      // NestedLoopJoinStrategy
-            0,      // MergeJoinStrategy
-            0,      // Null
-            0,      // PlanExecution
-            1,      // Project
-            0,      // Select
-            0,      // Sort
-            0       // UnionAll
-        }); 
-    }   
-
     /** Case 1456, defect 10492*/
     public void testAliasingDefect1(){
         // Create query
@@ -6740,7 +6161,7 @@
                 
         ProcessorPlan plan = helpPlan(
         		"select a.e1, b.e1 from vm2.g1 a, vm2.g1 b where a.e1 = b.e1 and a.e2 in (select e2 from vm1.g1)",  //$NON-NLS-1$
-        		metadata, null, capFinder, new String[] {"SELECT g_1.e1, g_3.e1 FROM pm1.g1 AS g_0, pm1.g2 AS g_1, pm1.g1 AS g_2, pm1.g2 AS g_3 WHERE (g_1.e1 = g_3.e1) AND (g_1.e2 IN (SELECT g_4.e2 FROM pm1.g1 AS g_4)) AND (g_0.e2 = g_1.e2) AND (g_2.e2 = g_3.e2)"}, true); //$NON-NLS-1$
+        		metadata, null, capFinder, new String[] {"SELECT g_1.e1, g_3.e1 FROM pm1.g1 AS g_0, pm1.g2 AS g_1, pm1.g1 AS g_2, pm1.g2 AS g_3 WHERE (g_1.e1 = g_3.e1) AND (g_0.e2 = g_1.e2) AND (g_1.e2 IN (SELECT g_4.e2 FROM pm1.g1 AS g_4)) AND (g_2.e2 = g_3.e2)"}, true); //$NON-NLS-1$
 
         checkNodeTypes(plan, FULL_PUSHDOWN);    
         

Added: trunk/engine/src/test/java/com/metamatrix/query/optimizer/TestSubqueryPushdown.java
===================================================================
--- trunk/engine/src/test/java/com/metamatrix/query/optimizer/TestSubqueryPushdown.java	                        (rev 0)
+++ trunk/engine/src/test/java/com/metamatrix/query/optimizer/TestSubqueryPushdown.java	2009-04-13 01:32:50 UTC (rev 762)
@@ -0,0 +1,694 @@
+/*
+ * 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 com.metamatrix.query.optimizer;
+
+import static com.metamatrix.query.optimizer.TestOptimizer.*;
+
+import org.junit.Test;
+import org.teiid.connector.api.SourceSystemFunctions;
+
+import com.metamatrix.query.optimizer.capabilities.BasicSourceCapabilities;
+import com.metamatrix.query.optimizer.capabilities.FakeCapabilitiesFinder;
+import com.metamatrix.query.optimizer.capabilities.SourceCapabilities.Capability;
+import com.metamatrix.query.processor.ProcessorPlan;
+import com.metamatrix.query.unittest.FakeMetadataFacade;
+import com.metamatrix.query.unittest.FakeMetadataFactory;
+
+public class TestSubqueryPushdown {
+
+	@Test public void testPushSubqueryBelowVirtual() throws Exception {
+		String sql = "select g3.e1 from (select e1, max(e2) y from pm1.g1 group by e1) x, pm1.g3 where exists (select e1 from pm1.g2 where x.e1 = e1)"; //$NON-NLS-1$
+
+	    // Create capabilities
+	    FakeCapabilitiesFinder capFinder = new FakeCapabilitiesFinder();
+	    BasicSourceCapabilities caps = TestOptimizer.getTypicalCapabilities();
+	    caps.setCapabilitySupport(Capability.QUERY_SUBQUERIES_CORRELATED, true);
+	    caps.setCapabilitySupport(Capability.QUERY_WHERE_EXISTS, true);
+	    capFinder.addCapabilities("pm1", caps); //$NON-NLS-1$
+	    
+	    FakeMetadataFacade metadata = FakeMetadataFactory.example1Cached();
+	
+	    // Plan query
+	    ProcessorPlan plan = TestOptimizer.helpPlan(sql, metadata, 
+	        null, capFinder,
+	        new String[] { "SELECT g_0.e1 FROM pm1.g1 AS g_0 WHERE EXISTS (SELECT g_1.e1 FROM pm1.g2 AS g_1 WHERE g_1.e1 = g_0.e1)", //$NON-NLS-1$
+	    		"SELECT g_0.e1 FROM pm1.g3 AS g_0" },  //$NON-NLS-1$
+	        TestOptimizer.ComparisonMode.EXACT_COMMAND_STRING);
+        TestOptimizer.checkNodeTypes(plan, new int[] {
+                2,      // Access
+                0,      // DependentAccess
+                0,      // DependentSelect
+                0,      // DependentProject
+                0,      // DupRemove
+                1,      // Grouping
+                1,      // NestedLoopJoinStrategy
+                0,      // MergeJoinStrategy
+                0,      // Null
+                0,      // PlanExecution
+                2,      // Project
+                0,      // Select
+                0,      // Sort
+                0       // UnionAll
+            });
+	}
+	
+	/**
+	 * Same as above, but using a correlated variable based on an aggregate
+	 * @throws Exception
+	 */
+	@Test public void testDontPushSubqueryBelowVirtual() throws Exception {
+		String sql = "select g3.e1 from (select e1, max(e2) y from pm1.g1 group by e1) x, pm1.g3 where exists (select e1 from pm1.g2 where x.y = e1)"; //$NON-NLS-1$
+
+	    // Create capabilities
+	    FakeCapabilitiesFinder capFinder = new FakeCapabilitiesFinder();
+	    BasicSourceCapabilities caps = TestOptimizer.getTypicalCapabilities();
+	    caps.setCapabilitySupport(Capability.QUERY_SUBQUERIES_CORRELATED, true);
+	    caps.setCapabilitySupport(Capability.QUERY_WHERE_EXISTS, true);
+	    capFinder.addCapabilities("pm1", caps); //$NON-NLS-1$
+	    
+	    FakeMetadataFacade metadata = FakeMetadataFactory.example1Cached();
+	
+	    // Plan query
+	    ProcessorPlan plan = TestOptimizer.helpPlan(sql, metadata, 
+	        null, capFinder,
+	        new String[] { "SELECT g_0.e1 FROM pm1.g3 AS g_0", //$NON-NLS-1$
+	    		"SELECT g_0.e1, g_0.e2 FROM pm1.g1 AS g_0" },  //$NON-NLS-1$
+	        TestOptimizer.ComparisonMode.EXACT_COMMAND_STRING);
+        TestOptimizer.checkNodeTypes(plan, new int[] {
+                2,      // Access
+                0,      // DependentAccess
+                1,      // DependentSelect
+                0,      // DependentProject
+                0,      // DupRemove
+                1,      // Grouping
+                1,      // NestedLoopJoinStrategy
+                0,      // MergeJoinStrategy
+                0,      // Null
+                0,      // PlanExecution
+                2,      // Project
+                0,      // Select
+                0,      // Sort
+                0       // UnionAll
+            });
+	}
+	
+	@Test public void testPushCorrelatedSubquery1() {
+        FakeCapabilitiesFinder capFinder = new FakeCapabilitiesFinder();
+        BasicSourceCapabilities caps = new BasicSourceCapabilities();
+        caps.setCapabilitySupport(Capability.QUERY_WHERE, true);
+        caps.setCapabilitySupport(Capability.QUERY_WHERE_COMPARE, true);
+        caps.setCapabilitySupport(Capability.QUERY_WHERE_COMPARE_EQ, true);
+        caps.setCapabilitySupport(Capability.QUERY_WHERE_QUANTIFIED_COMPARISON, true);
+        caps.setCapabilitySupport(Capability.QUERY_WHERE_QUANTIFIED_ALL, true);
+        caps.setCapabilitySupport(Capability.QUERY_WHERE_QUANTIFIED_SOME, true);
+        caps.setCapabilitySupport(Capability.QUERY_WHERE_IN, true);
+        caps.setCapabilitySupport(Capability.QUERY_WHERE_IN_SUBQUERY, true);
+        caps.setCapabilitySupport(Capability.QUERY_SUBQUERIES_SCALAR, true);
+        caps.setCapabilitySupport(Capability.QUERY_SUBQUERIES_CORRELATED, true);
+        caps.setCapabilitySupport(Capability.QUERY_AGGREGATES, true);
+        caps.setCapabilitySupport(Capability.QUERY_AGGREGATES_MAX, true);
+        caps.setCapabilitySupport(Capability.QUERY_FROM_GROUP_ALIAS, true);
+        capFinder.addCapabilities("BQT1", caps); //$NON-NLS-1$
+
+        ProcessorPlan plan = helpPlan("SELECT intkey FROM bqt1.smalla AS n WHERE intkey = (SELECT MAX(intkey) FROM bqt1.smallb AS s WHERE s.stringkey = n.stringkey )", FakeMetadataFactory.exampleBQTCached(),  //$NON-NLS-1$
+            null, capFinder,
+            new String[] { "SELECT intkey FROM bqt1.smalla AS n WHERE intkey = (SELECT MAX(intkey) FROM bqt1.smallb AS s WHERE s.stringkey = n.stringkey)" }, SHOULD_SUCCEED); //$NON-NLS-1$ 
+        checkNodeTypes(plan, FULL_PUSHDOWN); 
+    }   
+
+    @Test public void testPushCorrelatedSubquery2() {
+        FakeCapabilitiesFinder capFinder = new FakeCapabilitiesFinder();
+        BasicSourceCapabilities caps = new BasicSourceCapabilities();
+        caps.setCapabilitySupport(Capability.QUERY_WHERE, true);
+        caps.setCapabilitySupport(Capability.QUERY_WHERE_AND, true);
+        caps.setCapabilitySupport(Capability.QUERY_WHERE_COMPARE, true);
+        caps.setCapabilitySupport(Capability.QUERY_WHERE_COMPARE_EQ, true);
+        caps.setCapabilitySupport(Capability.QUERY_WHERE_LIKE, true);
+        caps.setCapabilitySupport(Capability.QUERY_WHERE_QUANTIFIED_COMPARISON, true);
+        caps.setCapabilitySupport(Capability.QUERY_WHERE_QUANTIFIED_ALL, true);
+        caps.setCapabilitySupport(Capability.QUERY_WHERE_QUANTIFIED_SOME, true);
+        caps.setCapabilitySupport(Capability.QUERY_WHERE_IN, true);
+        caps.setCapabilitySupport(Capability.QUERY_WHERE_IN_SUBQUERY, true);
+        caps.setCapabilitySupport(Capability.QUERY_SUBQUERIES_SCALAR, true);
+        caps.setCapabilitySupport(Capability.QUERY_SUBQUERIES_CORRELATED, true);
+        caps.setCapabilitySupport(Capability.QUERY_AGGREGATES, true);
+        caps.setCapabilitySupport(Capability.QUERY_AGGREGATES_MAX, true);
+        caps.setCapabilitySupport(Capability.QUERY_FROM_GROUP_ALIAS, true);
+        caps.setCapabilitySupport(Capability.QUERY_FROM_JOIN, true);
+        caps.setCapabilitySupport(Capability.QUERY_FROM_JOIN_SELFJOIN, true);
+        caps.setCapabilitySupport(Capability.FUNCTION, true);
+        caps.setFunctionSupport(SourceSystemFunctions.CONCAT, true);
+        caps.setFunctionSupport("convert", true); //$NON-NLS-1$
+        capFinder.addCapabilities("BQT1", caps); //$NON-NLS-1$
+
+        String sqlIn = 
+            "SELECT c37n.intkey " + //$NON-NLS-1$
+            "FROM bqt1.mediuma AS c37n, bqt1.smallb AS m37n " + //$NON-NLS-1$
+            "WHERE (m37n.stringkey LIKE '%0') AND " + //$NON-NLS-1$
+            "(c37n.stringkey = ('1' || (m37n.intkey || '0'))) AND " + //$NON-NLS-1$
+            "(c37n.datevalue = (" + //$NON-NLS-1$
+            "SELECT MAX(c37s.datevalue) " + //$NON-NLS-1$
+            "FROM bqt1.mediuma AS c37s, bqt1.smallb AS m37s " + //$NON-NLS-1$
+            "WHERE (m37s.stringkey LIKE '%0') AND " + //$NON-NLS-1$
+            "(c37s.stringkey = ('1' || (m37s.intkey || '0'))) AND " + //$NON-NLS-1$
+            "(m37s.stringkey = m37n.stringkey) ))"; //$NON-NLS-1$
+
+        String sqlOut = "SELECT c37n.intkey FROM bqt1.mediuma AS c37n, bqt1.smallb AS m37n WHERE (c37n.datevalue = (SELECT MAX(c37s.datevalue) FROM bqt1.mediuma AS c37s, bqt1.smallb AS m37s WHERE (c37s.stringkey = ('1' || (m37s.intkey || '0'))) AND (m37s.stringkey LIKE '%0') AND (m37s.stringkey = m37n.stringkey))) AND (c37n.stringkey = ('1' || (m37n.intkey || '0'))) AND (m37n.stringkey LIKE '%0')"; //$NON-NLS-1$
+
+        ProcessorPlan plan = helpPlan(sqlIn, FakeMetadataFactory.exampleBQTCached(),  
+            null, capFinder,
+            new String[] { sqlOut }, SHOULD_SUCCEED); 
+        checkNodeTypes(plan, FULL_PUSHDOWN); 
+    }   
+
+    @Test public void testPushCorrelatedSubquery3() {
+        FakeCapabilitiesFinder capFinder = new FakeCapabilitiesFinder();
+        BasicSourceCapabilities caps = new BasicSourceCapabilities();
+        caps.setCapabilitySupport(Capability.QUERY_WHERE, true);
+        caps.setCapabilitySupport(Capability.QUERY_WHERE_AND, true);
+        caps.setCapabilitySupport(Capability.QUERY_WHERE_COMPARE, true);
+        caps.setCapabilitySupport(Capability.QUERY_WHERE_COMPARE_EQ, true);
+        caps.setCapabilitySupport(Capability.QUERY_WHERE_LIKE, true);
+        caps.setCapabilitySupport(Capability.QUERY_WHERE_QUANTIFIED_COMPARISON, true);
+        caps.setCapabilitySupport(Capability.QUERY_WHERE_QUANTIFIED_ALL, true);
+        caps.setCapabilitySupport(Capability.QUERY_WHERE_QUANTIFIED_SOME, true);
+        caps.setCapabilitySupport(Capability.QUERY_WHERE_IN, true);
+        caps.setCapabilitySupport(Capability.QUERY_WHERE_IN_SUBQUERY, true);
+        caps.setCapabilitySupport(Capability.QUERY_SUBQUERIES_SCALAR, true);
+        caps.setCapabilitySupport(Capability.QUERY_SUBQUERIES_CORRELATED, true);
+        caps.setCapabilitySupport(Capability.QUERY_AGGREGATES, true);
+        caps.setCapabilitySupport(Capability.QUERY_AGGREGATES_MAX, true);
+        caps.setCapabilitySupport(Capability.QUERY_FROM_GROUP_ALIAS, true);
+        caps.setCapabilitySupport(Capability.QUERY_FROM_JOIN, true);
+        caps.setCapabilitySupport(Capability.QUERY_FROM_JOIN_SELFJOIN, true);
+        caps.setCapabilitySupport(Capability.FUNCTION, true);
+        caps.setFunctionSupport("||", true); //$NON-NLS-1$
+        caps.setFunctionSupport("convert", true); //$NON-NLS-1$
+        capFinder.addCapabilities("BQT1", caps); //$NON-NLS-1$
+
+        String sqlIn = 
+            "SELECT intkey " + //$NON-NLS-1$
+            "FROM vqt.smalla AS e " + //$NON-NLS-1$
+            "WHERE (stringkey = 'VOD.L') AND " + //$NON-NLS-1$
+            "(datevalue = (" + //$NON-NLS-1$
+            "SELECT MAX(datevalue) " + //$NON-NLS-1$
+            "FROM vqt.smalla " + //$NON-NLS-1$
+            "WHERE (stringkey = e.stringkey) ))"; //$NON-NLS-1$
+
+        String sqlOut = 
+            "SELECT SmallA__1.IntKey FROM BQT1.SmallA AS SmallA__1 WHERE (SmallA__1.StringKey = 'VOD.L') AND (SmallA__1.DateValue = (SELECT MAX(BQT1.SmallA.DateValue) FROM BQT1.SmallA WHERE BQT1.SmallA.StringKey = SmallA__1.StringKey))"; //$NON-NLS-1$
+        
+        ProcessorPlan plan = helpPlan(sqlIn, FakeMetadataFactory.exampleBQTCached(),  
+            null, capFinder,
+            new String[] { sqlOut }, SHOULD_SUCCEED); 
+        checkNodeTypes(plan, FULL_PUSHDOWN); 
+    }  
+
+    /**
+     * Check that scalar subquery in select is pushed 
+     */
+    public void DEFER_testPushSubqueryInSelectClause1() {
+        FakeCapabilitiesFinder capFinder = new FakeCapabilitiesFinder();
+        BasicSourceCapabilities caps = new BasicSourceCapabilities();
+        caps.setCapabilitySupport(Capability.QUERY_WHERE, true);
+        caps.setCapabilitySupport(Capability.QUERY_WHERE_COMPARE, true);
+        caps.setCapabilitySupport(Capability.QUERY_WHERE_COMPARE_EQ, true);
+        caps.setCapabilitySupport(Capability.QUERY_FROM_GROUP_ALIAS, true);
+        caps.setCapabilitySupport(Capability.QUERY_SUBQUERIES_CORRELATED, true);
+        caps.setCapabilitySupport(Capability.QUERY_SUBQUERIES_SCALAR, true);
+        capFinder.addCapabilities("BQT1", caps); //$NON-NLS-1$
+
+        ProcessorPlan plan = helpPlan("SELECT stringkey, (SELECT intkey FROM BQT1.SmallA AS b WHERE Intnum = 22) FROM BQT1.SmallA", FakeMetadataFactory.exampleBQTCached(),  //$NON-NLS-1$
+            null, capFinder,
+            new String[] { "SELECT stringkey, (SELECT intkey FROM BQT1.SmallA AS b WHERE Intnum = 22) FROM BQT1.SmallA" }, SHOULD_SUCCEED); //$NON-NLS-1$ 
+        checkNodeTypes(plan, new int[] {
+            1,      // Access
+            0,      // DependentAccess
+            0,      // DependentSelect
+            0,      // DependentProject
+            0,      // DupRemove
+            0,      // Grouping
+            0,      // NestedLoopJoinStrategy
+            0,      // MergeJoinStrategy
+            0,      // Null
+            0,      // PlanExecution
+            1,      // Project
+            0,      // Select
+            0,      // Sort
+            0       // UnionAll
+        }); 
+    }   
+    
+    @Test public void testCorrelatedSubquery1() {
+        ProcessorPlan plan = helpPlan("Select e1 from pm1.g1 where e1 in (select e1 FROM pm2.g1 WHERE pm1.g1.e2 = pm2.g1.e2)", example1(),  //$NON-NLS-1$
+            new String[] { "SELECT e1, pm1.g1.e2 FROM pm1.g1" }); //$NON-NLS-1$
+        checkNodeTypes(plan, new int[] {
+            1,      // Access
+            0,      // DependentAccess
+            1,      // DependentSelect
+            0,      // DependentProject
+            0,      // DupRemove
+            0,      // Grouping
+            0,      // NestedLoopJoinStrategy
+            0,      // MergeJoinStrategy
+            0,      // Null
+            0,      // PlanExecution
+            1,      // Project
+            0,      // Select
+            0,      // Sort
+            0       // UnionAll
+        }); 
+        
+        checkSubPlanCount(plan, 1);        
+    }
+
+    @Test public void testCorrelatedSubquery2() {
+        ProcessorPlan plan = helpPlan("Select e1, (select e1 FROM pm2.g1 WHERE pm1.g1.e2 = pm2.g1.e2) from pm1.g1", example1(),  //$NON-NLS-1$
+            new String[] { "SELECT e1, pm1.g1.e2 FROM pm1.g1" }); //$NON-NLS-1$
+        checkNodeTypes(plan, new int[] {
+            1,      // Access
+            0,      // DependentAccess
+            0,      // DependentSelect
+            1,      // DependentProject
+            0,      // DupRemove
+            0,      // Grouping
+            0,      // NestedLoopJoinStrategy
+            0,      // MergeJoinStrategy
+            0,      // Null
+            0,      // PlanExecution
+            0,      // Project
+            0,      // Select
+            0,      // Sort
+            0       // UnionAll
+        }); 
+    }
+
+    @Test public void testCorrelatedSubqueryVirtualLayer1() {
+        ProcessorPlan plan = helpPlan("Select e1 from vm1.g6 where e1 in (select e1 FROM pm2.g1 WHERE vm1.g6.e3 = pm2.g1.e2)", example1(),  //$NON-NLS-1$
+            new String[] { "SELECT e1 FROM pm1.g1" }); //$NON-NLS-1$
+        checkNodeTypes(plan, new int[] {
+            1,      // Access
+            0,      // DependentAccess
+            1,      // DependentSelect
+            0,      // DependentProject
+            0,      // DupRemove
+            0,      // Grouping
+            0,      // NestedLoopJoinStrategy
+            0,      // MergeJoinStrategy
+            0,      // Null
+            0,      // PlanExecution
+            1,      // Project
+            0,      // Select
+            0,      // Sort
+            0       // UnionAll
+        }); 
+    }
+
+    @Test public void testCorrelatedSubqueryVirtualLayer2() {
+        ProcessorPlan plan = helpPlan("Select e1 from vm1.g6 where e1 in (select e1 FROM pm2.g1 WHERE vm1.g6.e4 = pm2.g1.e4)", example1(),  //$NON-NLS-1$
+            new String[] { "SELECT e1, e2, e4 FROM pm1.g1" }); //$NON-NLS-1$
+        checkNodeTypes(plan, new int[] {
+            1,      // Access
+            0,      // DependentAccess
+            1,      // DependentSelect
+            0,      // DependentProject
+            0,      // DupRemove
+            0,      // Grouping
+            0,      // NestedLoopJoinStrategy
+            0,      // MergeJoinStrategy
+            0,      // Null
+            0,      // PlanExecution
+            1,      // Project
+            0,      // Select
+            0,      // Sort
+            0       // UnionAll
+        }); 
+    }
+
+    @Test public void testCorrelatedSubqueryVirtualLayer3() {
+        ProcessorPlan plan = helpPlan("Select e1, (select e1 FROM pm2.g1 WHERE vm1.g6.e4 = pm2.g1.e4) from vm1.g6", example1(),  //$NON-NLS-1$
+            new String[] { "SELECT e1, e2, e4 FROM pm1.g1" }); //$NON-NLS-1$
+        checkNodeTypes(plan, new int[] {
+            1,      // Access
+            0,      // DependentAccess
+            0,      // DependentSelect
+            1,      // DependentProject
+            0,      // DupRemove
+            0,      // Grouping
+            0,      // NestedLoopJoinStrategy
+            0,      // MergeJoinStrategy
+            0,      // Null
+            0,      // PlanExecution
+            0,      // Project
+            0,      // Select
+            0,      // Sort
+            0       // UnionAll
+        }); 
+        checkSubPlanCount(plan, 1);        
+    }
+
+    @Test public void testCorrelatedSubqueryInTransformation2() {
+        String sql = "Select * from vm1.g20"; //$NON-NLS-1$
+        ProcessorPlan plan = helpPlan(sql, FakeMetadataFactory.example1Cached(), 
+            new String[] { "SELECT pm1.g1.e1, pm1.g1.e2, pm1.g1.e3, pm1.g1.e4 FROM pm1.g1" }); //$NON-NLS-1$
+        checkNodeTypes(plan, new int[] {
+            1,      // Access
+            0,      // DependentAccess
+            1,      // DependentSelect
+            0,      // DependentProject
+            0,      // DupRemove
+            0,      // Grouping
+            0,      // NestedLoopJoinStrategy
+            0,      // MergeJoinStrategy
+            0,      // Null
+            0,      // PlanExecution
+            1,      // Project
+            0,      // Select
+            0,      // Sort
+            0       // UnionAll
+        });         
+    }
+    
+    /**
+     * Check that subquery is not pushed if the subquery cannot all be pushed to the source.
+     */
+    @Test public void testNoPushSubqueryInWhereClause1() {
+        FakeCapabilitiesFinder capFinder = new FakeCapabilitiesFinder();
+        BasicSourceCapabilities caps = getTypicalCapabilities();
+        caps.setCapabilitySupport(Capability.QUERY_WHERE_IN_SUBQUERY, true);
+        caps.setCapabilitySupport(Capability.QUERY_AGGREGATES, false);
+        capFinder.addCapabilities("pm1", caps); //$NON-NLS-1$
+
+        ProcessorPlan plan = helpPlan("Select e1 from pm1.g1 where e1 in (select max(e1) FROM pm1.g2)", example1(),  //$NON-NLS-1$
+            null, capFinder,
+            new String[] { "SELECT e1 FROM pm1.g1" }, SHOULD_SUCCEED); //$NON-NLS-1$ 
+        checkNodeTypes(plan, new int[] {
+            1,      // Access
+            0,      // DependentAccess
+            1,      // DependentSelect
+            0,      // DependentProject
+            0,      // DupRemove
+            0,      // Grouping
+            0,      // NestedLoopJoinStrategy
+            0,      // MergeJoinStrategy
+            0,      // Null
+            0,      // PlanExecution
+            1,      // Project
+            0,      // Select
+            0,      // Sort
+            0       // UnionAll
+        }); 
+    }
+
+    /**
+     * Check that subquery is not pushed if the subquery is from a different model
+     * than the outer query.
+     */
+    @Test public void testNoPushSubqueryInWhereClause2() {
+        FakeCapabilitiesFinder capFinder = new FakeCapabilitiesFinder();
+        BasicSourceCapabilities caps = getTypicalCapabilities();
+        caps.setCapabilitySupport(Capability.QUERY_WHERE_IN_SUBQUERY, true);
+        capFinder.addCapabilities("pm1", caps); //$NON-NLS-1$
+        capFinder.addCapabilities("pm2", getTypicalCapabilities()); //$NON-NLS-1$
+
+        ProcessorPlan plan = helpPlan("Select e1 from pm1.g1 where e1 in (select e1 FROM pm2.g1)", example1(),  //$NON-NLS-1$
+            null, capFinder,
+            new String[] { "SELECT e1 FROM pm1.g1" }, SHOULD_SUCCEED); //$NON-NLS-1$ 
+        checkNodeTypes(plan, new int[] {
+            1,      // Access
+            0,      // DependentAccess
+            1,      // DependentSelect
+            0,      // DependentProject
+            0,      // DupRemove
+            0,      // Grouping
+            0,      // NestedLoopJoinStrategy
+            0,      // MergeJoinStrategy
+            0,      // Null
+            0,      // PlanExecution
+            1,      // Project
+            0,      // Select
+            0,      // Sort
+            0       // UnionAll
+        }); 
+    }
+    
+    /**
+     * Do not support XML query as subquery
+     * Check that subquery is not pushed if the subquery is not relational.
+     */
+    public void defer_testNoPushSubqueryInWhereClause3() {
+        FakeCapabilitiesFinder capFinder = new FakeCapabilitiesFinder();
+        BasicSourceCapabilities caps = new BasicSourceCapabilities();
+        caps.setCapabilitySupport(Capability.QUERY_WHERE, true);
+        caps.setCapabilitySupport(Capability.QUERY_WHERE_IN, true);
+        caps.setCapabilitySupport(Capability.QUERY_WHERE_IN_SUBQUERY, true);
+        capFinder.addCapabilities("pm1", caps); //$NON-NLS-1$
+        capFinder.addCapabilities("pm2", new BasicSourceCapabilities()); //$NON-NLS-1$
+
+        ProcessorPlan plan = helpPlan("Select e1 from pm1.g1 where e1 in (select * from xmltest.doc1)", FakeMetadataFactory.example1Cached(),  //$NON-NLS-1$
+            null, capFinder,
+            new String[] { "SELECT e1 FROM pm1.g1" }, SHOULD_SUCCEED); //$NON-NLS-1$ 
+        checkNodeTypes(plan, new int[] {
+            1,      // Access
+            0,      // DependentAccess
+            1,      // DependentSelect
+            0,      // DependentProject
+            0,      // DupRemove
+            0,      // Grouping
+            0,      // NestedLoopJoinStrategy
+            0,      // MergeJoinStrategy
+            0,      // Null
+            0,      // PlanExecution
+            1,      // Project
+            0,      // Select
+            0,      // Sort
+            0       // UnionAll
+        }); 
+    }
+
+    /**
+     * Check that subquery is not pushed if the subquery has a function that can't be pushed 
+     * in the SELECT clause
+     */
+    @Test public void testNoPushSubqueryInWhereClause4() {
+        FakeCapabilitiesFinder capFinder = new FakeCapabilitiesFinder();
+        BasicSourceCapabilities caps = new BasicSourceCapabilities();
+        caps.setCapabilitySupport(Capability.QUERY_WHERE, true);
+        caps.setCapabilitySupport(Capability.QUERY_WHERE_IN, true);
+        caps.setCapabilitySupport(Capability.QUERY_WHERE_IN_SUBQUERY, true);
+        capFinder.addCapabilities("pm1", caps); //$NON-NLS-1$
+        capFinder.addCapabilities("pm2", new BasicSourceCapabilities()); //$NON-NLS-1$
+
+        ProcessorPlan plan = helpPlan("Select e1 from pm1.g1 where e1 in (SELECT ltrim(e1) FROM pm1.g2)", FakeMetadataFactory.example1Cached(),  //$NON-NLS-1$
+            null, capFinder,
+            new String[] { "SELECT e1 FROM pm1.g1" }, SHOULD_SUCCEED); //$NON-NLS-1$ 
+        checkNodeTypes(plan, new int[] {
+            1,      // Access
+            0,      // DependentAccess
+            1,      // DependentSelect
+            0,      // DependentProject
+            0,      // DupRemove
+            0,      // Grouping
+            0,      // NestedLoopJoinStrategy
+            0,      // MergeJoinStrategy
+            0,      // Null
+            0,      // PlanExecution
+            1,      // Project
+            0,      // Select
+            0,      // Sort
+            0       // UnionAll
+        }); 
+    }
+    
+    /**
+     * Check that subquery is not pushed if the subquery selects a constant value
+     */
+    @Test public void testNoPushSubqueryInWhereClause5() {
+        FakeCapabilitiesFinder capFinder = new FakeCapabilitiesFinder();
+        BasicSourceCapabilities caps = new BasicSourceCapabilities();
+        caps.setCapabilitySupport(Capability.QUERY_WHERE, true);
+        caps.setCapabilitySupport(Capability.QUERY_WHERE_IN, true);
+        caps.setCapabilitySupport(Capability.QUERY_WHERE_IN_SUBQUERY, true);
+        capFinder.addCapabilities("pm1", caps); //$NON-NLS-1$
+        capFinder.addCapabilities("pm2", new BasicSourceCapabilities()); //$NON-NLS-1$
+
+        ProcessorPlan plan = helpPlan("Select e1 from pm1.g1 where e1 in (SELECT 'xyz' FROM pm1.g2)", FakeMetadataFactory.example1Cached(),  //$NON-NLS-1$
+            null, capFinder,
+            new String[] { "SELECT e1 FROM pm1.g1" }, SHOULD_SUCCEED); //$NON-NLS-1$ 
+        checkNodeTypes(plan, new int[] {
+            1,      // Access
+            0,      // DependentAccess
+            1,      // DependentSelect
+            0,      // DependentProject
+            0,      // DupRemove
+            0,      // Grouping
+            0,      // NestedLoopJoinStrategy
+            0,      // MergeJoinStrategy
+            0,      // Null
+            0,      // PlanExecution
+            1,      // Project
+            0,      // Select
+            0,      // Sort
+            0       // UnionAll
+        }); 
+    }
+    
+    /**
+     * Check that subquery is not pushed if the subquery does ORDER BY
+     */
+    @Test public void testNoPushSubqueryInWhereClause6() {
+        FakeCapabilitiesFinder capFinder = new FakeCapabilitiesFinder();
+        BasicSourceCapabilities caps = new BasicSourceCapabilities();
+        caps.setCapabilitySupport(Capability.QUERY_WHERE, true);
+        caps.setCapabilitySupport(Capability.QUERY_WHERE_IN, true);
+        caps.setCapabilitySupport(Capability.QUERY_WHERE_IN_SUBQUERY, true);
+        capFinder.addCapabilities("pm1", caps); //$NON-NLS-1$
+        capFinder.addCapabilities("pm2", new BasicSourceCapabilities()); //$NON-NLS-1$
+
+        ProcessorPlan plan = helpPlan("Select e1 from pm1.g1 where e1 in (SELECT e1 FROM pm1.g2 ORDER BY e1)", FakeMetadataFactory.example1Cached(),  //$NON-NLS-1$
+            null, capFinder,
+            new String[] { "SELECT e1 FROM pm1.g1" }, SHOULD_SUCCEED); //$NON-NLS-1$ 
+        checkNodeTypes(plan, new int[] {
+            1,      // Access
+            0,      // DependentAccess
+            1,      // DependentSelect
+            0,      // DependentProject
+            0,      // DupRemove
+            0,      // Grouping
+            0,      // NestedLoopJoinStrategy
+            0,      // MergeJoinStrategy
+            0,      // Null
+            0,      // PlanExecution
+            1,      // Project
+            0,      // Select
+            0,      // Sort
+            0       // UnionAll
+        }); 
+    }
+    
+    /**
+     * Check that subquery is not pushed if the subquery has a function that can't be pushed 
+     * in the SELECT clause
+     */
+    @Test public void testNoPushSubqueryInWhereClause7() {
+        FakeCapabilitiesFinder capFinder = new FakeCapabilitiesFinder();
+        BasicSourceCapabilities caps = new BasicSourceCapabilities();
+        caps.setCapabilitySupport(Capability.QUERY_WHERE, true);
+        caps.setCapabilitySupport(Capability.QUERY_WHERE_IN, true);
+        caps.setCapabilitySupport(Capability.QUERY_WHERE_IN_SUBQUERY, true);
+        caps.setCapabilitySupport(Capability.FUNCTION, true);
+        caps.setFunctionSupport("ltrim", true); //$NON-NLS-1$
+        capFinder.addCapabilities("pm1", caps); //$NON-NLS-1$
+        capFinder.addCapabilities("pm2", new BasicSourceCapabilities()); //$NON-NLS-1$
+
+        ProcessorPlan plan = helpPlan("Select e1 from pm1.g1 where e1 in (SELECT rtrim(ltrim(e1)) FROM pm1.g2)", FakeMetadataFactory.example1Cached(),  //$NON-NLS-1$
+            null, capFinder,
+            new String[] { "SELECT e1 FROM pm1.g1" }, SHOULD_SUCCEED); //$NON-NLS-1$ 
+        checkNodeTypes(plan, new int[] {
+            1,      // Access
+            0,      // DependentAccess
+            1,      // DependentSelect
+            0,      // DependentProject
+            0,      // DupRemove
+            0,      // Grouping
+            0,      // NestedLoopJoinStrategy
+            0,      // MergeJoinStrategy
+            0,      // Null
+            0,      // PlanExecution
+            1,      // Project
+            0,      // Select
+            0,      // Sort
+            0       // UnionAll
+        }); 
+        
+        checkSubPlanCount(plan, 1);
+
+    }
+
+    /**
+     * Check that subquery is not pushed if the subquery holds non-query access node.
+     */
+    @Test public void testNoPushSubqueryInWhereClause8() {
+        FakeCapabilitiesFinder capFinder = new FakeCapabilitiesFinder();
+        BasicSourceCapabilities caps = new BasicSourceCapabilities();
+        caps.setCapabilitySupport(Capability.QUERY_WHERE, true);
+        caps.setCapabilitySupport(Capability.QUERY_WHERE_IN, true);
+        caps.setCapabilitySupport(Capability.QUERY_WHERE_IN_SUBQUERY, true);
+        capFinder.addCapabilities("pm1", caps); //$NON-NLS-1$
+        capFinder.addCapabilities("pm2", new BasicSourceCapabilities()); //$NON-NLS-1$
+
+        ProcessorPlan plan = helpPlan("Select e1 from pm1.g1 where e1 in (EXEC pm1.sqsp1())", FakeMetadataFactory.example1Cached(),  //$NON-NLS-1$
+            null, capFinder,
+            new String[] { "SELECT e1 FROM pm1.g1" }, SHOULD_SUCCEED); //$NON-NLS-1$ 
+        checkNodeTypes(plan, new int[] {
+            1,      // Access
+            0,      // DependentAccess
+            1,      // DependentSelect
+            0,      // DependentProject
+            0,      // DupRemove
+            0,      // Grouping
+            0,      // NestedLoopJoinStrategy
+            0,      // MergeJoinStrategy
+            0,      // Null
+            0,      // PlanExecution
+            1,      // Project
+            0,      // Select
+            0,      // Sort
+            0       // UnionAll
+        }); 
+    }
+
+    /**
+     * Check that subquery is not pushed if the subquery is correlated and correlated not supported
+     */
+    @Test public void testNoPushSubqueryInWhereClause9() {
+        FakeCapabilitiesFinder capFinder = new FakeCapabilitiesFinder();
+        BasicSourceCapabilities caps = new BasicSourceCapabilities();
+        caps.setCapabilitySupport(Capability.QUERY_WHERE, true);
+        caps.setCapabilitySupport(Capability.QUERY_WHERE_IN, true);
+        caps.setCapabilitySupport(Capability.QUERY_WHERE_IN_SUBQUERY, true);
+        caps.setCapabilitySupport(Capability.QUERY_SUBQUERIES_CORRELATED, false);
+        capFinder.addCapabilities("pm1", caps); //$NON-NLS-1$
+
+        ProcessorPlan plan = helpPlan("Select e1 from pm1.g1 where e1 in (SELECT pm1.g2.e1 FROM pm1.g2 WHERE pm1.g2.e1 = pm1.g1.e1)", FakeMetadataFactory.example1Cached(),  //$NON-NLS-1$
+            null, capFinder,
+            new String[] { "SELECT e1 FROM pm1.g1" }, SHOULD_SUCCEED); //$NON-NLS-1$ 
+        checkNodeTypes(plan, new int[] {
+            1,      // Access
+            0,      // DependentAccess
+            1,      // DependentSelect
+            0,      // DependentProject
+            0,      // DupRemove
+            0,      // Grouping
+            0,      // NestedLoopJoinStrategy
+            0,      // MergeJoinStrategy
+            0,      // Null
+            0,      // PlanExecution
+            1,      // Project
+            0,      // Select
+            0,      // Sort
+            0       // UnionAll
+        }); 
+    }       
+
+}


Property changes on: trunk/engine/src/test/java/com/metamatrix/query/optimizer/TestSubqueryPushdown.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain




More information about the teiid-commits mailing list