/** As {@link #testFilterQueryOnProjectView3()} but also contains an expression column. */
 @Test
 public void testFilterQueryOnProjectView5() {
   checkMaterialize(
       "select \"deptno\" - 10 as \"x\", \"empid\" + 1 as ee, \"name\"\n" + "from \"emps\"",
       "select \"name\", \"empid\" + 1 as e\n" + "from \"emps\" where \"deptno\" - 10 = 2",
       JdbcTest.HR_MODEL,
       CalciteAssert.checkResultContains(
           "EnumerableCalc(expr#0..2=[{inputs}], expr#3=[2], expr#4=[=($t0, $t3)], name=[$t2], E=[$t1], $condition=[$t4])\n"
               + "  EnumerableTableScan(table=[[hr, m0]]"));
 }
 @Test
 public void testFilter() {
   CalciteAssert.that()
       .withMaterializations(
           JdbcTest.HR_MODEL, "m0", "select * from \"emps\" where \"deptno\" = 10")
       .query("select \"empid\" + 1 from \"emps\" where \"deptno\" = 10")
       .enableMaterializations(true)
       .explainContains("EnumerableTableScan(table=[[hr, m0]])")
       .sameResultWithMaterializationsDisabled();
 }
 /** Aggregation materialization with a project. */
 @Ignore("work in progress")
 @Test
 public void testAggregateProject() {
   // Note that materialization does not start with the GROUP BY columns.
   // Not a smart way to design a materialization, but people may do it.
   checkMaterialize(
       "select \"deptno\", count(*) as c, \"empid\" + 2, sum(\"empid\") as s from \"emps\" group by \"empid\", \"deptno\"",
       "select count(*) + 1 as c, \"deptno\" from \"emps\" group by \"deptno\"",
       JdbcTest.HR_MODEL,
       CalciteAssert.checkResultContains("xxx"));
 }
 /**
  * Aggregation query at coarser level of aggregation than aggregation materialization. Requires an
  * additional aggregate to roll up. Note that COUNT is rolled up using SUM.
  */
 @Test
 public void testAggregateRollUp() {
   checkMaterialize(
       "select \"empid\", \"deptno\", count(*) as c, sum(\"empid\") as s from \"emps\" group by \"empid\", \"deptno\"",
       "select count(*) + 1 as c, \"deptno\" from \"emps\" group by \"deptno\"",
       JdbcTest.HR_MODEL,
       CalciteAssert.checkResultContains(
           "EnumerableCalc(expr#0..1=[{inputs}], expr#2=[1], expr#3=[+($t1, $t2)], C=[$t3], deptno=[$t0])\n"
               + "  EnumerableAggregate(group=[{1}], agg#0=[$SUM0($2)])\n"
               + "    EnumerableTableScan(table=[[hr, m0]])"));
 }
 /** As {@link #testFilterQueryOnFilterView()} but condition is weaker in view. */
 @Ignore("not implemented")
 @Test
 public void testFilterQueryOnFilterView3() {
   checkMaterialize(
       "select \"deptno\", \"empid\", \"name\" from \"emps\" where \"deptno\" = 10 or \"deptno\" = 20 or \"empid\" < 160",
       "select \"empid\" + 1 as x, \"name\" from \"emps\" where \"deptno\" = 10",
       JdbcTest.HR_MODEL,
       CalciteAssert.checkResultContains(
           "EnumerableCalcRel(expr#0..2=[{inputs}], expr#3=[1], expr#4=[+($t1, $t3)], X=[$t4], name=[$t2], condition=?)\n"
               + "  EnumerableTableScan(table=[[hr, m0]])"));
 }
 /** Checks that a given query CAN NOT use a materialized view with a given definition. */
 private void checkNoMaterialize(String materialize, String query, String model) {
   try {
     Prepare.THREAD_TRIM.set(true);
     MaterializationService.setThreadLocal();
     CalciteAssert.that()
         .withMaterializations(model, "m0", materialize)
         .query(query)
         .enableMaterializations(true)
         .explainContains("EnumerableTableScan(table=[[hr, emps]])");
   } finally {
     Prepare.THREAD_TRIM.set(false);
   }
 }
 /** Checks that a given query can use a materialized view with a given definition. */
 private void checkMaterialize(
     String materialize, String query, String model, Function<ResultSet, Void> explainChecker) {
   try {
     Prepare.THREAD_TRIM.set(true);
     MaterializationService.setThreadLocal();
     CalciteAssert.that()
         .withMaterializations(model, "m0", materialize)
         .query(query)
         .enableMaterializations(true)
         .explainMatches("", explainChecker)
         .sameResultWithMaterializationsDisabled();
   } finally {
     Prepare.THREAD_TRIM.set(false);
   }
 }
 @Test
 public void testFilterQueryOnProjectView() {
   try {
     Prepare.THREAD_TRIM.set(true);
     MaterializationService.setThreadLocal();
     CalciteAssert.that()
         .withMaterializations(
             JdbcTest.HR_MODEL, "m0", "select \"deptno\", \"empid\" from \"emps\"")
         .query("select \"empid\" + 1 as x from \"emps\" where \"deptno\" = 10")
         .enableMaterializations(true)
         .explainContains("EnumerableTableScan(table=[[hr, m0]])")
         .sameResultWithMaterializationsDisabled();
   } finally {
     Prepare.THREAD_TRIM.set(false);
   }
 }
