gt:         FakeCapabilitiesFinder capFinder = new FakeCapabilitiesFinder();
context: /*
   * JBoss, Home of Professional Open Source.
   * See the COPYRIGHT.txt file distributed with this work for information
   * regarding copyright ownership.  Some portions may be licensed
   * to Red Hat, Inc. under one or more contributor license agreements.
   * 
   * This library is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
   * License as published by the Free Software Foundation; either
   * version 2.1 of the License, or (at your option) any later version.
   * 
   * This library is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * Lesser General Public License for more details.
   * 
   * You should have received a copy of the GNU Lesser General Public
   * License along with this library; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
   * 02110-1301 USA.
   */
  
  package org.teiid.query.optimizer;
  
  import static org.junit.Assert.*;
  
  import java.util.Arrays;
  import java.util.Collection;
  import java.util.HashSet;
  import java.util.Set;
  
  import org.junit.Test;
  import org.teiid.core.TeiidComponentException;
  import org.teiid.core.TeiidProcessingException;
  import org.teiid.metadata.Column;
  import org.teiid.query.metadata.CompositeMetadataStore;
  import org.teiid.query.metadata.QueryMetadataInterface;
  import org.teiid.query.metadata.SystemMetadata;
  import org.teiid.query.metadata.TransformationMetadata;
  import org.teiid.query.optimizer.TestOptimizer.ComparisonMode;
  import org.teiid.query.optimizer.capabilities.BasicSourceCapabilities;
  import org.teiid.query.optimizer.capabilities.CapabilitiesFinder;
  import org.teiid.query.optimizer.capabilities.DefaultCapabilitiesFinder;
  import org.teiid.query.optimizer.capabilities.FakeCapabilitiesFinder;
  import org.teiid.query.optimizer.capabilities.SourceCapabilities.Capability;
  import org.teiid.query.optimizer.relational.rules.RuleChooseDependent;
  import org.teiid.query.processor.ProcessorPlan;
  import org.teiid.query.processor.relational.AccessNode;
  import org.teiid.query.processor.relational.DependentAccessNode;
  import org.teiid.query.processor.relational.JoinNode;
  import org.teiid.query.processor.relational.RelationalNode;
  import org.teiid.query.processor.relational.RelationalPlan;
  import org.teiid.query.sql.lang.Command;
  import org.teiid.query.sql.lang.JoinType;
  import org.teiid.query.sql.symbol.GroupSymbol;
  import org.teiid.query.sql.visitor.GroupCollectorVisitor;
  import org.teiid.query.unittest.RealMetadataFactory;
  
  @SuppressWarnings("nls")
  public class TestDependentJoins {
      
      static void checkDependentGroups(ProcessorPlan plan, String[] groups) {
          if(! (plan instanceof RelationalPlan)) {
              return;                
          }
                                  
          // Collect all the group names (uppercase) for all the dependent groups in the plan
          Set<String> depGroups = new HashSet<String>();
          getDependentGroups(((RelationalPlan)plan).getRootNode(), depGroups, true);
  
          // Check that all the expected groups exist in depGroups
          Set<String> expectedGroups = new HashSet<String>();
          for(int i=0; i<groups.length; i++) {
              expectedGroups.add(groups[i].toUpperCase());    
          }        
          
          assertEquals("Expected groups were not made dependent", expectedGroups, depGroups);         //$NON-NLS-1$
      }
      
      static void getDependentGroups(RelationalNode node, Set<String> depGroups, boolean depdenent) {
          if(node instanceof AccessNode) {
          	if (node instanceof DependentAccessNode) {
          		if (!depdenent) {
          			return;
          		}
          	} else if (depdenent) {
          		return;
          	}
              AccessNode accessNode = (AccessNode)node;
              Command depCommand = accessNode.getCommand();
              Collection<GroupSymbol> groupSymbols = GroupCollectorVisitor.getGroups(depCommand, true);
              for (GroupSymbol groupSymbol : groupSymbols) {
          		depGroups.add(groupSymbol.getNonCorrelationName().toUpperCase());
              }
          }
          
          // Recurse through children
          RelationalNode[] children = node.getChildren();
          for(int i=0; i<children.length; i++) {
              if(children[i] != null) {
                  getDependentGroups(node.getChildren()[i], depGroups, depdenent);                
              }
          }
      }
              
      private void checkNotDependentGroups(ProcessorPlan plan, String[] groups) {
          if(! (plan instanceof RelationalPlan)) {
              return;                
          }
                                  
          // Collect all the group names (uppercase) for all the dependent groups in the plan
          Set<String> notDepGroups = new HashSet<String>();
          getDependentGroups(((RelationalPlan)plan).getRootNode(), notDepGroups, false);
  
          // Check that all the expected groups exist in depGroups
          Set<String> expectedGroups = new HashSet<String>();
          for(int i=0; i<groups.length; i++) {
              expectedGroups.add(groups[i].toUpperCase());    
          }        
          
          assertEquals("Expected groups were made dependent", expectedGroups, notDepGroups);         //$NON-NLS-1$
      }
      
      @Test public void testOptionMakeDep1() throws Exception {
  
          FakeCapabilitiesFinder capFinder = new FakeCapabilitiesFinder();
          BasicSourceCapabilities caps = TestOptimizer.getTypicalCapabilities();
          caps.setCapabilitySupport(Capability.QUERY_ORDERBY, false);
          capFinder.addCapabilities("pm1", caps); //$NON-NLS-1$
          capFinder.addCapabilities("pm2", caps); //$NON-NLS-1$
          
          ProcessorPlan plan = TestOptimizer.helpPlan("select pm1.g1.e1 from pm1.g1, pm2.g1 where pm1.g1.e1 = pm2.g1.e1 option makedep pm2.g1", RealMetadataFactory.example1Cached(), null, capFinder, //$NON-NLS-1$
              new String[] { "SELECT g_0.e1 FROM pm2.g1 AS g_0 WHERE g_0.e1 IN (<dependent values>)", "SELECT g_0.e1 FROM pm1.g1 AS g_0" }, TestOptimizer.ComparisonMode.EXACT_COMMAND_STRING); //$NON-NLS-1$ //$NON-NLS-2$
  
          checkDependentGroups(plan, new String[] {"pm2.g1"}); //$NON-NLS-1$
          checkNotDependentGroups(plan, new String[] {"pm1.g1"}); //$NON-NLS-1$
          TestOptimizer.checkNodeTypes(plan, new int[] {
              1,      // Access
              1,      // DependentAccess
              0,      // DependentSelect
              0,      // DependentProject
              0,      // DupRemove
              0,      // Grouping
              0,      // NestedLoopJoinStrategy
              1,      // MergeJoinStrategy
              0,      // Null
              0,      // PlanExecution
              1,      // Project
              0,      // Select
              0,      // Sort
              0       // UnionAll
          });
      }
      
      @Test public void testOptionMakeDep2() throws Exception {
          FakeCapabilitiesFinder capFinder = new FakeCapabilitiesFinder();
          BasicSourceCapabilities caps = TestOptimizer.getTypicalCapabilities();
          caps.setCapabilitySupport(Capability.QUERY_ORDERBY, false);
          capFinder.addCapabilities("pm1", caps); //$NON-NLS-1$
          capFinder.addCapabilities("pm2", caps); //$NON-NLS-1$
          
          ProcessorPlan plan = TestOptimizer.helpPlan("select pm1.g1.e1, pm2.g1.e1 from pm1.g1 MAKEDEP INNER JOIN pm2.g1 MAKENOTDEP ON pm1.g1.e1 = pm2.g1.e1", RealMetadataFactory.example1Cached(), null, capFinder, //$NON-NLS-1$
                  new String[] { "SELECT g_0.e1 FROM pm1.g1 AS g_0 WHERE g_0.e1 IN (<dependent values>)", "SELECT g_0.e1 FROM pm2.g1 AS g_0" }, TestOptimizer.ComparisonMode.EXACT_COMMAND_STRING); //$NON-NLS-1$ //$NON-NLS-2$
  
          checkDependentGroups(plan, new String[] {"pm1.g1"}); //$NON-NLS-1$
          checkNotDependentGroups(plan, new String[] {"pm2.g1"}); //$NON-NLS-1$
          TestOptimizer.checkNodeTypes(plan, new int[] {
              1,      // Access
              1,      // DependentAccess
              0,      // DependentSelect
              0,      // DependentProject
              0,      // DupRemove
              0,      // Grouping
              0,      // NestedLoopJoinStrategy
              1,      // MergeJoinStrategy
              0,      // Null
              0,      // PlanExecution
              1,      // Project
              0,      // Select
              0,      // Sort
              0       // UnionAll
          });
      }
      
      @Test public void testDepJoinHintForceLeft() throws Exception {
      	ProcessorPlan plan = TestOptimizer.helpPlan("select * FROM vm1.g4 option makedep pm1.g1", TestOptimizer.example1(), //$NON-NLS-1$
              new String[] { "SELECT g_0.e1 FROM pm1.g1 AS g_0 WHERE g_0.e1 IN (<dependent values>)", "SELECT g_0.e1 FROM pm1.g2 AS g_0" }, TestOptimizer.getGenericFinder(false), TestOptimizer.ComparisonMode.EXACT_COMMAND_STRING ); //$NON-NLS-1$ //$NON-NLS-2$
          TestOptimizer.checkNodeTypes(plan, new int[] {
              1,      // Access
              1,      // DependentAccess
              0,      // DependentSelect
              0,      // DependentProject
              0,      // DupRemove
              0,      // Grouping
              0,      // NestedLoopJoinStrategy
              1,      // MergeJoinStrategy
              0,      // Null
              0,      // PlanExecution
              1,      // Project
              0,      // Select
              0,      // Sort
              0       // UnionAll
          }); 
          checkDependentGroups(plan, new String[] {"pm1.g1"});                             //$NON-NLS-1$
      }
  
      @Test public void testDepJoinHintForceRight() throws Exception {
          
          ProcessorPlan plan = TestOptimizer.helpPlan("select * FROM vm1.g4 option makedep pm1.g2", TestOptimizer.example1(), //$NON-NLS-1$
              new String[] { "SELECT g_0.e1 FROM pm1.g2 AS g_0 WHERE g_0.e1 IN (<dependent values>)", "SELECT g_0.e1 FROM pm1.g1 AS g_0" }, TestOptimizer.getGenericFinder(false), TestOptimizer.ComparisonMode.EXACT_COMMAND_STRING ); //$NON-NLS-1$ //$NON-NLS-2$ 
          TestOptimizer.checkNodeTypes(plan, new int[] {
              1,      // Access
              1,      // DependentAccess
              0,      // DependentSelect
              0,      // DependentProject
              0,      // DupRemove
              0,      // Grouping
              0,      // NestedLoopJoinStrategy
              1,      // MergeJoinStrategy
              0,      // Null
              0,      // PlanExecution
              1,      // Project
              0,      // Select
              0,      // Sort
              0       // UnionAll
          }); 
          checkDependentGroups(plan, new String[] {"pm1.g2"});                             //$NON-NLS-1$
      }
      
      @Test public void testGlobalHint() throws Exception {
          ProcessorPlan plan = TestOptimizer.helpPlan("select * FROM vm1.g4 option makedep @g4.g2", TestOptimizer.example1(), //$NON-NLS-1$
              new String[] { "SELECT g_0.e1 FROM pm1.g2 AS g_0 WHERE g_0.e1 IN (<dependent values>)", "SELECT g_0.e1 FROM pm1.g1 AS g_0" }, TestOptimizer.getGenericFinder(false), TestOptimizer.ComparisonMode.EXACT_COMMAND_STRING ); //$NON-NLS-1$ //$NON-NLS-2$ 
          TestOptimizer.checkNodeTypes(plan, new int[] {
              1,      // Access
              1,      // DependentAccess
              0,      // DependentSelect
              0,      // DependentProject
              0,      // DupRemove
              0,      // Grouping
              0,      // NestedLoopJoinStrategy
              1,      // MergeJoinStrategy
              0,      // Null
              0,      // PlanExecution
              1,      // Project
              0,      // Select
              0,      // Sort
              0       // UnionAll
          }); 
          checkDependentGroups(plan, new String[] {"pm1.g2"});                             //$NON-NLS-1$
      }
      
      @Test public void testGlobalHint1() throws Exception {
          ProcessorPlan plan = TestOptimizer.helpPlan("select * FROM vm1.g4 as x option makedep @x.g2", TestOptimizer.example1(), //$NON-NLS-1$
              new String[] { "SELECT g_0.e1 FROM pm1.g2 AS g_0 WHERE g_0.e1 IN (<dependent values>)", "SELECT g_0.e1 FROM pm1.g1 AS g_0" }, TestOptimizer.getGenericFinder(false), TestOptimizer.ComparisonMode.EXACT_COMMAND_STRING ); //$NON-NLS-1$ //$NON-NLS-2$ 
          TestOptimizer.checkNodeTypes(plan, new int[] {
              1,      // Access
              1,      // DependentAccess
              0,      // DependentSelect
              0,      // DependentProject
              0,      // DupRemove
              0,      // Grouping
              0,      // NestedLoopJoinStrategy
              1,      // MergeJoinStrategy
              0,      // Null
              0,      // PlanExecution
              1,      // Project
              0,      // Select
              0,      // Sort
              0       // UnionAll
          }); 
          checkDependentGroups(plan, new String[] {"pm1.g2"});                             //$NON-NLS-1$
      }
  
  	@Test public void testDepJoinMultiGroupBaseline() throws Exception {
          ProcessorPlan plan = TestOptimizer.helpPlan("select vm1.g4.*, pm1.g3.e1 FROM vm1.g4, pm1.g3 where pm1.g3.e1=vm1.g4.e1", TestOptimizer.example1(), //$NON-NLS-1$
              new String[] { "SELECT pm1.g1.e1 FROM pm1.g1", //$NON-NLS-1$
                              "SELECT pm1.g2.e1 FROM pm1.g2", //$NON-NLS-1$
                              "SELECT pm1.g3.e1 FROM pm1.g3" }, TestOptimizer.getGenericFinder(false), ComparisonMode.CORRECTED_COMMAND_STRING ); //$NON-NLS-1$
          TestOptimizer.checkNodeTypes(plan, new int[] {
              3,      // Access
              0,      // DependentAccess
              0,      // DependentSelect
              0,      // DependentProject
              0,      // DupRemove
              0,      // Grouping
              0,      // NestedLoopJoinStrategy
              2,      // MergeJoinStrategy
              0,      // Null
              0,      // PlanExecution
              1,      // Project
              0,      // Select
              0,      // Sort
              0       // UnionAll
          }); 
          checkDependentGroups(plan, new String[0]);                             
      }
  
      @Test public void testDepJoinMultiGroupForceOther() throws Exception {
          ProcessorPlan plan = TestOptimizer.helpPlan("select vm1.g4.*, pm1.g3.e1 FROM vm1.g4, pm1.g3 where pm1.g3.e1=vm1.g4.e1 option makedep pm1.g2", TestOptimizer.example1(), //$NON-NLS-1$
              new String[] { "SELECT g_0.e1 FROM pm1.g3 AS g_0", "SELECT g_0.e1 FROM pm1.g2 AS g_0 WHERE g_0.e1 IN (<dependent values>)", "SELECT g_0.e1 FROM pm1.g1 AS g_0" }, TestOptimizer.getGenericFinder(false), TestOptimizer.ComparisonMode.EXACT_COMMAND_STRING ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
          TestOptimizer.checkNodeTypes(plan, new int[] {
              2,      // Access
              1,      // DependentAccess
              0,      // DependentSelect
              0,      // DependentProject
              0,      // DupRemove
              0,      // Grouping
              0,      // NestedLoopJoinStrategy
              2,      // MergeJoinStrategy
              0,      // Null
              0,      // PlanExecution
              1,      // Project
              0,      // Select
              0,      // Sort
              0       // UnionAll
          }); 
          checkDependentGroups(plan, new String[] {"pm1.g2"});                             //$NON-NLS-1$ 
      }
  
      @Test public void testDepJoinHintForceLeft_NotDep() throws Exception {
          ProcessorPlan plan = TestOptimizer.helpPlan("select * FROM vm1.g4 option makedep pm1.g1 makenotdep pm1.g2", TestOptimizer.example1(), //$NON-NLS-1$
              new String[] { "SELECT g_0.e1 FROM pm1.g1 AS g_0 WHERE g_0.e1 IN (<dependent values>)", "SELECT g_0.e1 FROM pm1.g2 AS g_0" }, TestOptimizer.getGenericFinder(false), TestOptimizer.ComparisonMode.EXACT_COMMAND_STRING ); //$NON-NLS-1$ //$NON-NLS-2$
          TestOptimizer.checkNodeTypes(plan, new int[] {
              1,      // Access
              1,      // DependentAccess
              0,      // DependentSelect
              0,      // DependentProject
              0,      // DupRemove
              0,      // Grouping
              0,      // NestedLoopJoinStrategy
              1,      // MergeJoinStrategy
              0,      // Null
              0,      // PlanExecution
              1,      // Project
              0,      // Select
              0,      // Sort
              0       // UnionAll
          }); 
          checkDependentGroups(plan, new String[] {"pm1.g1"});                             //$NON-NLS-1$
          checkNotDependentGroups(plan, new String[] {"pm1.g2"});                             //$NON-NLS-1$
      }
  
      @Test public void testDepJoinHintForceRight_NotDep() throws Exception {
          ProcessorPlan plan = TestOptimizer.helpPlan("select * FROM vm1.g4 option makedep pm1.g2 makenotdep pm1.g1", TestOptimizer.example1(), //$NON-NLS-1$
              new String[] { "SELECT g_0.e1 FROM pm1.g2 AS g_0 WHERE g_0.e1 IN (<dependent values>)", "SELECT g_0.e1 FROM pm1.g1 AS g_0" }, TestOptimizer.getGenericFinder(false), TestOptimizer.ComparisonMode.EXACT_COMMAND_STRING ); //$NON-NLS-1$ //$NON-NLS-2$
          TestOptimizer.checkNodeTypes(plan, new int[] {
              1,      // Access
              1,      // DependentAccess
              0,      // DependentSelect
              0,      // DependentProject
              0,      // DupRemove
              0,      // Grouping
              0,      // NestedLoopJoinStrategy
              1,      // MergeJoinStrategy
              0,      // Null
              0,      // PlanExecution
              1,      // Project
              0,      // Select
              0,      // Sort
              0       // UnionAll
          }); 
          checkDependentGroups(plan, new String[] {"pm1.g2"});                             //$NON-NLS-1$
          checkNotDependentGroups(plan, new String[] {"pm1.g1"});                             //$NON-NLS-1$
      }
  
      @Test public void testDepJoinMultiGroupForceOther_NotDep() throws Exception {
          ProcessorPlan plan = TestOptimizer.helpPlan("select vm1.g4.*, pm1.g3.e1 FROM vm1.g4, pm1.g3 where pm1.g3.e1=vm1.g4.e1 option makedep pm1.g2 makenotdep pm1.g1, pm1.g3", TestOptimizer.example1(), //$NON-NLS-1$
              new String[] { "SELECT g_0.e1 FROM pm1.g3 AS g_0", "SELECT g_0.e1 FROM pm1.g2 AS g_0 WHERE g_0.e1 IN (<dependent values>)", "SELECT g_0.e1 FROM pm1.g1 AS g_0" }, TestOptimizer.getGenericFinder(false), TestOptimizer.ComparisonMode.EXACT_COMMAND_STRING ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
          TestOptimizer.checkNodeTypes(plan, new int[] {
              2,      // Access
              1,      // DependentAccess
              0,      // DependentSelect
              0,      // DependentProject
              0,      // DupRemove
              0,      // Grouping
              0,      // NestedLoopJoinStrategy
              2,      // MergeJoinStrategy
              0,      // Null
              0,      // PlanExecution
              1,      // Project
              0,      // Select
              0,      // Sort
              0       // UnionAll
          }); 
          checkDependentGroups(plan, new String[] {"pm1.g2"}); //$NON-NLS-1$ 
          checkNotDependentGroups(plan, new String[] {"pm1.g1", "pm1.g3"}); //$NON-NLS-1$ //$NON-NLS-2$
      }
      
      /**
       * Test that access node with unsatisfied access pattern is made dependent
       */
      @Test public void testMakeDependentAccessPattern1() throws Exception {
          ProcessorPlan plan = TestOptimizer.helpPlan("select pm1.g1.e1 from pm1.g1, pm4.g1 where pm1.g1.e1 = pm4.g1.e1", RealMetadataFactory.example1Cached(), //$NON-NLS-1$
              new String[] { "SELECT g_0.e1 FROM pm4.g1 AS g_0 WHERE g_0.e1 IN (<dependent values>)", "SELECT g_0.e1 FROM pm1.g1 AS g_0" }, TestOptimizer.getGenericFinder(false), TestOptimizer.ComparisonMode.EXACT_COMMAND_STRING); //$NON-NLS-1$ //$NON-NLS-2$
          checkDependentGroups(plan, new String[] {"pm4.g1"}); //$NON-NLS-1$
      }
  
      /**
       * Test that access node with unsatisfied access pattern is made dependent
       * (Same query written slightly different way)
       */
      @Test public void testMakeDependentAccessPattern1a() throws Exception {
          ProcessorPlan plan = TestOptimizer.helpPlan("select pm1.g1.e1 from pm4.g1, pm1.g1 where pm4.g1.e1 = pm1.g1.e1", RealMetadataFactory.example1Cached(), //$NON-NLS-1$
              new String[] { "SELECT g_0.e1 FROM pm4.g1 AS g_0 WHERE g_0.e1 IN (<dependent values>)", "SELECT g_0.e1 FROM pm1.g1 AS g_0" }, TestOptimizer.getGenericFinder(false), TestOptimizer.ComparisonMode.EXACT_COMMAND_STRING); //$NON-NLS-1$ //$NON-NLS-2$
          checkDependentGroups(plan, new String[] {"pm4.g1"}); //$NON-NLS-1$
      }
      
      /**
       * Test that access node with unsatisfied access pattern is made dependent
       */
      @Test public void testMakeDependentAccessPattern2() throws Exception {
          ProcessorPlan plan = TestOptimizer.helpPlan("select pm1.g1.e1 from pm1.g1, pm4.g1 where pm4.g1.e2 = 1 and pm1.g1.e1 = pm4.g1.e1", RealMetadataFactory.example1Cached(), //$NON-NLS-1$
              new String[] { "SELECT g_0.e1 FROM pm4.g1 AS g_0 WHERE (g_0.e2 = 1) AND (g_0.e1 IN (<dependent values>))", "SELECT g_0.e1 FROM pm1.g1 AS g_0" }, TestOptimizer.getGenericFinder(false), TestOptimizer.ComparisonMode.EXACT_COMMAND_STRING); //$NON-NLS-1$ //$NON-NLS-2$
          checkDependentGroups(plan, new String[] {"pm4.g1"}); //$NON-NLS-1$
      }
  
      /**
       * Test that second access pattern of access node is chosen to make
       * dependent with
       */
      @Test public void testMakeDependentAccessPattern3() throws Exception {
          ProcessorPlan plan = TestOptimizer.helpPlan("select pm1.g1.e1 from pm1.g1, pm4.g2 where pm1.g1.e1 = pm4.g2.e5", RealMetadataFactory.example1Cached(), //$NON-NLS-1$
              new String[] { "SELECT g_0.e5 FROM pm4.g2 AS g_0 WHERE g_0.e5 IN (<dependent values>)", "SELECT g_0.e1 FROM pm1.g1 AS g_0" }, TestOptimizer.getGenericFinder(false), TestOptimizer.ComparisonMode.EXACT_COMMAND_STRING); //$NON-NLS-1$ //$NON-NLS-2$
          checkDependentGroups(plan, new String[] {"pm4.g2"}); //$NON-NLS-1$
      }
  
      /**
       * This case actually tests the dead-tie case - either access node could
       * be made dependent, but merge join is used since no access pattern 
       * needs to be fulfilled and there is no cost info available for either source
       */
      @Test public void testPushSelectAndMakeDependentAccessPattern1() throws Exception {
          ProcessorPlan plan = TestOptimizer.helpPlan("select pm1.g1.e1 from pm1.g1, pm4.g1 where pm4.g1.e1 = 'abc' and pm1.g1.e1 = 'abc' and pm1.g1.e2 = pm4.g1.e2", RealMetadataFactory.example1Cached(), //$NON-NLS-1$
              new String[] { "SELECT pm1.g1.e2, pm1.g1.e1 FROM pm1.g1 WHERE pm1.g1.e1 = 'abc'", "SELECT pm4.g1.e2 FROM pm4.g1 WHERE pm4.g1.e1 = 'abc'" }, TestOptimizer.getGenericFinder(false), ComparisonMode.CORRECTED_COMMAND_STRING); //$NON-NLS-1$ //$NON-NLS-2$
          checkDependentGroups(plan, new String[0]); 
  
          TestOptimizer.checkNodeTypes(plan, new int[] {
              2,      // Access
              0,      // DependentAccess
              0,      // DependentSelect
              0,      // DependentProject
              0,      // DupRemove
              0,      // Grouping
              0,      // NestedLoopJoinStrategy
              1,      // MergeJoinStrategy
              0,      // Null
              0,      // PlanExecution
              1,      // Project
              0,      // Select
              0,      // Sort
              0       // UnionAll
          }); 
      }
  
      /**
       * This case actually tests the dead-tie case - either access node could
       * be made dependent, but merge join is used since no access pattern 
       * needs to be fulfilled and there is no cost info available for either source
       * (Same query written slightly different)
       */
      @Test public void testPushSelectAndMakeDependentAccessPattern1a() throws Exception {
          ProcessorPlan plan = TestOptimizer.helpPlan("select pm1.g1.e1 from pm4.g1, pm1.g1 where pm4.g1.e2 = pm1.g1.e2 and pm4.g1.e1 = 'abc' and pm1.g1.e1 = 'abc'", RealMetadataFactory.example1Cached(), //$NON-NLS-1$
              new String[] { "SELECT pm1.g1.e2, pm1.g1.e1 FROM pm1.g1 WHERE pm1.g1.e1 = 'abc'", "SELECT pm4.g1.e2 FROM pm4.g1 WHERE pm4.g1.e1 = 'abc'" }, TestOptimizer.getGenericFinder(false), ComparisonMode.CORRECTED_COMMAND_STRING); //$NON-NLS-1$ //$NON-NLS-2$
          checkDependentGroups(plan, new String[0]); 
      
          TestOptimizer.checkNodeTypes(plan, new int[] {
              2,      // Access
              0,      // DependentAccess
              0,      // DependentSelect
              0,      // DependentProject
              0,      // DupRemove
              0,      // Grouping
              0,      // NestedLoopJoinStrategy
              1,      // MergeJoinStrategy
              0,      // Null
              0,      // PlanExecution
              1,      // Project
              0,      // Select
              0,      // Sort
              0       // UnionAll
          }); 
      }
  
      /**
       * Tests that it is enforced if an access node can't be made dependent
       * because of it's (already-satisfied) access pattern - merge join is used
       */
      @Test public void testPushSelectAndMakeDependentAccessPattern2() throws Exception {
          ProcessorPlan plan = TestOptimizer.helpPlan("select pm1.g1.e1 from pm1.g1, pm4.g1 where pm4.g1.e1 = 'abc' and pm1.g1.e2 = pm4.g1.e2", RealMetadataFactory.example1Cached(), //$NON-NLS-1$
              new String[] { "SELECT pm1.g1.e2, pm1.g1.e1 FROM pm1.g1", "SELECT pm4.g1.e2 FROM pm4.g1 WHERE pm4.g1.e1 = 'abc'" }, TestOptimizer.getGenericFinder(false), ComparisonMode.CORRECTED_COMMAND_STRING); //$NON-NLS-1$ //$NON-NLS-2$
          checkDependentGroups(plan, new String[0] ); 
  
          TestOptimizer.checkNodeTypes(plan, new int[] {
              2,      // Access
              0,      // DependentAccess
              0,      // DependentSelect
              0,      // DependentProject
              0,      // DupRemove
              0,      // Grouping
              0,      // NestedLoopJoinStrategy
              1,      // MergeJoinStrategy
              0,      // Null
              0,      // PlanExecution
              1,      // Project
              0,      // Select
              0,      // Sort
              0       // UnionAll
          }); 
      }
      
      /** Should use dependent join since one access node is "strong" */
      @Test public void testUseMergeJoin1() throws Exception {
          // Create query
          String sql = "SELECT pm1.g1.e1 FROM pm1.g1, pm1.g2 WHERE pm1.g1.e1 = pm1.g2.e1";//$NON-NLS-1$
  
          FakeCapabilitiesFinder capFinder = new FakeCapabilitiesFinder();
          BasicSourceCapabilities caps = new BasicSourceCapabilities();
          caps.setCapabilitySupport(Capability.CRITERIA_COMPARE_EQ, true);
          caps.setCapabilitySupport(Capability.CRITERIA_QUANTIFIED_SOME, true);
          caps.setCapabilitySupport(Capability.CRITERIA_IN, true);
          caps.setCapabilitySupport(Capability.QUERY_FROM_GROUP_ALIAS, true);
          caps.setSourceProperty(Capability.MAX_IN_CRITERIA_SIZE, new Integer(10));
          capFinder.addCapabilities("pm1", caps); //$NON-NLS-1$
  
          QueryMetadataInterface metadata = RealMetadataFactory.example1();
          RealMetadataFactory.setCardinality("pm1.g1", RuleChooseDependent.DEFAULT_INDEPENDENT_CARDINALITY - 1, metadata);
              
          ProcessorPlan plan = TestOptimizer.helpPlan(sql, metadata,  
              null, capFinder,
              new String[] { "SELECT g_0.e1 FROM pm1.g2 AS g_0 WHERE g_0.e1 IN (<dependent values>)", "SELECT g_0.e1 FROM pm1.g1 AS g_0" }, TestOptimizer.ComparisonMode.EXACT_COMMAND_STRING); //$NON-NLS-1$ //$NON-NLS-2$
          TestOptimizer.checkNodeTypes(plan, new int[] {
              1,      // Access
              1,      // DependentAccess
              0,      // DependentSelect
              0,      // DependentProject
              0,      // DupRemove
              0,      // Grouping
              0,      // NestedLoopJoinStrategy
              1,      // MergeJoinStrategy
              0,      // Null
              0,      // PlanExecution
              1,      // Project
              0,      // Select
              0,      // Sort
              0       // UnionAll
          });         
      }
  
      /** Should not use a dependent join since neither access node is "strong" */
      @Test public void testUseMergeJoin2() throws Exception {
          // Create query
          String sql = "SELECT pm1.g1.e1 FROM pm1.g1, pm1.g2 WHERE pm1.g1.e1 = pm1.g2.e1";//$NON-NLS-1$
  
  