gt:         Row[] expected = new Row[]{
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.server.test.it.qp;
  
  import com.foundationdb.qp.expression.IndexBound;
  import com.foundationdb.qp.expression.IndexKeyRange;
  import com.foundationdb.qp.operator.Cursor;
  import com.foundationdb.qp.operator.Operator;
  import com.foundationdb.qp.row.Row;
  import com.foundationdb.qp.rowtype.IndexRowType;
  import com.foundationdb.qp.rowtype.RowType;
  import com.foundationdb.qp.rowtype.Schema;
  import com.foundationdb.qp.rowtype.TableRowType;
  import com.foundationdb.server.api.dml.SetColumnSelector;
  import org.junit.Ignore;
  import org.junit.Test;
  
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Collections;
  import java.util.List;
  
  import static com.foundationdb.qp.operator.API.*;
  
  public class GroupLookup_DefaultIT extends OperatorITBase
  {
      @Override
      protected void setupPostCreateSchema() {
          super.setupPostCreateSchema();
          Row[] dbWithOrphans = new Row[]{
              row(customer, 1L, "northbridge"),
              row(customer, 2L, "foundation"),
              row(customer, 4L, "highland"),
              row(address, 1001L, 1L, "111 1111 st"),
              row(address, 1002L, 1L, "111 2222 st"),
              row(address, 2001L, 2L, "222 1111 st"),
              row(address, 2002L, 2L, "222 2222 st"),
              row(address, 4001L, 4L, "444 1111 st"),
              row(address, 4002L, 4L, "444 2222 st"),
              row(order, 11L, 1L, "ori"),
              row(order, 12L, 1L, "david"),
              row(order, 21L, 2L, "tom"),
              row(order, 22L, 2L, "jack"),
              row(order, 31L, 3L, "peter"),
              row(item, 111L, 11L),
              row(item, 112L, 11L),
              row(item, 121L, 12L),
              row(item, 122L, 12L),
              row(item, 211L, 21L),
              row(item, 212L, 21L),
              row(item, 221L, 22L),
              row(item, 222L, 22L),
              // orphans
              row(address, 5001L, 5L, "555 1111 st"),
              row(item, 311L, 31L),
              row(item, 312L, 31L)};
          use(dbWithOrphans);
      }
  
      protected int lookaheadQuantum() {
          return 1;
      }
  
      // IllegalArumentException tests
  
      @Test(expected = IllegalArgumentException.class)
      public void testAtLeastOneAncestor()
      {
          groupLookup_Default(groupScan_Default(coi),
                              coi,
                              customerRowType,
                              list(),
                              InputPreservationOption.KEEP_INPUT,
                              lookaheadQuantum());
      }
  
      @Test(expected = IllegalArgumentException.class)
      public void testKeepInputSelf()
      {
          groupLookup_Default(groupScan_Default(coi),
                              coi,
                              customerRowType,
                              list(customerRowType),
                              InputPreservationOption.KEEP_INPUT,
                              lookaheadQuantum());
      }
  
      @Test(expected = IllegalArgumentException.class)
      public void testKeepIndexInput()
      {
          groupLookup_Default(groupScan_Default(coi),
                              coi,
                              customerNameIndexRowType,
                              list(customerRowType),
                              InputPreservationOption.KEEP_INPUT,
                              lookaheadQuantum());
      }
  
      @Test(expected = IllegalArgumentException.class)
      public void testBranchNonRootLookup()
      {
          groupLookup_Default(groupScan_Default(coi),
                              coi,
                              addressRowType,
                              list(itemRowType),
                              InputPreservationOption.KEEP_INPUT,
                              lookaheadQuantum());
      }
  
      @Test(expected = IllegalArgumentException.class)
      public void testBranchRootLookup()
      {
          groupLookup_Default(groupScan_Default(coi),
                              coi,
                              addressRowType,
                              list(orderRowType, itemRowType),
                              InputPreservationOption.KEEP_INPUT,
                              lookaheadQuantum());
      }
  
      // Test ancestor lookup given index row
  
      @Test
      public void testItemIndexToMissingCustomerAndOrder()
      {
          Operator plan = indexRowToAncestorPlan(999, customerRowType, orderRowType);
          Cursor cursor = cursor(plan, queryContext, queryBindings);
          Row[] expected = new Row[]{};
          compareRows(expected, cursor);
      }
  
      @Test
      public void testItemIndexToCustomerAndOrder()
      {
          Operator plan = indexRowToAncestorPlan(111, customerRowType, orderRowType);
          Cursor cursor = cursor(plan, queryContext, queryBindings);
          Row[] expected = new Row[]{
              row(customerRowType, 1L, "northbridge"),
              row(orderRowType, 11L, 1L, "ori")
          };
          compareRows(expected, cursor);
      }
  
      @Test
      public void testItemIndexToCustomerOnly()
      {
          Operator plan = indexRowToAncestorPlan(111, customerRowType);
          Cursor cursor = cursor(plan, queryContext, queryBindings);
          Row[] expected = new Row[]{
              row(customerRowType, 1L, "northbridge")
          };
          compareRows(expected, cursor);
      }
  
      @Test
      public void testItemIndexToOrderOnly()
      {
          Operator plan = indexRowToAncestorPlan(111, orderRowType);
          Cursor cursor = cursor(plan, queryContext, queryBindings);
          Row[] expected = new Row[]{
              row(orderRowType, 11L, 1L, "ori")
          };
          compareRows(expected, cursor);
      }
  
      @Test
      public void testOrphanItemIndexToCustomerAndOrder()
      {
          Operator plan = indexRowToAncestorPlan(311, customerRowType, orderRowType);
          Cursor cursor = cursor(plan, queryContext, queryBindings);
          Row[] expected = new Row[]{
              row(orderRowType, 31L, 3L, "peter")
          };
          compareRows(expected, cursor);
      }
  
      @Test
      public void testOrphanItemIndexToCustomerOnly()
      {
          Operator plan = indexRowToAncestorPlan(311, customerRowType);
          Cursor cursor = cursor(plan, queryContext, queryBindings);
          Row[] expected = new Row[]{};
          compareRows(expected, cursor);
      }
  
      @Test
      public void testOrphanItemIndexToOrderOnly()
      {
          Operator plan = indexRowToAncestorPlan(311, orderRowType);
          Cursor cursor = cursor(plan, queryContext, queryBindings);
          Row[] expected = new Row[]{
              row(orderRowType, 31L, 3L, "peter")
          };
          compareRows(expected, cursor);
      }
  
      @Test
      public void testOrphanItemIndexToItem()
      {
          Operator plan = indexRowToAncestorPlan(311, itemRowType);
          Cursor cursor = cursor(plan, queryContext, queryBindings);
          Row[] expected = new Row[]{
              row(itemRowType, 311L, 31L)
          };
          compareRows(expected, cursor);
      }
  
      // Test ancestor lookup given group row
  
      @Test
      public void testItemRowToMissingCustomerAndOrder()
      {
          Operator plan = groupRowToAncestorPlan(999, true, customerRowType, orderRowType);
          Cursor cursor = cursor(plan, queryContext, queryBindings);
          Row[] expected = new Row[]{};
          compareRows(expected, cursor);
      }
  
      @Test
      public void testItemRowToCustomerAndOrder()
      {
          // Keep input
          Operator plan = groupRowToAncestorPlan(111, true, customerRowType, orderRowType);
          Cursor cursor = cursor(plan, queryContext, queryBindings);
          Row[] expected = new Row[]{
              row(customerRowType, 1L, "northbridge"),
              row(orderRowType, 11L, 1L, "ori"),
              row(itemRowType, 111L, 11L)
          };
          compareRows(expected, cursor);
          // Don't keep input
          plan = groupRowToAncestorPlan(111, false, customerRowType, orderRowType);
          cursor = cursor(plan, queryContext, queryBindings);
          expected = new Row[]{
              row(customerRowType, 1L, "northbridge"),
              row(orderRowType, 11L, 1L, "ori")
          };
          compareRows(expected, cursor);
      }
  
      @Test
      public void testItemRowToCustomerOnly()
      {
          // Keep input
          Operator plan = groupRowToAncestorPlan(111, true, customerRowType);
          Cursor cursor = cursor(plan, queryContext, queryBindings);
          Row[] expected = new Row[]{
              row(customerRowType, 1L, "northbridge"),
              row(itemRowType, 111L, 11L)
          };
          compareRows(expected, cursor);
          // Don't keep input
          plan = groupRowToAncestorPlan(111, false, customerRowType);
          cursor = cursor(plan, queryContext, queryBindings);
          expected = new Row[]{
              row(customerRowType, 1L, "northbridge")
          };
          compareRows(expected, cursor);
      }
  
      @Test
      public void testItemRowToOrderOnly()
      {
          // Keep input
          Operator plan = groupRowToAncestorPlan(111, true, orderRowType);
          Cursor cursor = cursor(plan, queryContext, queryBindings);
          Row[] expected = new Row[]{
              row(orderRowType, 11L, 1L, "ori"),
              row(itemRowType, 111L, 11L)
          };
          compareRows(expected, cursor);
          // Don't keep input
          plan = groupRowToAncestorPlan(111, false, orderRowType);
          cursor = cursor(plan, queryContext, queryBindings);
          expected = new Row[]{
              row(orderRowType, 11L, 1L, "ori")
          };
          compareRows(expected, cursor);
      }
  
      @Test
      public void testOrphanItemRowToCustomerAndOrder()
      {
          // Keep input
          Operator plan = groupRowToAncestorPlan(311, true, customerRowType, orderRowType);
          Cursor cursor = cursor(plan, queryContext, queryBindings);
          Row[] expected = new Row[]{
              row(orderRowType, 31L, 3L, "peter"),
              row(itemRowType, 311L, 31L)
          };
          compareRows(expected, cursor);
          // Don't keep input
          plan = groupRowToAncestorPlan(311, false, customerRowType, orderRowType);
          cursor = cursor(plan, queryContext, queryBindings);
          expected = new Row[]{
              row(orderRowType, 31L, 3L, "peter")
          };
          compareRows(expected, cursor);
      }
  
      @Test
      public void testOrphanItemRowToCustomerOnly()
      {
          // Keep input
          Operator plan = groupRowToAncestorPlan(311, true, customerRowType);
          Cursor cursor = cursor(plan, queryContext, queryBindings);
          Row[] expected = new Row[]{
              row(itemRowType, 311L, 31L)
          };
          compareRows(expected, cursor);
          // Don't keep input
          plan = groupRowToAncestorPlan(311, false, customerRowType);
          cursor = cursor(plan, queryContext, queryBindings);
          expected = new Row[]{
          };
          compareRows(expected, cursor);
      }
  
      @Test
      public void testOrphanItemRowToOrderOnly()
      {
          // Keep input
          Operator plan = groupRowToAncestorPlan(311, true, orderRowType);
          Cursor cursor = cursor(plan, queryContext, queryBindings);
          Row[] expected = new Row[]{
              row(orderRowType, 31L, 3L, "peter"),
              row(itemRowType, 311L, 31L)
          };
          compareRows(expected, cursor);
          // Don't keep input
          plan = groupRowToAncestorPlan(311, false, orderRowType);
          cursor = cursor(plan, queryContext, queryBindings);
          expected = new Row[]{
              row(orderRowType, 31L, 3L, "peter")
          };
          compareRows(expected, cursor);
      }
      
      // hkey input
      
      @Test
      public void testOrderHKeyToCustomerAndOrder()
      {
          Operator plan = orderHKeyToCustomerAndOrderPlan("jack");
          Row[] expected = new Row[]{
              row(customerRowType, 2L, "foundation"),
              row(orderRowType, 22L, 2L, "jack"),
          };
          compareRows(expected, cursor(plan, queryContext, queryBindings));
      }
  
      // customer index -> customer + descendents
  
      @Test
      public void testCustomerIndexToMissingCustomer()
      {
          Operator plan = customerNameToCustomerPlan("matrix");
          Cursor cursor = cursor(plan, queryContext, queryBindings);
          Row[] expected = new Row[]{};
          compareRows(expected, cursor);
      }
  
      @Test
      public void testCustomerIndexToCustomer()
      {
          Operator plan = customerNameToCustomerPlan("northbridge");
          Cursor cursor = cursor(plan, queryContext, queryBindings);
          Row[] expected = new Row[]{
              row(customerRowType, 1L, "northbridge"),
              row(orderRowType, 11L, 1L, "ori"),
              row(itemRowType, 111L, 11L),
              row(itemRowType, 112L, 11L),
              row(orderRowType, 12L, 1L, "david"),
              row(itemRowType, 121L, 12L),
              row(itemRowType, 122L, 12L),
              row(addressRowType, 1001L, 1L, "111 1111 st"),
              row(addressRowType, 1002L, 1L, "111 2222 st")
          };
          compareRows(expected, cursor);
      }
  
      @Test
      public void testCustomerIndexToCustomerWithNoOrders()
      {
          Operator plan = customerNameToCustomerPlan("highland");
          Cursor cursor = cursor(plan, queryContext, queryBindings);
          Row[] expected = new Row[]{
              row(customerRowType, 4L, "highland"),
              row(addressRowType, 4001L, 4L, "444 1111 st"),
              row(addressRowType, 4002L, 4L, "444 2222 st")
          };
          compareRows(expected, cursor);
      }
  
      // address index -> customer + descendents
      // MOSTLY IGNORE since not supported by GroupLookup_Default currently.
  
      @Test @Ignore
      public void testAddressIndexToMissingCustomer()
      {
          Operator plan = addressAddressToCustomerPlan("555 1111 st");
          Cursor cursor = cursor(plan, queryContext, queryBindings);
          Row[] expected = new Row[]{
              row(addressRowType, 5001L, 5L, "555 1111 st"),
          };
          compareRows(expected, cursor);
      }
  
      @Test @Ignore
      public void testAddressIndexToCustomer()
      {
          Operator plan = addressAddressToCustomerPlan("222 2222 st");
          Cursor cursor = cursor(plan, queryContext, queryBindings);
          Row[] expected = new Row[]{
              row(customerRowType, 2L, "foundation"),
              row(orderRowType, 21L, 2L, "tom"),
              row(itemRowType, 211L, 21L),
              row(itemRowType, 212L, 21L),
              row(orderRowType, 22L, 2L, "jack"),
              row(itemRowType, 221L, 22L),
              row(itemRowType, 222L, 22L),
              row(addressRowType, 2001L, 2L, "222 1111 st"),
              row(addressRowType, 2002L, 2L, "222 2222 st")
          };
          compareRows(expected, cursor);
      }
  
      // address row -> customer + descendents
  
      @Test @Ignore
      public void testAddressToMissingCustomer()
      {
          Operator plan = addressToCustomerPlan("555 1111 st");
          Cursor cursor = cursor(plan, queryContext, queryBindings);
          Row[] expected = new Row[]{
              row(addressRowType, 5001L, 5L, "555 1111 st"),
          };
          compareRows(expected, cursor);
      }
  
      @Test @Ignore
      public void testAddressToCustomer()
      {
          Operator plan = addressToCustomerPlan("222 2222 st");
          Cursor cursor = cursor(plan, queryContext, queryBindings);
  