/**
 * Unit test for the materialized view rewrite mechanism. Each test has a query and one or more
 * materializations (what Oracle calls materialized views) and checks that the materialization is
 * used.
 */
public class MaterializationTest {
  private static final Function<ResultSet, Void> CONTAINS_M0 =
      CalciteAssert.checkResultContains("EnumerableTableScan(table=[[hr, m0]])");

  final JavaTypeFactoryImpl typeFactory = new JavaTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
  final RexBuilder rexBuilder = new RexBuilder(typeFactory);

  @Test
  public void testFilter() {
    CalciteAssert.that()
        .withMaterializations(
            JdbcTest.HR_MODEL, "m0", "select * from \"emps\" where \"deptno\" = 10")
        .query("select \"empid\" + 1 from \"emps\" where \"deptno\" = 10")
        .enableMaterializations(true)
        .explainContains("EnumerableTableScan(table=[[hr, m0]])")
        .sameResultWithMaterializationsDisabled();
  }

  @Test
  public void testFilterQueryOnProjectView() {
    try {
      Prepare.THREAD_TRIM.set(true);
      MaterializationService.setThreadLocal();
      CalciteAssert.that()
          .withMaterializations(
              JdbcTest.HR_MODEL, "m0", "select \"deptno\", \"empid\" from \"emps\"")
          .query("select \"empid\" + 1 as x from \"emps\" where \"deptno\" = 10")
          .enableMaterializations(true)
          .explainContains("EnumerableTableScan(table=[[hr, m0]])")
          .sameResultWithMaterializationsDisabled();
    } finally {
      Prepare.THREAD_TRIM.set(false);
    }
  }

  /** Checks that a given query can use a materialized view with a given definition. */
  private void checkMaterialize(String materialize, String query) {
    checkMaterialize(materialize, query, JdbcTest.HR_MODEL, CONTAINS_M0);
  }

  /** Checks that a given query can use a materialized view with a given definition. */
  private void checkMaterialize(
      String materialize, String query, String model, Function<ResultSet, Void> explainChecker) {
    try {
      Prepare.THREAD_TRIM.set(true);
      MaterializationService.setThreadLocal();
      CalciteAssert.that()
          .withMaterializations(model, "m0", materialize)
          .query(query)
          .enableMaterializations(true)
          .explainMatches("", explainChecker)
          .sameResultWithMaterializationsDisabled();
    } finally {
      Prepare.THREAD_TRIM.set(false);
    }
  }

  /** Checks that a given query CAN NOT use a materialized view with a given definition. */
  private void checkNoMaterialize(String materialize, String query, String model) {
    try {
      Prepare.THREAD_TRIM.set(true);
      MaterializationService.setThreadLocal();
      CalciteAssert.that()
          .withMaterializations(model, "m0", materialize)
          .query(query)
          .enableMaterializations(true)
          .explainContains("EnumerableTableScan(table=[[hr, emps]])");
    } finally {
      Prepare.THREAD_TRIM.set(false);
    }
  }

