You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by vl...@apache.org on 2018/08/02 09:12:57 UTC

[3/3] calcite git commit: [CALCITE-2435] tests: refactor SqlTestFactory

[CALCITE-2435] tests: refactor SqlTestFactory

closes #771


Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/96b28f7b
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/96b28f7b
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/96b28f7b

Branch: refs/heads/master
Commit: 96b28f7ba11de22a68d984de6b6b88311cc2c57e
Parents: c113765
Author: Vladimir Sitnikov <si...@gmail.com>
Authored: Wed Aug 1 13:40:56 2018 +0300
Committer: Vladimir Sitnikov <si...@gmail.com>
Committed: Thu Aug 2 12:10:12 2018 +0300

----------------------------------------------------------------------
 .../calcite/sql/test/DefaultSqlTestFactory.java |  159 --
 .../sql/test/DelegatingSqlTestFactory.java      |   73 -
 .../apache/calcite/sql/test/SqlAdvisorTest.java |   48 +-
 .../calcite/sql/test/SqlOperatorBaseTest.java   |   14 +-
 .../apache/calcite/sql/test/SqlTestFactory.java |  181 +-
 .../apache/calcite/sql/test/SqlTesterImpl.java  |   16 +-
 .../sql/validate/SqlValidatorUtilTest.java      |    4 +-
 .../apache/calcite/test/MockCatalogReader.java  | 1750 ------------------
 .../apache/calcite/test/RelOptRulesTest.java    |    1 +
 .../org/apache/calcite/test/SqlTestGen.java     |   20 +-
 .../calcite/test/SqlToRelConverterTest.java     |    3 +-
 .../apache/calcite/test/SqlToRelTestBase.java   |    4 +-
 .../calcite/test/SqlValidatorFeatureTest.java   |   28 +-
 .../apache/calcite/test/SqlValidatorTest.java   |   11 +-
 .../calcite/test/SqlValidatorTestCase.java      |   13 +-
 .../test/catalog/CompoundNameColumn.java        |   39 +
 .../catalog/CompoundNameColumnResolver.java     |  150 ++
 .../calcite/test/catalog/CountingFactory.java   |   82 +
 .../EmpInitializerExpressionFactory.java        |   64 +
 .../apache/calcite/test/catalog/Fixture.java    |  125 ++
 .../calcite/test/catalog/MockCatalogReader.java | 1006 ++++++++++
 .../test/catalog/MockCatalogReaderExtended.java |  120 ++
 .../test/catalog/MockCatalogReaderSimple.java   |  422 +++++
 23 files changed, 2218 insertions(+), 2115 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/96b28f7b/core/src/test/java/org/apache/calcite/sql/test/DefaultSqlTestFactory.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/sql/test/DefaultSqlTestFactory.java b/core/src/test/java/org/apache/calcite/sql/test/DefaultSqlTestFactory.java
