gt:         if (leafMostParent != null) {
context: /**
   * Copyright (C) 2009-2013 FoundationDB, LLC
   *
   * This program is free software: you can redistribute it and/or modify
   * it under the terms of the GNU Affero General Public License as published by
   * the Free Software Foundation, either version 3 of the License, or
   * (at your option) any later version.
   *
   * This program 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 Affero General Public License for more details.
   *
   * You should have received a copy of the GNU Affero General Public License
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   */
  
  package com.foundationdb.sql.optimizer.rule;
  
  import com.foundationdb.sql.optimizer.plan.*;
  import com.foundationdb.sql.optimizer.plan.JoinNode.JoinType;
  import com.foundationdb.sql.optimizer.plan.TableGroupJoinTree.TableGroupJoinNode;
  
  import com.foundationdb.server.error.AkibanInternalException;
  
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import java.util.*;
  
  /** Get tables in groups into homogenous rows, consisting of Products
   * (of Products ...) of Flattened rows.
   * That is, the graph is covered by a connected set of "strips" of
   * flattened branches. Connectedness is achieved by
   * <code>KEEP_INPUT</code> and so replicating the branchpoint inside
   * the nested loop and then flattening it in.
   */
  public class BranchJoiner extends BaseRule 
  {
      private static final Logger logger = LoggerFactory.getLogger(BranchJoiner.class);
  
      @Override
      protected Logger getLogger() {
          return logger;
      }
  
      static class TableGroupsFinder implements PlanVisitor, ExpressionVisitor {
          List<TableGroupJoinTree> result = new ArrayList<>();
  
          public List<TableGroupJoinTree> find(PlanNode root) {
              root.accept(this);
              return result;
          }
  
          @Override
          public boolean visitEnter(PlanNode n) {
              return visit(n);
          }
  
          @Override
          public boolean visitLeave(PlanNode n) {
              return true;
          }
  
          @Override
          public boolean visit(PlanNode n) {
              if (n instanceof TableGroupJoinTree) {
                  result.add((TableGroupJoinTree)n);
              }
              return true;
          }
  
          @Override
          public boolean visitEnter(ExpressionNode n) {
              return visit(n);
          }
  
          @Override
          public boolean visitLeave(ExpressionNode n) {
              return true;
          }
  
          @Override
          public boolean visit(ExpressionNode n) {
              return true;
          }
      }
  
      @Override
      public void apply(PlanContext planContext) {
          List<TableGroupJoinTree> groups = new TableGroupsFinder().find(planContext.getPlan());
          for (TableGroupJoinTree tableGroup : groups) {
              PlanNode joins = joinBranches(tableGroup);
              tableGroup.getOutput().replaceInput(tableGroup, joins);
          }
      }
  
      protected PlanNode joinBranches(TableGroupJoinTree tableGroup) {
          TableGroupJoinNode rootTable = tableGroup.getRoot();
          PlanNode scan = tableGroup.getScan();
          if (scan instanceof IndexScan) {
              IndexScan indexScan = (IndexScan)scan;
              if (indexScan.isCovering())
                  return indexScan;
          }
          Set<TableSource> requiredTables = null;
          if (scan instanceof BaseScan) {
              requiredTables = ((BaseScan)scan).getRequiredTables();
          }
          markBranches(tableGroup, requiredTables);
          top:
          if (scan instanceof JoinTreeScan) {
              TableSource indexTable = ((JoinTreeScan)scan).getLeafMostTable();
              TableGroupJoinNode leafTable = rootTable.findTable(indexTable);
              assert (leafTable != null) : scan;
              List<TableSource> ancestors = new ArrayList<>();
              pendingTableSources(leafTable, rootTable, ancestors);
              if (isParent(leafTable)) {
                  if (ancestors.remove(indexTable))
                      setPending(leafTable); // Changed from ancestor to branch.
                  List<TableSource> tables = new ArrayList<>();
                  scan = new BranchLookup(scan, indexTable.getTable(), tables);
                  leafTable = singleBranchPending(leafTable, tables);
              }
              else if (!isRequired(leafTable)) {
                  // Don't need the table that the index points to or
                  // anything beneath it; might be able to jump to a
                  // side branch.
                  PlanNode sideScan = trySideBranch(scan, leafTable, rootTable,
                                                    indexTable, ancestors);
                  if (sideScan != null) {
                      scan = sideScan;
                      break top;
                  }
              }
              if (!ancestors.isEmpty())
                  scan = new AncestorLookup(scan, indexTable, ancestors);
              scan = flatten(scan, leafTable, rootTable);
              scan = fillSideBranches(scan, leafTable, rootTable);
          }
          else if (scan instanceof GroupScan) {
              GroupScan groupScan = (GroupScan)scan;
              List<TableSource> tables = new ArrayList<>();
              groupScan.setTables(tables);
              scan = fillBranch(scan, tables, rootTable, rootTable, rootTable);
          }
          else if (scan instanceof GroupLoopScan) {
              GroupLoopScan groupLoop = (GroupLoopScan)scan;
              TableSource outsideTable = groupLoop.getOutsideTable();
              TableSource insideTable = groupLoop.getInsideTable();
              if (groupLoop.isInsideParent()) {
                  TableGroupJoinNode parent = rootTable.findTable(groupLoop.getInsideTable());
                  assert (parent != null) : groupLoop;
                  List<TableSource> ancestors = new ArrayList<>();
                  pendingTableSources(parent, rootTable, ancestors);
                  scan = new AncestorLookup(scan, outsideTable, ancestors);
                  scan = flatten(scan, parent, rootTable);
                  scan = fillGroupLoopBranches(scan, parent, rootTable);
              }
              else {
                  assert (groupLoop.getInsideTable() == rootTable.getTable());
                  List<TableSource> tables = new ArrayList<>();
                  scan = new BranchLookup(scan, outsideTable.getTable(), insideTable.getTable(), tables);
                  scan = fillBranch(scan, tables, rootTable, rootTable, rootTable);
              }
          }
          else if (scan instanceof FullTextScan) {
              FullTextScan textScan = (FullTextScan)scan;
              TableSource indexSource = textScan.getIndexTable();
              TableGroupJoinNode indexTable = rootTable.findTable(indexSource);
              assert (indexTable != null) : textScan;
              List<TableSource> ancestors = new ArrayList<>();
              pendingTableSources(indexTable, rootTable, ancestors);
              if (isParent(indexTable)) {
                  if (ancestors.remove(indexSource))
                      setPending(indexTable); // Changed from ancestor to branch.
                  List<TableSource> tables = new ArrayList<>();
                  scan = new BranchLookup(scan, indexSource.getTable(), tables);
                  indexTable = singleBranchPending(indexTable, tables);
              }
              if (!ancestors.isEmpty())
                  scan = new AncestorLookup(scan, indexSource, ancestors);
              scan = flatten(scan, indexTable, rootTable);
              scan = fillSideBranches(scan, indexTable, rootTable);
          }
          else {
              throw new AkibanInternalException("Unknown TableGroupJoinTree scan");
          }
          for (TableGroupJoinNode table : tableGroup) {
              assert !isPending(table) : table;
          }
          return scan;
      }
  
      /** Try to switch from the main branch over to a side branch.
       * When there is nothing on the main branch, it would work to just
       * carry on and <code>Product</code> the <code>IndexScan</code>
       * alone with whatever else is needed. But a slightly better plan
       * is to switch over to a some needed branch with a non-nested
       * <code>BranchLookup</code> and carry on from there.
       */
      protected PlanNode trySideBranch(PlanNode scan,
                                       TableGroupJoinNode leafTable,
                                       TableGroupJoinNode rootTable,
                                       TableSource indexTable,
                                       List<TableSource> ancestors) {
          // If there any any ancestors, need a child of the leaf-most,
          // so that we can BranchLookup over there and still be able to
          // get all the required ancestors. If there aren't any
          // ancestors, anyplace that BranchLookup can take us to will
          // do.
          boolean findRequired = !ancestors.isEmpty();
          TableGroupJoinNode leafMostChild = leafTable;
          TableGroupJoinNode leafMostParent = null;
          while (leafMostChild != rootTable) {
              TableGroupJoinNode parent = leafMostChild.getParent();
              if (findRequired ? isRequired(parent) : isParent(parent)) {
                  leafMostParent = parent;
                  break;
              }
              leafMostChild = parent;
          }
          TableGroupJoinNode sideBranch = null;
  