  /** Runs the same test as {@link #testFilterQueryOnProjectView()} but more concisely. */
  @Test
  public void testFilterQueryOnProjectView0() {
    checkMaterialize(
        "select \"deptno\", \"empid\" from \"emps\"",
        "select \"empid\" + 1 as x from \"emps\" where \"deptno\" = 10");
  }

  /** As {@link #testFilterQueryOnProjectView()} but with extra column in materialized view. */
  @Test
  public void testFilterQueryOnProjectView1() {
    checkMaterialize(
        "select \"deptno\", \"empid\", \"name\" from \"emps\"",
        "select \"empid\" + 1 as x from \"emps\" where \"deptno\" = 10");
  }

  /**
   * As {@link #testFilterQueryOnProjectView()} but with extra column in both materialized view and
   * query.
   */
  @Test
  public void testFilterQueryOnProjectView2() {
    checkMaterialize(
        "select \"deptno\", \"empid\", \"name\" from \"emps\"",
        "select \"empid\" + 1 as x, \"name\" from \"emps\" where \"deptno\" = 10");
  }

  @Test
  public void testFilterQueryOnProjectView3() {
    checkMaterialize(
        "select \"deptno\" - 10 as \"x\", \"empid\" + 1, \"name\" from \"emps\"",
        "select \"name\" from \"emps\" where \"deptno\" - 10 = 0");
  }

  /**
   * As {@link #testFilterQueryOnProjectView3()} but materialized view cannot be used because it
   * does not contain required expression.
   */
  @Test
  public void testFilterQueryOnProjectView4() {
    checkNoMaterialize(
        "select \"deptno\" - 10 as \"x\", \"empid\" + 1, \"name\" from \"emps\"",
        "select \"name\" from \"emps\" where \"deptno\" + 10 = 20",
        JdbcTest.HR_MODEL);
  }

  /** As {@link #testFilterQueryOnProjectView3()} but also contains an expression column. */
  @Test
  public void testFilterQueryOnProjectView5() {
    checkMaterialize(
        "select \"deptno\" - 10 as \"x\", \"empid\" + 1 as ee, \"name\"\n" + "from \"emps\"",
        "select \"name\", \"empid\" + 1 as e\n" + "from \"emps\" where \"deptno\" - 10 = 2",
        JdbcTest.HR_MODEL,
        CalciteAssert.checkResultContains(
            "EnumerableCalc(expr#0..2=[{inputs}], expr#3=[2], expr#4=[=($t0, $t3)], name=[$t2], E=[$t1], $condition=[$t4])\n"
                + "  EnumerableTableScan(table=[[hr, m0]]"));
  }

  /** Cannot materialize because "name" is not projected in the MV. */
  @Test
  public void testFilterQueryOnProjectView6() {
    checkNoMaterialize(
        "select \"deptno\" - 10 as \"x\", \"empid\"  from \"emps\"",
        "select \"name\" from \"emps\" where \"deptno\" - 10 = 0",
        JdbcTest.HR_MODEL);
  }

  /** As {@link #testFilterQueryOnProjectView3()} but also contains an expression column. */
  @Test
  public void testFilterQueryOnProjectView7() {
    checkNoMaterialize(
        "select \"deptno\" - 10 as \"x\", \"empid\" + 1, \"name\" from \"emps\"",
        "select \"name\", \"empid\" + 2 from \"emps\" where \"deptno\" - 10 = 0",
        JdbcTest.HR_MODEL);
  }

  @Test
  public void testFilterQueryOnFilterView() {
    checkMaterialize(
        "select \"deptno\", \"empid\", \"name\" from \"emps\" where \"deptno\" = 10",
        "select \"empid\" + 1 as x, \"name\" from \"emps\" where \"deptno\" = 10");
  }

  /** As {@link #testFilterQueryOnFilterView()} but condition is stronger in query. */
  @Ignore
  @Test
  public void testFilterQueryOnFilterView2() {
    checkMaterialize(
        "select \"deptno\", \"empid\", \"name\" from \"emps\" where \"deptno\" = 10",
        "select \"empid\" + 1 as x, \"name\" from \"emps\" where \"deptno\" = 10 and \"empid\" < 150");
  }