deleted file mode 100644
index e2667d5..0000000
--- a/core/src/test/java/org/apache/calcite/sql/test/DefaultSqlTestFactory.java
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to you under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.calcite.sql.test;
-
-import org.apache.calcite.adapter.java.JavaTypeFactory;
-import org.apache.calcite.avatica.util.Casing;
-import org.apache.calcite.avatica.util.Quoting;
-import org.apache.calcite.jdbc.JavaTypeFactoryImpl;
-import org.apache.calcite.rel.type.DelegatingTypeSystem;
-import org.apache.calcite.rel.type.RelDataTypeSystem;
-import org.apache.calcite.sql.SqlOperatorTable;
-import org.apache.calcite.sql.advise.SqlAdvisor;
-import org.apache.calcite.sql.fun.SqlStdOperatorTable;
-import org.apache.calcite.sql.parser.SqlParser;
-import org.apache.calcite.sql.validate.SqlConformance;
-import org.apache.calcite.sql.validate.SqlConformanceEnum;
-import org.apache.calcite.sql.validate.SqlValidator;
-import org.apache.calcite.sql.validate.SqlValidatorUtil;
-import org.apache.calcite.sql.validate.SqlValidatorWithHints;
-import org.apache.calcite.test.CalciteAssert;
-import org.apache.calcite.test.MockCatalogReader;
-import org.apache.calcite.test.MockSqlOperatorTable;
-
-import com.google.common.cache.CacheBuilder;
-import com.google.common.cache.CacheLoader;
-import com.google.common.cache.LoadingCache;
-import com.google.common.collect.ImmutableMap;
-
-import javax.annotation.Nonnull;
-
-/**
- * Default implementation of {@link SqlTestFactory}.
- *
- * <p>Suitable for most tests. If you want different behavior, you can extend;
- * if you want a factory with different properties (e.g. SQL conformance level
- * or identifier quoting), wrap in a
- * {@link DelegatingSqlTestFactory} and
- * override {@link #get}.</p>
-*/
-public class DefaultSqlTestFactory implements SqlTestFactory {
-  public static final ImmutableMap<String, Object> DEFAULT_OPTIONS =
-      ImmutableMap.<String, Object>builder()
-          .put("quoting", Quoting.DOUBLE_QUOTE)
-          .put("quotedCasing", Casing.UNCHANGED)
-          .put("unquotedCasing", Casing.TO_UPPER)
-          .put("caseSensitive", true)
-          .put("conformance", SqlConformanceEnum.DEFAULT)
-          .put("operatorTable", SqlStdOperatorTable.instance())
-          .put("connectionFactory",
-              CalciteAssert.EMPTY_CONNECTION_FACTORY
-                  .with(
-                      new CalciteAssert.AddSchemaSpecPostProcessor(
-                          CalciteAssert.SchemaSpec.HR)))
-          .build();
-
-  /** Caches the mock catalog.
-   * Due to view parsing, initializing a mock catalog is quite expensive.
-   * Validator is not re-entrant, so we create a new one for each test.
-   * Caching improves SqlValidatorTest from 23s to 8s,
-   * and CalciteSqlOperatorTest from 65s to 43s. */
-  private final LoadingCache<SqlTestFactory, Xyz> cache =
-      CacheBuilder.newBuilder().build(CacheLoader.from(Xyz::from));
-
-  public static final DefaultSqlTestFactory INSTANCE =
-      new DefaultSqlTestFactory();
-
-  private DefaultSqlTestFactory() {
-  }
-
-  public MockCatalogReader createCatalogReader(SqlTestFactory testFactory,
-      JavaTypeFactory typeFactory) {
-    final boolean caseSensitive = (Boolean) testFactory.get("caseSensitive");
-    return new MockCatalogReader(typeFactory, caseSensitive).init();
-  }
-
-  public SqlOperatorTable createOperatorTable(SqlTestFactory factory) {
-    final SqlOperatorTable opTab0 =
-        (SqlOperatorTable) factory.get("operatorTable");
-    MockSqlOperatorTable opTab = new MockSqlOperatorTable(opTab0);
-    MockSqlOperatorTable.addRamp(opTab);
-    return opTab;
-  }
-
-  public SqlParser createParser(SqlTestFactory factory, String sql) {
-    return SqlParser.create(sql,
-        SqlParser.configBuilder()
-            .setQuoting((Quoting) factory.get("quoting"))
-            .setUnquotedCasing((Casing) factory.get("unquotedCasing"))
-            .setQuotedCasing((Casing) factory.get("quotedCasing"))
-            .setConformance((SqlConformance) factory.get("conformance"))
-            .build());
-  }
-
-  public SqlValidator getValidator(SqlTestFactory factory) {
-    final Xyz xyz = cache.getUnchecked(factory);
-    final SqlConformance conformance =
-        (SqlConformance) factory.get("conformance");
-    return SqlValidatorUtil.newValidator(xyz.operatorTable,
-        xyz.catalogReader, xyz.typeFactory, conformance);
-  }
-
-  public SqlAdvisor createAdvisor(SqlValidatorWithHints validator) {
-    throw new UnsupportedOperationException();
-  }
-
-  public Object get(String name) {
-    return DEFAULT_OPTIONS.get(name);
-  }
-
-  /** State that can be cached and shared among tests. */
-  private static class Xyz {
-    private final SqlOperatorTable operatorTable;
-    private final JavaTypeFactory typeFactory;
-    private final MockCatalogReader catalogReader;
-
-    Xyz(SqlOperatorTable operatorTable, JavaTypeFactory typeFactory,
-        MockCatalogReader catalogReader) {
-      this.operatorTable = operatorTable;
-      this.typeFactory = typeFactory;
-      this.catalogReader = catalogReader;
-    }
-
-    static Xyz from(@Nonnull SqlTestFactory factory) {
-      final SqlOperatorTable operatorTable =
-          factory.createOperatorTable(factory);
-      RelDataTypeSystem typeSystem = RelDataTypeSystem.DEFAULT;
-      final SqlConformance conformance =
-          (SqlConformance) factory.get("conformance");
-      if (conformance.shouldConvertRaggedUnionTypesToVarying()) {
-        typeSystem = new DelegatingTypeSystem(typeSystem) {
-          public boolean shouldConvertRaggedUnionTypesToVarying() {
-            return true;
-          }
-        };
-      }
-      final JavaTypeFactory typeFactory =
-          new JavaTypeFactoryImpl(typeSystem);
-      final MockCatalogReader catalogReader =
-          factory.createCatalogReader(factory, typeFactory);
-      return new Xyz(operatorTable, typeFactory, catalogReader);
-    }
-  }
-}
-
-// End DefaultSqlTestFactory.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/96b28f7b/core/src/test/java/org/apache/calcite/sql/test/DelegatingSqlTestFactory.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/sql/test/DelegatingSqlTestFactory.java b/core/src/test/java/org/apache/calcite/sql/test/DelegatingSqlTestFactory.java
deleted file mode 100644
index cfd628f..0000000
--- a/core/src/test/java/org/apache/calcite/sql/test/DelegatingSqlTestFactory.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to you under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.calcite.sql.test;
-
-import org.apache.calcite.adapter.java.JavaTypeFactory;
-import org.apache.calcite.sql.SqlOperatorTable;
-import org.apache.calcite.sql.advise.SqlAdvisor;
-import org.apache.calcite.sql.parser.SqlParser;
-import org.apache.calcite.sql.validate.SqlValidator;
-import org.apache.calcite.sql.validate.SqlValidatorWithHints;
-import org.apache.calcite.test.MockCatalogReader;
-
-/**
-* Implementation of {@link SqlTestFactory} that delegates
- * everything to an underlying factory.
- *
- * <p>Generally a chain starts with a
- * {@link org.apache.calcite.sql.test.DefaultSqlTestFactory}, and continues with
- * a succession of objects that derive from {@code DelegatingSqlTestFactory} and
- * override one method.</p>
- *
- * <p>Methods such as
- * {@link org.apache.calcite.sql.test.SqlTester#withConformance} help create
- * such chains.</p>
-*/
-public class DelegatingSqlTestFactory implements SqlTestFactory {
-  private final SqlTestFactory factory;
-
-  public DelegatingSqlTestFactory(SqlTestFactory factory) {
-    this.factory = factory;
-  }
-
-  public Object get(String name) {
-    return factory.get(name);
-  }
-
-  public MockCatalogReader createCatalogReader(SqlTestFactory factory,
-      JavaTypeFactory typeFactory) {
-    return this.factory.createCatalogReader(factory, typeFactory);
-  }
-
-  public SqlOperatorTable createOperatorTable(SqlTestFactory factory) {
-    return this.factory.createOperatorTable(factory);
-  }
-
-  public SqlAdvisor createAdvisor(SqlValidatorWithHints validator) {
-    return factory.createAdvisor(validator);
-  }
-
-  public SqlValidator getValidator(SqlTestFactory factory) {
-    return this.factory.getValidator(factory);
-  }
-
-  public SqlParser createParser(SqlTestFactory factory, String sql) {
-    return this.factory.createParser(factory, sql);
-  }
-}
-
-// End DelegatingSqlTestFactory.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/96b28f7b/core/src/test/java/org/apache/calcite/sql/test/SqlAdvisorTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/sql/test/SqlAdvisorTest.java b/core/src/test/java/org/apache/calcite/sql/test/SqlAdvisorTest.java
index c8476be..0742731 100644
--- a/core/src/test/java/org/apache/calcite/sql/test/SqlAdvisorTest.java
+++ b/core/src/test/java/org/apache/calcite/sql/test/SqlAdvisorTest.java
@@ -16,20 +16,12 @@
  */
 package org.apache.calcite.sql.test;
 