  /** As {@link #testFilterQueryOnFilterView()} but condition is weaker in view. */
  @Ignore("not implemented")
  @Test
  public void testFilterQueryOnFilterView3() {
    checkMaterialize(
        "select \"deptno\", \"empid\", \"name\" from \"emps\" where \"deptno\" = 10 or \"deptno\" = 20 or \"empid\" < 160",
        "select \"empid\" + 1 as x, \"name\" from \"emps\" where \"deptno\" = 10",
        JdbcTest.HR_MODEL,
        CalciteAssert.checkResultContains(
            "EnumerableCalcRel(expr#0..2=[{inputs}], expr#3=[1], expr#4=[+($t1, $t3)], X=[$t4], name=[$t2], condition=?)\n"
                + "  EnumerableTableScan(table=[[hr, m0]])"));
  }

  /** Aggregation query at same level of aggregation as aggregation materialization. */
  @Test
  public void testAggregate() {
    checkMaterialize(
        "select \"deptno\", count(*) as c, sum(\"empid\") as s from \"emps\" group by \"deptno\"",
        "select count(*) + 1 as c, \"deptno\" from \"emps\" group by \"deptno\"");
  }

  /**
   * Aggregation query at coarser level of aggregation than aggregation materialization. Requires an
   * additional aggregate to roll up. Note that COUNT is rolled up using SUM.
   */
  @Test
  public void testAggregateRollUp() {
    checkMaterialize(
        "select \"empid\", \"deptno\", count(*) as c, sum(\"empid\") as s from \"emps\" group by \"empid\", \"deptno\"",
        "select count(*) + 1 as c, \"deptno\" from \"emps\" group by \"deptno\"",
        JdbcTest.HR_MODEL,
        CalciteAssert.checkResultContains(
            "EnumerableCalc(expr#0..1=[{inputs}], expr#2=[1], expr#3=[+($t1, $t2)], C=[$t3], deptno=[$t0])\n"
                + "  EnumerableAggregate(group=[{1}], agg#0=[$SUM0($2)])\n"
                + "    EnumerableTableScan(table=[[hr, m0]])"));
  }

  /** Aggregation materialization with a project. */
  @Ignore("work in progress")
  @Test
  public void testAggregateProject() {
    // Note that materialization does not start with the GROUP BY columns.
    // Not a smart way to design a materialization, but people may do it.
    checkMaterialize(
        "select \"deptno\", count(*) as c, \"empid\" + 2, sum(\"empid\") as s from \"emps\" group by \"empid\", \"deptno\"",
        "select count(*) + 1 as c, \"deptno\" from \"emps\" group by \"deptno\"",
        JdbcTest.HR_MODEL,
        CalciteAssert.checkResultContains("xxx"));
  }

  @Ignore
  @Test
  public void testSwapJoin() {
    String q1 =
        "select count(*) as c from \"foodmart\".\"sales_fact_1997\" as s join \"foodmart\".\"time_by_day\" as t on s.\"time_id\" = t.\"time_id\"";
    String q2 =
        "select count(*) as c from \"foodmart\".\"time_by_day\" as t join \"foodmart\".\"sales_fact_1997\" as s on t.\"time_id\" = s.\"time_id\"";
  }

  @Ignore
  @Test
  public void testDifferentColumnNames() {}

  @Ignore
  @Test
  public void testDifferentType() {}

  @Ignore
  @Test
  public void testPartialUnion() {}

  @Ignore
  @Test
  public void testNonDisjointUnion() {}

  @Ignore
  @Test
  public void testMaterializationReferencesTableInOtherSchema() {}

  /**
   * Unit test for logic functions {@link
   * org.apache.calcite.plan.SubstitutionVisitor#mayBeSatisfiable} and {@link
   * org.apache.calcite.plan.SubstitutionVisitor#simplify}.
   */
  @Test
  public void testSatisfiable() {
    // TRUE may be satisfiable
    checkSatisfiable(rexBuilder.makeLiteral(true), "true");

    // FALSE is not satisfiable
    checkNotSatisfiable(rexBuilder.makeLiteral(false));

    // The expression "$0 = 1".
    final RexNode i0_eq_0 =
        rexBuilder.makeCall(
            SqlStdOperatorTable.EQUALS,
            rexBuilder.makeInputRef(typeFactory.createType(int.class), 0),
            rexBuilder.makeExactLiteral(BigDecimal.ZERO));

    // "$0 = 1" may be satisfiable
    checkSatisfiable(i0_eq_0, "=($0, 0)");

    // "$0 = 1 AND TRUE" may be satisfiable
    final RexNode e0 =
        rexBuilder.makeCall(SqlStdOperatorTable.AND, i0_eq_0, rexBuilder.makeLiteral(true));
    checkSatisfiable(e0, "=($0, 0)");

    // "$0 = 1 AND FALSE" is not satisfiable
    final RexNode e1 =
        rexBuilder.makeCall(SqlStdOperatorTable.AND, i0_eq_0, rexBuilder.makeLiteral(false));
    checkNotSatisfiable(e1);

    // "$0 = 0 AND NOT $0 = 0" is not satisfiable
    final RexNode e2 =
        rexBuilder.makeCall(
            SqlStdOperatorTable.AND,
            i0_eq_0,
            rexBuilder.makeCall(SqlStdOperatorTable.NOT, i0_eq_0));
    checkNotSatisfiable(e2);

    // "TRUE AND NOT $0 = 0" may be satisfiable. Can simplify.
    final RexNode e3 =
        rexBuilder.makeCall(
            SqlStdOperatorTable.AND,
            rexBuilder.makeLiteral(true),
            rexBuilder.makeCall(SqlStdOperatorTable.NOT, i0_eq_0));
    checkSatisfiable(e3, "NOT(=($0, 0))");

    // The expression "$1 = 1".
    final RexNode i1_eq_1 =
        rexBuilder.makeCall(
            SqlStdOperatorTable.EQUALS,
            rexBuilder.makeInputRef(typeFactory.createType(int.class), 1),
            rexBuilder.makeExactLiteral(BigDecimal.ONE));

    // "$0 = 0 AND $1 = 1 AND NOT $0 = 0" is not satisfiable
    final RexNode e4 =
        rexBuilder.makeCall(
            SqlStdOperatorTable.AND,
            i0_eq_0,
            rexBuilder.makeCall(
                SqlStdOperatorTable.AND,
                i1_eq_1,
                rexBuilder.makeCall(SqlStdOperatorTable.NOT, i0_eq_0)));
    checkNotSatisfiable(e4);

    // "$0 = 0 AND NOT $1 = 1" may be satisfiable. Can't simplify.
    final RexNode e5 =
        rexBuilder.makeCall(
            SqlStdOperatorTable.AND,
            i0_eq_0,
            rexBuilder.makeCall(SqlStdOperatorTable.NOT, i1_eq_1));
    checkSatisfiable(e5, "AND(=($0, 0), NOT(=($1, 1)))");

    // "$0 = 0 AND NOT ($0 = 0 AND $1 = 1)" may be satisfiable. Can simplify.
    final RexNode e6 =
        rexBuilder.makeCall(
            SqlStdOperatorTable.AND,
            i0_eq_0,
            rexBuilder.makeCall(
                SqlStdOperatorTable.NOT,
                rexBuilder.makeCall(SqlStdOperatorTable.AND, i0_eq_0, i1_eq_1)));
    checkSatisfiable(e6, "AND(=($0, 0), NOT(AND(=($0, 0), =($1, 1))))");

    // "$0 = 0 AND ($1 = 1 AND NOT ($0 = 0))" is not satisfiable.
    final RexNode e7 =
        rexBuilder.makeCall(
            SqlStdOperatorTable.AND,
            i0_eq_0,
            rexBuilder.makeCall(
                SqlStdOperatorTable.AND,
                i1_eq_1,
                rexBuilder.makeCall(SqlStdOperatorTable.NOT, i0_eq_0)));
    checkNotSatisfiable(e7);

    // The expression "$2".
    final RexInputRef i2 = rexBuilder.makeInputRef(typeFactory.createType(boolean.class), 2);

    // The expression "$3".
    final RexInputRef i3 = rexBuilder.makeInputRef(typeFactory.createType(boolean.class), 3);

    // The expression "$4".
    final RexInputRef i4 = rexBuilder.makeInputRef(typeFactory.createType(boolean.class), 4);

    // "$0 = 0 AND $2 AND $3 AND NOT ($2 AND $3 AND $4) AND NOT ($2 AND $4)" may
    // be satisfiable. Can't simplify.
    final RexNode e8 =
        rexBuilder.makeCall(
            SqlStdOperatorTable.AND,
            i0_eq_0,
            rexBuilder.makeCall(
                SqlStdOperatorTable.AND,
                i2,
                rexBuilder.makeCall(
                    SqlStdOperatorTable.AND,
                    i3,
                    rexBuilder.makeCall(
                        SqlStdOperatorTable.NOT,
                        rexBuilder.makeCall(SqlStdOperatorTable.AND, i2, i3, i4)),
                    rexBuilder.makeCall(SqlStdOperatorTable.NOT, i4))));
    checkSatisfiable(e8, "AND(=($0, 0), $2, $3, NOT(AND($2, $3, $4)), NOT($4))");
  }