-import org.apache.calcite.rel.type.RelDataTypeFactory;
-import org.apache.calcite.rel.type.RelDataTypeSystem;
 import org.apache.calcite.sql.advise.SqlAdvisor;
 import org.apache.calcite.sql.advise.SqlAdvisorValidator;
 import org.apache.calcite.sql.advise.SqlSimpleParser;
-import org.apache.calcite.sql.fun.SqlStdOperatorTable;
 import org.apache.calcite.sql.parser.SqlParserUtil;
-import org.apache.calcite.sql.type.SqlTypeFactoryImpl;
-import org.apache.calcite.sql.validate.SqlConformance;
 import org.apache.calcite.sql.validate.SqlMoniker;
 import org.apache.calcite.sql.validate.SqlMonikerType;
-import org.apache.calcite.sql.validate.SqlValidator;
-import org.apache.calcite.sql.validate.SqlValidatorWithHints;
-import org.apache.calcite.test.MockCatalogReader;
 import org.apache.calcite.test.SqlValidatorTestCase;
 
 import org.junit.Assert;
@@ -52,6 +44,9 @@ import static org.junit.Assert.fail;
  * for SqlAdvisor.
  */
 public class SqlAdvisorTest extends SqlValidatorTestCase {
+  public static final SqlTestFactory ADVISOR_TEST_FACTORY = SqlTestFactory.INSTANCE.withValidator(
+      SqlAdvisorValidator::new);
+
   //~ Static fields/initializers ---------------------------------------------
 
   private static final List<String> STAR_KEYWORD =
@@ -413,9 +408,7 @@ public class SqlAdvisorTest extends SqlValidatorTestCase {
   protected void assertHint(
       String sql,
       String expectedResults) throws Exception {
-    SqlValidatorWithHints validator =
-        (SqlValidatorWithHints) tester.getValidator();
-    SqlAdvisor advisor = tester.getFactory().createAdvisor(validator);
+    SqlAdvisor advisor = tester.getFactory().createAdvisor();
 
     SqlParserUtil.StringAndPos sap = SqlParserUtil.findPos(sql);
 
@@ -436,9 +429,7 @@ public class SqlAdvisorTest extends SqlValidatorTestCase {
    * @param expected Expected result after simplification.
    */
   protected void assertSimplify(String sql, String expected) {
-    SqlValidatorWithHints validator =
-        (SqlValidatorWithHints) tester.getValidator();
-    SqlAdvisor advisor = tester.getFactory().createAdvisor(validator);
+    SqlAdvisor advisor = tester.getFactory().createAdvisor();
 
     SqlParserUtil.StringAndPos sap = SqlParserUtil.findPos(sql);
     String actual = advisor.simplifySql(sap.sql, sap.cursor);
@@ -467,9 +458,7 @@ public class SqlAdvisorTest extends SqlValidatorTestCase {
       String sql,
       String expectedResults,
       String expectedWord) {
-    SqlValidatorWithHints validator =
-        (SqlValidatorWithHints) tester.getValidator();
-    SqlAdvisor advisor = tester.getFactory().createAdvisor(validator);
+    SqlAdvisor advisor = tester.getFactory().createAdvisor();
 
     SqlParserUtil.StringAndPos sap = SqlParserUtil.findPos(sql);
     final String[] replaced = {null};
@@ -527,7 +516,7 @@ public class SqlAdvisorTest extends SqlValidatorTestCase {
   }
 
   @Override public SqlTester getTester() {
-    return new SqlTesterImpl(new AdvisorTesterFactory());
+    return new SqlTesterImpl(ADVISOR_TEST_FACTORY);
   }
 
   /**
@@ -1234,29 +1223,6 @@ public class SqlAdvisorTest extends SqlValidatorTestCase {
     simplified = "SELECT * FROM emp GROUP BY _suggest_";
     assertSimplify(sql, simplified);
   }
-
-  /** Factory that creates testers. */
-  private static class AdvisorTesterFactory extends DelegatingSqlTestFactory {
-    AdvisorTesterFactory() {
-      super(DefaultSqlTestFactory.INSTANCE);
-    }
-
-    @Override public SqlValidator getValidator(SqlTestFactory factory) {
-      final RelDataTypeFactory typeFactory =
-          new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
-      final SqlConformance conformance = (SqlConformance) get("conformance");
-      final boolean caseSensitive = (Boolean) factory.get("caseSensitive");
-      return new SqlAdvisorValidator(
-          SqlStdOperatorTable.instance(),
-          new MockCatalogReader(typeFactory, caseSensitive).init(),
-          typeFactory,
-          conformance);
-    }
-
-    @Override public SqlAdvisor createAdvisor(SqlValidatorWithHints validator) {
-      return new SqlAdvisor(validator);
-    }
-  }
 }
 
 // End SqlAdvisorTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/96b28f7b/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java b/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java
index 778715c..903504c 100644
--- a/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java
+++ b/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java
@@ -7641,7 +7641,7 @@ public abstract class SqlOperatorBaseTest {
   }
 
   public static SqlTester tester() {
-    return new TesterImpl(DefaultSqlTestFactory.INSTANCE);
+    return new TesterImpl(SqlTestFactory.INSTANCE);
   }
 
   /**
@@ -7672,16 +7672,8 @@ public abstract class SqlOperatorBaseTest {
       }
     }
 
-    @Override protected TesterImpl with(final String name2, final Object value) {
-      return new TesterImpl(
-          new DelegatingSqlTestFactory(factory) {
-            @Override public Object get(String name) {
-              if (name.equals(name2)) {
-                return value;
-              }
-              return super.get(name);
-            }
-          });
+    @Override protected TesterImpl with(final String name, final Object value) {
+      return new TesterImpl(factory.with(name, value));
     }
   }
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/96b28f7b/core/src/test/java/org/apache/calcite/sql/test/SqlTestFactory.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/sql/test/SqlTestFactory.java b/core/src/test/java/org/apache/calcite/sql/test/SqlTestFactory.java
index f4dd25a..7269c8a 100644
--- a/core/src/test/java/org/apache/calcite/sql/test/SqlTestFactory.java
+++ b/core/src/test/java/org/apache/calcite/sql/test/SqlTestFactory.java
@@ -16,27 +16,184 @@
  */
 package org.apache.calcite.sql.test;
 
-import org.apache.calcite.adapter.java.JavaTypeFactory;
+import org.apache.calcite.avatica.util.Casing;
+import org.apache.calcite.avatica.util.Quoting;
+import org.apache.calcite.jdbc.JavaTypeFactoryImpl;
+import org.apache.calcite.rel.type.DelegatingTypeSystem;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
+import org.apache.calcite.rel.type.RelDataTypeSystem;
 import org.apache.calcite.sql.SqlOperatorTable;
 import org.apache.calcite.sql.advise.SqlAdvisor;
+import org.apache.calcite.sql.fun.SqlStdOperatorTable;
 import org.apache.calcite.sql.parser.SqlParser;
+import org.apache.calcite.sql.validate.SqlConformance;
+import org.apache.calcite.sql.validate.SqlConformanceEnum;
 import org.apache.calcite.sql.validate.SqlValidator;
+import org.apache.calcite.sql.validate.SqlValidatorCatalogReader;
+import org.apache.calcite.sql.validate.SqlValidatorUtil;
 import org.apache.calcite.sql.validate.SqlValidatorWithHints;
-import org.apache.calcite.test.MockCatalogReader;
+import org.apache.calcite.test.CalciteAssert;
+import org.apache.calcite.test.MockSqlOperatorTable;
+import org.apache.calcite.test.catalog.MockCatalogReader;
+import org.apache.calcite.test.catalog.MockCatalogReaderSimple;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSortedMap;
+
+import java.util.Map;
+import java.util.Objects;
 
 /**
-* Creates the objects needed to run a SQL parsing or validation test.
+ * Default implementation of {@link SqlTestFactory}.
  *
- * @see org.apache.calcite.sql.test.SqlTester
+ * <p>Suitable for most tests. If you want different behavior, you can extend;
+ * if you want a factory with different properties (e.g. SQL conformance level
+ * or identifier quoting), use {@link #with(String, Object)} to create a new factory.</p>
 */
-public interface SqlTestFactory {
-  MockCatalogReader createCatalogReader(SqlTestFactory testFactory,
-      JavaTypeFactory typeFactory);
-  SqlOperatorTable createOperatorTable(SqlTestFactory factory);
-  SqlParser createParser(SqlTestFactory factory, String sql);
-  SqlValidator getValidator(SqlTestFactory factory);
-  SqlAdvisor createAdvisor(SqlValidatorWithHints validator);
-  Object get(String name);
+public class SqlTestFactory {
+  public static final ImmutableMap<String, Object> DEFAULT_OPTIONS =
+      ImmutableSortedMap.<String, Object>naturalOrder()
+          .put("quoting", Quoting.DOUBLE_QUOTE)
+          .put("quotedCasing", Casing.UNCHANGED)
+          .put("unquotedCasing", Casing.TO_UPPER)
+          .put("caseSensitive", true)
+          .put("conformance", SqlConformanceEnum.DEFAULT)
+          .put("operatorTable", SqlStdOperatorTable.instance())
+          .put("connectionFactory",
+              CalciteAssert.EMPTY_CONNECTION_FACTORY
+                  .with(
+                      new CalciteAssert.AddSchemaSpecPostProcessor(
+                          CalciteAssert.SchemaSpec.HR)))
+          .build();
+
+  public static final SqlTestFactory INSTANCE =
+      new SqlTestFactory();
+
+  private final ImmutableMap<String, Object> options;
+  private final MockCatalogReaderFactory catalogReaderFactory;
+  private final ValidatorFactory validatorFactory;
+
+  private final RelDataTypeFactory typeFactory;
+  private final SqlOperatorTable operatorTable;
+  private final SqlValidatorCatalogReader catalogReader;
+  private final SqlParser.Config parserConfig;
+
+  protected SqlTestFactory() {
+    this(DEFAULT_OPTIONS, MockCatalogReaderSimple::new, SqlValidatorUtil::newValidator);
+  }
+
+  protected SqlTestFactory(ImmutableMap<String, Object> options,
+      MockCatalogReaderFactory catalogReaderFactory,
+      ValidatorFactory validatorFactory) {
+    this.options = options;
+    this.catalogReaderFactory = catalogReaderFactory;
+    this.validatorFactory = validatorFactory;
+    this.operatorTable = createOperatorTable((SqlOperatorTable) options.get("operatorTable"));
+    this.typeFactory = createTypeFactory((SqlConformance) options.get("conformance"));
+    Boolean caseSensitive = (Boolean) options.get("caseSensitive");
+    this.catalogReader = catalogReaderFactory.create(typeFactory, caseSensitive).init();
+    this.parserConfig = createParserConfig(options);
+  }
+
+  public static MockCatalogReader createCatalogReader(
+      RelDataTypeFactory typeFactory,
+      boolean caseSensitive) {
+    return new MockCatalogReaderSimple(typeFactory, caseSensitive).init();
+  }
+
+  private static SqlOperatorTable createOperatorTable(SqlOperatorTable opTab0) {
+    MockSqlOperatorTable opTab = new MockSqlOperatorTable(opTab0);
+    MockSqlOperatorTable.addRamp(opTab);
+    return opTab;
+  }
+
+  public SqlParser createParser(String sql) {
+    return SqlParser.create(sql, parserConfig);
+  }
+
+  public static SqlParser.Config createParserConfig(ImmutableMap<String, Object> options) {
+    return SqlParser.configBuilder()
+        .setQuoting((Quoting) options.get("quoting"))
+        .setUnquotedCasing((Casing) options.get("unquotedCasing"))
+        .setQuotedCasing((Casing) options.get("quotedCasing"))
+        .setConformance((SqlConformance) options.get("conformance"))
+        .build();
+  }
+
+  public SqlValidator getValidator() {
+    final SqlConformance conformance =
+        (SqlConformance) options.get("conformance");
+    return validatorFactory.create(operatorTable, catalogReader, typeFactory, conformance);
+  }
+
+  public SqlAdvisor createAdvisor() {
+    SqlValidator validator = getValidator();
+    if (validator instanceof SqlValidatorWithHints) {
+      return new SqlAdvisor((SqlValidatorWithHints) validator);
+    }
+    throw new UnsupportedOperationException(
+        "Validator should implement SqlValidatorWithHints, actual validator is " + validator);
+  }
+
+  public SqlTestFactory with(String name, Object value) {
+    if (Objects.equals(value, options.get(name))) {
+      return this;
+    }
+    ImmutableMap.Builder<String, Object> builder = ImmutableSortedMap.naturalOrder();
+    // Protect from IllegalArgumentException: Multiple entries with same key
+    for (Map.Entry<String, Object> entry : options.entrySet()) {
+      if (name.equals(entry.getKey())) {
+        continue;
+      }
+      builder.put(entry);
+    }
+    builder.put(name, value);
+    return new SqlTestFactory(builder.build(), catalogReaderFactory, validatorFactory);
+  }
+
+  public SqlTestFactory withCatalogReader(MockCatalogReaderFactory newCatalogReaderFactory) {
+    return new SqlTestFactory(options, newCatalogReaderFactory, validatorFactory);
+  }
+
+  public SqlTestFactory withValidator(ValidatorFactory newValidatorFactory) {
+    return new SqlTestFactory(options, catalogReaderFactory, newValidatorFactory);
+  }
+
+  public final Object get(String name) {
+    return options.get(name);
+  }
+
+  private static RelDataTypeFactory createTypeFactory(SqlConformance conformance) {
+    RelDataTypeSystem typeSystem = RelDataTypeSystem.DEFAULT;
+    if (conformance.shouldConvertRaggedUnionTypesToVarying()) {
+      typeSystem = new DelegatingTypeSystem(typeSystem) {
+        public boolean shouldConvertRaggedUnionTypesToVarying() {
+          return true;
+        }
+      };
+    }
+    return new JavaTypeFactoryImpl(typeSystem);
+  }
+
+  /**
+   * Creates {@link SqlValidator} for tests.
+   */
+  public interface ValidatorFactory {
+    SqlValidator create(
+        SqlOperatorTable opTab,
+        SqlValidatorCatalogReader catalogReader,
+        RelDataTypeFactory typeFactory,
+        SqlConformance conformance);
+  }
+
+  /**
+   * Creates {@link MockCatalogReader} for tests.
+   * Note: {@link MockCatalogReader#init()} is to be invoked later, so a typical implementation
+   * should be via constructor reference like {@code MockCatalogReaderSimple::new}.
+   */
+  public interface MockCatalogReaderFactory {
+    MockCatalogReader create(RelDataTypeFactory typeFactory, boolean caseSensitive);
+  }
 }
 
 // End SqlTestFactory.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/96b28f7b/core/src/test/java/org/apache/calcite/sql/test/SqlTesterImpl.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/sql/test/SqlTesterImpl.java b/core/src/test/java/org/apache/calcite/sql/test/SqlTesterImpl.java
index d215b45..533cb99 100644
--- a/core/src/test/java/org/apache/calcite/sql/test/SqlTesterImpl.java
+++ b/core/src/test/java/org/apache/calcite/sql/test/SqlTesterImpl.java
@@ -98,7 +98,7 @@ public class SqlTesterImpl implements SqlTester, AutoCloseable {
   }
 
   public final SqlValidator getValidator() {
-    return factory.getValidator(factory);
+    return factory.getValidator();
   }
 
   public void assertExceptionIsThrown(
@@ -163,7 +163,7 @@ public class SqlTesterImpl implements SqlTester, AutoCloseable {
   }
 
   public SqlNode parseQuery(String sql) throws SqlParseException {
-    SqlParser parser = factory.createParser(factory, sql);
+    SqlParser parser = factory.createParser(sql);
     return parser.parseQuery();
   }
 
@@ -312,16 +312,8 @@ public class SqlTesterImpl implements SqlTester, AutoCloseable {
     return with("connectionFactory", connectionFactory);
   }
 
-  protected SqlTesterImpl with(final String name2, final Object value) {
-    return new SqlTesterImpl(
-        new DelegatingSqlTestFactory(factory) {
-          @Override public Object get(String name) {
-            if (name.equals(name2)) {
-              return value;
-            }
-            return super.get(name);
-          }
-        });
+  protected SqlTesterImpl with(final String name, final Object value) {
+    return new SqlTesterImpl(factory.with(name, value));
   }
 
   // SqlTester methods

http://git-wip-us.apache.org/repos/asf/calcite/blob/96b28f7b/core/src/test/java/org/apache/calcite/sql/validate/SqlValidatorUtilTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/sql/validate/SqlValidatorUtilTest.java b/core/src/test/java/org/apache/calcite/sql/validate/SqlValidatorUtilTest.java
index a716002..0405308 100644
--- a/core/src/test/java/org/apache/calcite/sql/validate/SqlValidatorUtilTest.java
+++ b/core/src/test/java/org/apache/calcite/sql/validate/SqlValidatorUtilTest.java
@@ -20,7 +20,7 @@ import org.apache.calcite.runtime.CalciteContextException;
 import org.apache.calcite.sql.SqlIdentifier;
 import org.apache.calcite.sql.SqlNode;
 import org.apache.calcite.sql.parser.SqlParserPos;
-import org.apache.calcite.sql.test.DefaultSqlTestFactory;
+import org.apache.calcite.sql.test.SqlTestFactory;
 import org.apache.calcite.sql.test.SqlTesterImpl;
 
 import com.google.common.collect.ImmutableList;
@@ -125,7 +125,7 @@ public class SqlValidatorUtilTest {
     newList.add(new SqlIdentifier(Arrays.asList("f0", "c0"), SqlParserPos.ZERO));
     newList.add(new SqlIdentifier(Arrays.asList("f0", "c0"), SqlParserPos.ZERO));
     final SqlTesterImpl tester =
-        new SqlTesterImpl(DefaultSqlTestFactory.INSTANCE);
+        new SqlTesterImpl(SqlTestFactory.INSTANCE);
     final SqlValidatorImpl validator =
         (SqlValidatorImpl) tester.getValidator();
     try {