  private void checkNotSatisfiable(RexNode e) {
    assertFalse(SubstitutionVisitor.mayBeSatisfiable(e));
    final RexNode simple = SubstitutionVisitor.simplify(rexBuilder, e);
    assertFalse(RexLiteral.booleanValue(simple));
  }

  private void checkSatisfiable(RexNode e, String s) {
    assertTrue(SubstitutionVisitor.mayBeSatisfiable(e));
    final RexNode simple = SubstitutionVisitor.simplify(rexBuilder, e);
    assertEquals(s, simple.toString());
  }

  @Test
  public void testSplitFilter() {
    final RexLiteral i1 = rexBuilder.makeExactLiteral(BigDecimal.ONE);
    final RexLiteral i2 = rexBuilder.makeExactLiteral(BigDecimal.valueOf(2));
    final RexLiteral i3 = rexBuilder.makeExactLiteral(BigDecimal.valueOf(3));

    final RelDataType intType = typeFactory.createType(int.class);
    final RexInputRef x = rexBuilder.makeInputRef(intType, 0); // $0
    final RexInputRef y = rexBuilder.makeInputRef(intType, 1); // $1
    final RexInputRef z = rexBuilder.makeInputRef(intType, 2); // $2

    final RexNode x_eq_1 = rexBuilder.makeCall(SqlStdOperatorTable.EQUALS, x, i1); // $0 = 1
    final RexNode x_eq_1_b = rexBuilder.makeCall(SqlStdOperatorTable.EQUALS, x, i1); // $0 = 1 again
    final RexNode y_eq_2 = rexBuilder.makeCall(SqlStdOperatorTable.EQUALS, y, i2); // $1 = 2
    final RexNode z_eq_3 = rexBuilder.makeCall(SqlStdOperatorTable.EQUALS, z, i3); // $2 = 3

    RexNode newFilter;

    // Example 1.
    // TODO:

    // Example 2.
    //   condition: x = 1,
    //   target:    x = 1 or z = 3
    // yields
    //   residue:   not (z = 3)
    newFilter =
        SubstitutionVisitor.splitFilter(
            rexBuilder, x_eq_1, rexBuilder.makeCall(SqlStdOperatorTable.OR, x_eq_1, z_eq_3));
    assertThat(newFilter.toString(), equalTo("NOT(=($2, 3))"));

    // 2b.
    //   condition: x = 1 or y = 2
    //   target:    x = 1 or y = 2 or z = 3
    // yields
    //   residue:   not (z = 3)
    newFilter =
        SubstitutionVisitor.splitFilter(
            rexBuilder,
            rexBuilder.makeCall(SqlStdOperatorTable.OR, x_eq_1, y_eq_2),
            rexBuilder.makeCall(SqlStdOperatorTable.OR, x_eq_1, y_eq_2, z_eq_3));
    assertThat(newFilter.toString(), equalTo("NOT(=($2, 3))"));

    // 2c.
    //   condition: x = 1
    //   target:    x = 1 or y = 2 or z = 3
    // yields
    //   residue:   not (y = 2) and not (z = 3)
    newFilter =
        SubstitutionVisitor.splitFilter(
            rexBuilder,
            x_eq_1,
            rexBuilder.makeCall(SqlStdOperatorTable.OR, x_eq_1, y_eq_2, z_eq_3));
    assertThat(newFilter.toString(), equalTo("AND(NOT(=($1, 2)), NOT(=($2, 3)))"));

    // 2d.
    //   condition: x = 1 or y = 2
    //   target:    y = 2 or x = 1
    // yields
    //   residue:   true
    newFilter =
        SubstitutionVisitor.splitFilter(
            rexBuilder,
            rexBuilder.makeCall(SqlStdOperatorTable.OR, x_eq_1, y_eq_2),
            rexBuilder.makeCall(SqlStdOperatorTable.OR, y_eq_2, x_eq_1));
    assertThat(newFilter.isAlwaysTrue(), equalTo(true));

    // 2e.
    //   condition: x = 1
    //   target:    x = 1 (different object)
    // yields
    //   residue:   true
    newFilter = SubstitutionVisitor.splitFilter(rexBuilder, x_eq_1, x_eq_1_b);
    assertThat(newFilter.isAlwaysTrue(), equalTo(true));

    // 2f.
    //   condition: x = 1 or y = 2
    //   target:    x = 1
    // yields
    //   residue:   null
    // TODO:

    // Example 3.
    // Condition [x = 1 and y = 2],
    // target [y = 2 and x = 1] yields
    // residue [true].
    // TODO:

    // Example 4.
    // TODO:
  }

  /**
   * Tests a complicated star-join query on a complicated materialized star-join query. Some of the
   * features:
   *
   * <p>1. query joins in different order; 2. query's join conditions are in where clause; 3. query
   * does not use all join tables (safe to omit them because they are many-to-mandatory-one joins);
   * 4. query is at higher granularity, therefore needs to roll up; 5. query has a condition on one
   * of the materialization's grouping columns.
   */
  @Ignore
  @Test
  public void testFilterGroupQueryOnStar() {
    checkMaterialize(
        "select p.\"product_name\", t.\"the_year\",\n"
            + "  sum(f.\"unit_sales\") as \"sum_unit_sales\", count(*) as \"c\"\n"
            + "from \"foodmart\".\"sales_fact_1997\" as f\n"
            + "join (\n"
            + "    select \"time_id\", \"the_year\", \"the_month\"\n"
            + "    from \"foodmart\".\"time_by_day\") as t\n"
            + "  on f.\"time_id\" = t.\"time_id\"\n"
            + "join \"foodmart\".\"product\" as p\n"
            + "  on f.\"product_id\" = p.\"product_id\"\n"
            + "join \"foodmart\".\"product_class\" as pc"
            + "  on p.\"product_class_id\" = pc.\"product_class_id\"\n"
            + "group by t.\"the_year\",\n"
            + " t.\"the_month\",\n"
            + " pc.\"product_department\",\n"
            + " pc.\"product_category\",\n"
            + " p.\"product_name\"",
        "select t.\"the_month\", count(*) as x\n"
            + "from (\n"
            + "  select \"time_id\", \"the_year\", \"the_month\"\n"
            + "  from \"foodmart\".\"time_by_day\") as t,\n"
            + " \"foodmart\".\"sales_fact_1997\" as f\n"
            + "where t.\"the_year\" = 1997\n"
            + "and t.\"time_id\" = f.\"time_id\"\n"
            + "group by t.\"the_year\",\n"
            + " t.\"the_month\"\n",
        JdbcTest.FOODMART_MODEL,
        CONTAINS_M0);
  }

  /**
   * Simpler than {@link #testFilterGroupQueryOnStar()}, tests a query on a materialization that is
   * just a join.
   */
  @Ignore
  @Test
  public void testQueryOnStar() {
    String q =
        "select *\n"
            + "from \"foodmart\".\"sales_fact_1997\" as f\n"
            + "join \"foodmart\".\"time_by_day\" as t on f.\"time_id\" = t.\"time_id\"\n"
            + "join \"foodmart\".\"product\" as p on f.\"product_id\" = p.\"product_id\"\n"
            + "join \"foodmart\".\"product_class\" as pc on p.\"product_class_id\" = pc.\"product_class_id\"\n";
    checkMaterialize(q, q + "where t.\"month_of_year\" = 10", JdbcTest.FOODMART_MODEL, CONTAINS_M0);
  }

  /**
   * A materialization that is a join of a union cannot at present be converted to a star table and
   * therefore cannot be recognized. This test checks that nothing unpleasant happens.
   */
  @Ignore
  @Test
  public void testJoinOnUnionMaterialization() {
    String q =
        "select *\n"
            + "from (select * from \"emps\" union all select * from \"emps\")\n"
            + "join \"depts\" using (\"deptno\")";
    checkNoMaterialize(q, q, JdbcTest.HR_MODEL);
  }
}