You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by mm...@apache.org on 2018/07/20 17:41:37 UTC

[24/53] [abbrv] calcite git commit: [CALCITE-2280] Babel SQL parser

[CALCITE-2280] Babel SQL parser

Add BABEL conformance value and SqlConformance.isLiberal() method.

CalciteAssert.withSchema automatically sets the schema as default.


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

Branch: refs/heads/site
Commit: 3e50a5325357720d109e5fa300dad7a918087139
Parents: c749f25
Author: Julian Hyde <jh...@apache.org>
Authored: Wed May 2 00:03:14 2018 -0700
Committer: Julian Hyde <jh...@apache.org>
Committed: Sun Jul 8 22:41:08 2018 -0700

----------------------------------------------------------------------
 babel/pom.xml                                   | 241 +++++++++++++++++++
 babel/src/main/codegen/config.fmpp              |  80 ++++++
 babel/src/main/codegen/includes/parserImpls.ftl |  18 ++
 .../org/apache/calcite/sql/babel/Babel.java     |  26 ++
 .../apache/calcite/sql/babel/package-info.java  |  26 ++
 .../apache/calcite/test/BabelParserTest.java    |  47 ++++
 .../apache/calcite/test/BabelQuidemTest.java    | 193 +++++++++++++++
 .../java/org/apache/calcite/test/BabelTest.java |  56 +++++
 babel/src/test/resources/sql/dummy.iq           |  27 +++
 babel/src/test/resources/sql/select.iq          |  54 +++++
 .../apache/calcite/runtime/CalciteResource.java |   3 +
 .../sql/validate/SqlAbstractConformance.java    |   4 +
 .../calcite/sql/validate/SqlConformance.java    |  20 ++
 .../sql/validate/SqlConformanceEnum.java        |  30 ++-
 .../calcite/runtime/CalciteResource.properties  |   1 +
 .../org/apache/calcite/test/CalciteAssert.java  | 131 +++++-----
 .../calcite/test/JdbcFrontJdbcBackTest.java     |   1 -
 .../org/apache/calcite/test/QuidemTest.java     |  19 +-
 pom.xml                                         |  21 +-
 .../src/test/resources/sql/materialized_view.iq |   4 +-
 server/src/test/resources/sql/schema.iq         |   2 +-
 21 files changed, 935 insertions(+), 69 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/3e50a532/babel/pom.xml
----------------------------------------------------------------------
diff --git a/babel/pom.xml b/babel/pom.xml
new file mode 100644
index 0000000..20550d7
--- /dev/null
+++ b/babel/pom.xml
@@ -0,0 +1,241 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.calcite</groupId>
+    <artifactId>calcite</artifactId>
+    <version>1.17.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>calcite-babel</artifactId>
+  <packaging>jar</packaging>
+  <version>1.17.0-SNAPSHOT</version>
+  <name>Calcite Babel</name>
+  <description>Calcite Babel</description>
+
+  <properties>
+    <top.dir>${project.basedir}/..</top.dir>
+  </properties>
+
+  <dependencies>
+    <!-- Sorted by groupId, artifactId; calcite dependencies first. Put versions
+         in dependencyManagement in the root POM, not here. -->
+    <dependency>
+      <groupId>org.apache.calcite.avatica</groupId>
+      <artifactId>avatica-core</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.calcite</groupId>
+      <artifactId>calcite-core</artifactId>
+      <type>jar</type>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.calcite</groupId>
+      <artifactId>calcite-core</artifactId>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>com.google.guava</groupId>
+      <artifactId>guava</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>net.hydromatic</groupId>
+      <artifactId>quidem</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>net.hydromatic</groupId>
+      <artifactId>scott-data-hsqldb</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.hamcrest</groupId>
+      <artifactId>hamcrest-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.incava</groupId>
+      <artifactId>java-diff</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.hsqldb</groupId>
+      <artifactId>hsqldb</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-log4j12</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <!-- Sorted by groupId, artifactId. Put versions in
+           pluginManagement in the root POM, not here. -->
+      <plugin>
+        <artifactId>maven-dependency-plugin</artifactId>
+        <version>${maven-dependency-plugin.version}</version>
+        <executions>
+          <execution>
+            <id>analyze</id>
+            <goals>
+              <goal>analyze-only</goal>
+            </goals>
+            <configuration>
+              <failOnWarning>true</failOnWarning>
+              <!-- ignore "unused but declared" warnings -->
+              <ignoredUnusedDeclaredDependencies>
+                <ignoredUnusedDeclaredDependency>net.hydromatic:scott-data-hsqldb</ignoredUnusedDeclaredDependency>
+                <ignoredUnusedDeclaredDependency>org.hsqldb:hsqldb</ignoredUnusedDeclaredDependency>
+                <ignoredUnusedDeclaredDependency>org.incava:java-diff</ignoredUnusedDeclaredDependency>
+                <ignoredUnusedDeclaredDependency>org.slf4j:slf4j-api</ignoredUnusedDeclaredDependency>
+                <ignoredUnusedDeclaredDependency>org.slf4j:slf4j-log4j12</ignoredUnusedDeclaredDependency>
+              </ignoredUnusedDeclaredDependencies>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <executions>
+          <execution>
+            <goals>
+              <goal>test-jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <artifactId>maven-resources-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>copy-fmpp-resources</id>
+            <phase>initialize</phase>
+            <goals>
+              <goal>copy-resources</goal>
+            </goals>
+            <configuration>
+              <outputDirectory>${project.build.directory}/codegen</outputDirectory>
+              <resources>
+                <resource>
+                  <directory>src/main/codegen</directory>
+                  <filtering>false</filtering>
+                </resource>
+              </resources>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-release-plugin</artifactId>
+      </plugin>
+      <!-- Parent module has the same plugin and does the work of
+           generating -sources.jar for each project. But without the
+           plugin declared here, IDEs don't know the sources are
+           available. -->
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-source-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>attach-sources</id>
+            <phase>verify</phase>
+            <goals>
+              <goal>jar-no-fork</goal>
+              <goal>test-jar-no-fork</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>javacc-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>javacc</id>
+            <goals>
+              <goal>javacc</goal>
+            </goals>
+            <configuration>
+              <sourceDirectory>${project.build.directory}/generated-sources/fmpp</sourceDirectory>
+              <outputDirectory>${project.build.directory}/generated-sources/javacc</outputDirectory>
+              <includes>
+                <include>**/Parser.jj</include>
+              </includes>
+              <lookAhead>2</lookAhead>
+              <isStatic>false</isStatic>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+  <profiles>
+    <profile>
+      <!-- CALCITE-538: workaround for https://github.com/freemarker/fmpp/issues/11
+        FMPP always overwrites destination file, however we do not want
+        recompile the whole module every time.
+      -->
+      <id>generate-parser</id>
+      <activation>
+        <property>
+          <name>!skipGenerate</name>
+        </property>
+      </activation>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>com.googlecode.fmpp-maven-plugin</groupId>
+            <artifactId>fmpp-maven-plugin</artifactId>
+            <executions>
+              <execution>
+                <configuration>
+                  <cfgFile>src/main/codegen/config.fmpp</cfgFile>
+                  <outputDirectory>${project.build.directory}/generated-sources/fmpp</outputDirectory>
+                  <templateDirectory>${top.dir}/core/src/main/codegen/templates</templateDirectory>
+                </configuration>
+                <id>generate-fmpp-sources</id>
+                <phase>validate</phase>
+                <goals>
+                  <goal>generate</goal>
+                </goals>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
+</project>

http://git-wip-us.apache.org/repos/asf/calcite/blob/3e50a532/babel/src/main/codegen/config.fmpp
----------------------------------------------------------------------
diff --git a/babel/src/main/codegen/config.fmpp b/babel/src/main/codegen/config.fmpp
new file mode 100644
index 0000000..57bbe86
--- /dev/null
+++ b/babel/src/main/codegen/config.fmpp
@@ -0,0 +1,80 @@
+# 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.
+
+data: {
+    parser: {
+      # Generated parser implementation class package and name
+      package: "org.apache.calcite.sql.parser.babel",
+      class: "SqlBabelParserImpl",
+
+      # List of import statements.
+      imports: [
+      ]
+
+      # List of keywords.
+      keywords: [
+        "SEMI"
+      ]
+
+      # List of keywords from "keywords" section that are not reserved.
+      nonReservedKeywords: [
+        "SEMI"
+      ]
+
+      # List of methods for parsing custom SQL statements.
+      statementParserMethods: [
+      ]
+
+      # List of methods for parsing custom literals.
+      # Example: ParseJsonLiteral().
+      literalParserMethods: [
+      ]
+
+      # List of methods for parsing custom data types.
+      dataTypeParserMethods: [
+      ]
+
+      # List of methods for parsing extensions to "ALTER <scope>" calls.
+      # Each must accept arguments "(SqlParserPos pos, String scope)".
+      alterStatementParserMethods: [
+      ]
+
+      # List of methods for parsing extensions to "CREATE [OR REPLACE]" calls.
+      # Each must accept arguments "(SqlParserPos pos, boolean replace)".
+      createStatementParserMethods: [
+      ]
+
+      # List of methods for parsing extensions to "DROP" calls.
+      # Each must accept arguments "(SqlParserPos pos)".
+      dropStatementParserMethods: [
+      ]
+
+      # List of files in @includes directory that have parser method
+      # implementations for parsing custom SQL statements, literals or types
+      # given as part of "statementParserMethods", "literalParserMethods" or
+      # "dataTypeParserMethods".
+      implementationFiles: [
+        "parserImpls.ftl"
+      ]
+
+      includeCompoundIdentifier: true
+      includeBraces: true
+      includeAdditionalDeclarations: false
+
+    }
+}
+freemarkerLinks: {
+    includes: includes/
+}

http://git-wip-us.apache.org/repos/asf/calcite/blob/3e50a532/babel/src/main/codegen/includes/parserImpls.ftl
----------------------------------------------------------------------
diff --git a/babel/src/main/codegen/includes/parserImpls.ftl b/babel/src/main/codegen/includes/parserImpls.ftl
new file mode 100644
index 0000000..627a634
--- /dev/null
+++ b/babel/src/main/codegen/includes/parserImpls.ftl
@@ -0,0 +1,18 @@
+<#--
+// 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.
+-->
+
+// End parserImpls.ftl

http://git-wip-us.apache.org/repos/asf/calcite/blob/3e50a532/babel/src/main/java/org/apache/calcite/sql/babel/Babel.java
----------------------------------------------------------------------
diff --git a/babel/src/main/java/org/apache/calcite/sql/babel/Babel.java b/babel/src/main/java/org/apache/calcite/sql/babel/Babel.java
new file mode 100644
index 0000000..167a136
--- /dev/null
+++ b/babel/src/main/java/org/apache/calcite/sql/babel/Babel.java
@@ -0,0 +1,26 @@
+/*
+ * 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.babel;
+
+/** SQL parser that accepts a wide variety of dialects. */
+@SuppressWarnings("unused")
+public class Babel {
+  // This class is currently a place-holder. Javadoc gets upset
+  // if there are no classes in babel/java/main.
+}
+
+// End Babel.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/3e50a532/babel/src/main/java/org/apache/calcite/sql/babel/package-info.java
----------------------------------------------------------------------
diff --git a/babel/src/main/java/org/apache/calcite/sql/babel/package-info.java b/babel/src/main/java/org/apache/calcite/sql/babel/package-info.java
new file mode 100644
index 0000000..498513a
--- /dev/null
+++ b/babel/src/main/java/org/apache/calcite/sql/babel/package-info.java
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+/**
+ * Parse tree for SQL extensions used by the Babel parser.
+ */
+@PackageMarker
+package org.apache.calcite.sql.babel;
+
+import org.apache.calcite.avatica.util.PackageMarker;
+
+// End package-info.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/3e50a532/babel/src/test/java/org/apache/calcite/test/BabelParserTest.java
----------------------------------------------------------------------
diff --git a/babel/src/test/java/org/apache/calcite/test/BabelParserTest.java b/babel/src/test/java/org/apache/calcite/test/BabelParserTest.java
new file mode 100644
index 0000000..521266d
--- /dev/null
+++ b/babel/src/test/java/org/apache/calcite/test/BabelParserTest.java
@@ -0,0 +1,47 @@
+/*
+ * 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.test;
+
+import org.apache.calcite.sql.parser.SqlParserImplFactory;
+import org.apache.calcite.sql.parser.SqlParserTest;
+import org.apache.calcite.sql.parser.babel.SqlBabelParserImpl;
+
+import org.junit.Test;
+
+/**
+ * Tests the "Babel" SQL parser, that understands all dialects of SQL.
+ */
+public class BabelParserTest extends SqlParserTest {
+
+  @Override protected SqlParserImplFactory parserImplFactory() {
+    return SqlBabelParserImpl.FACTORY;
+  }
+
+  @Override public void testGenerateKeyWords() {
+    // by design, method only works in base class; no-ops in this sub-class
+  }
+
+  @Test public void testSelect() {
+    final String sql = "select 1 from t";
+    final String expected = "SELECT 1\n"
+        + "FROM `T`";
+    sql(sql).ok(expected);
+  }
+
+}
+
+// End BabelParserTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/3e50a532/babel/src/test/java/org/apache/calcite/test/BabelQuidemTest.java
----------------------------------------------------------------------
diff --git a/babel/src/test/java/org/apache/calcite/test/BabelQuidemTest.java b/babel/src/test/java/org/apache/calcite/test/BabelQuidemTest.java
new file mode 100644
index 0000000..4c2e304
--- /dev/null
+++ b/babel/src/test/java/org/apache/calcite/test/BabelQuidemTest.java
@@ -0,0 +1,193 @@
+/*
+ * 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.test;
+
+import org.apache.calcite.config.CalciteConnectionConfig;
+import org.apache.calcite.config.CalciteConnectionConfigImpl;
+import org.apache.calcite.config.CalciteConnectionProperty;
+import org.apache.calcite.jdbc.CalciteConnection;
+import org.apache.calcite.materialize.MaterializationService;
+import org.apache.calcite.plan.Contexts;
+import org.apache.calcite.schema.SchemaPlus;
+import org.apache.calcite.sql.SqlNode;
+import org.apache.calcite.sql.SqlWriter;
+import org.apache.calcite.sql.dialect.CalciteSqlDialect;
+import org.apache.calcite.sql.parser.SqlParser;
+import org.apache.calcite.sql.parser.babel.SqlBabelParserImpl;
+import org.apache.calcite.sql.pretty.SqlPrettyWriter;
+import org.apache.calcite.sql.validate.SqlConformanceEnum;
+import org.apache.calcite.tools.Frameworks;
+import org.apache.calcite.tools.Planner;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+
+import net.hydromatic.quidem.AbstractCommand;
+import net.hydromatic.quidem.Command;
+import net.hydromatic.quidem.CommandHandler;
+import net.hydromatic.quidem.Quidem;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.sql.Connection;
+import java.util.Collection;
+import java.util.List;
+import java.util.Properties;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Unit tests for the Babel SQL parser.
+ */
+@RunWith(Parameterized.class)
+@Ignore
+public class BabelQuidemTest extends QuidemTest {
+  /** Creates a BabelQuidemTest. Public per {@link Parameterized}. */
+  @SuppressWarnings("WeakerAccess")
+  public BabelQuidemTest(String path) {
+    super(path);
+  }
+
+  /** Runs a test from the command line.
+   *
+   * <p>For example:
+   *
+   * <blockquote>
+   *   <code>java BabelQuidemTest sql/table.iq</code>
+   * </blockquote> */
+  public static void main(String[] args) throws Exception {
+    for (String arg : args) {
+      new BabelQuidemTest(arg).test();
+    }
+  }
+
+  @Override @Test public void test() throws Exception {
+    MaterializationService.setThreadLocal();
+    super.test();
+  }
+
+  /** For {@link Parameterized} runner. */
+  @Parameterized.Parameters(name = "{index}: quidem({0})")
+  public static Collection<Object[]> data() {
+    // Start with a test file we know exists, then find the directory and list
+    // its files.
+    final String first = "sql/select.iq";
+    return data(first);
+  }
+
+  @Override protected Quidem.ConnectionFactory createConnectionFactory() {
+    return new QuidemConnectionFactory() {
+      @Override public Connection connect(String name, boolean reference)
+          throws Exception {
+        switch (name) {
+        case "babel":
+          return BabelTest.connect();
+        }
+        return super.connect(name, reference);
+      }
+    };
+  }
+
+  @Override protected CommandHandler createCommandHandler() {
+    return new BabelCommandHandler();
+  }
+
+  /** Command that prints the validated parse tree of a SQL statement. */
+  static class ExplainValidatedCommand extends AbstractCommand {
+    private final ImmutableList<String> lines;
+    private final ImmutableList<String> content;
+    private final Set<String> productSet;
+
+    ExplainValidatedCommand(List<String> lines, List<String> content,
+        Set<String> productSet) {
+      this.lines = ImmutableList.copyOf(lines);
+      this.content = ImmutableList.copyOf(content);
+      this.productSet = ImmutableSet.copyOf(productSet);
+    }
+
+    @Override public void execute(Context x, boolean execute) throws Exception {
+      if (execute) {
+        // use Babel parser
+        final SqlParser.ConfigBuilder parserConfig =
+            SqlParser.configBuilder()
+                .setParserFactory(SqlBabelParserImpl.FACTORY);
+
+        // use Babel conformance for validation
+        final Properties properties = new Properties();
+        properties.setProperty(CalciteConnectionProperty.CONFORMANCE.name(),
+            SqlConformanceEnum.BABEL.name());
+        final CalciteConnectionConfig connectionConfig =
+            new CalciteConnectionConfigImpl(properties);
+
+        // extract named schema from connection and use it in planner
+        final CalciteConnection calciteConnection =
+            x.connection().unwrap(CalciteConnection.class);
+        final String schemaName = calciteConnection.getSchema();
+        final SchemaPlus schema =
+            schemaName != null
+                ? calciteConnection.getRootSchema().getSubSchema(schemaName)
+                : calciteConnection.getRootSchema();
+        final Frameworks.ConfigBuilder config =
+            Frameworks.newConfigBuilder()
+                .defaultSchema(schema)
+                .parserConfig(parserConfig.build())
+                .context(Contexts.of(connectionConfig));
+
+        // parse, validate and un-parse
+        final Quidem.SqlCommand sqlCommand = x.previousSqlCommand();
+        final Planner planner = Frameworks.getPlanner(config.build());
+        final SqlNode node = planner.parse(sqlCommand.sql);
+        final SqlNode validateNode = planner.validate(node);
+        final SqlWriter sqlWriter =
+            new SqlPrettyWriter(CalciteSqlDialect.DEFAULT);
+        validateNode.unparse(sqlWriter, 0, 0);
+        x.echo(ImmutableList.of(sqlWriter.toSqlString().getSql()));
+      } else {
+        x.echo(content);
+      }
+      x.echo(lines);
+    }
+  }
+
+  /** Command handler that adds a "!explain-validated-on dialect..." command
+   * (see {@link ExplainValidatedCommand}). */
+  private static class BabelCommandHandler implements CommandHandler {
+    @Override public Command parseCommand(List<String> lines,
+        List<String> content, String line) {
+      final String prefix = "explain-validated-on";
+      if (line.startsWith(prefix)) {
+        final Pattern pattern =
+            Pattern.compile("explain-validated-on( [-_+a-zA-Z0-9]+)*?");
+        final Matcher matcher = pattern.matcher(line);
+        if (matcher.matches()) {
+          final ImmutableSet.Builder<String> set = ImmutableSet.builder();
+          for (int i = 0; i < matcher.groupCount(); i++) {
+            set.add(matcher.group(i + 1));
+          }
+          return new ExplainValidatedCommand(lines, content, set.build());
+        }
+      }
+      return null;
+    }
+  }
+}
+
+// End BabelQuidemTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/3e50a532/babel/src/test/java/org/apache/calcite/test/BabelTest.java
----------------------------------------------------------------------
diff --git a/babel/src/test/java/org/apache/calcite/test/BabelTest.java b/babel/src/test/java/org/apache/calcite/test/BabelTest.java
new file mode 100644
index 0000000..5a21749
--- /dev/null
+++ b/babel/src/test/java/org/apache/calcite/test/BabelTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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.test;
+
+import org.apache.calcite.config.CalciteConnectionProperty;
+import org.apache.calcite.sql.parser.babel.SqlBabelParserImpl;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Unit tests for Babel framework.
+ */
+public class BabelTest {
+
+  static final String URL = "jdbc:calcite:";
+
+  @Rule
+  public ExpectedException thrown = ExpectedException.none();
+
+  static Connection connect() throws SQLException {
+    return DriverManager.getConnection(URL,
+        CalciteAssert.propBuilder()
+            .set(CalciteConnectionProperty.PARSER_FACTORY,
+                SqlBabelParserImpl.class.getName() + "#FACTORY")
+            .build());
+  }
+
+  @Test public void testFoo() {
+    assertThat(1 + 1, is(2));
+  }
+}
+
+// End BabelTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/3e50a532/babel/src/test/resources/sql/dummy.iq
----------------------------------------------------------------------
diff --git a/babel/src/test/resources/sql/dummy.iq b/babel/src/test/resources/sql/dummy.iq
new file mode 100755
index 0000000..e5aa269
--- /dev/null
+++ b/babel/src/test/resources/sql/dummy.iq
@@ -0,0 +1,27 @@
+# dummy.iq
+#
+# 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.
+#
+!use scott
+!set outputformat mysql
+
+# VALUES as top-level (not Oracle)
+VALUES 1 + 2;
+
+VALUES ROW(1 + 2)
+!explain-validated-on calcite postgres
+
+# End dummy.iq

http://git-wip-us.apache.org/repos/asf/calcite/blob/3e50a532/babel/src/test/resources/sql/select.iq
----------------------------------------------------------------------
diff --git a/babel/src/test/resources/sql/select.iq b/babel/src/test/resources/sql/select.iq
new file mode 100755
index 0000000..ef692dc
--- /dev/null
+++ b/babel/src/test/resources/sql/select.iq
@@ -0,0 +1,54 @@
+# select.iq - Babel test for non-standard clauses in SELECT
+#
+# 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.
+#
+!use scott
+!set outputformat mysql
+
+# ORDER BY column not in SELECT clause
+SELECT ename
+FROM emp, dept
+ORDER BY emp.deptno;
+
+SELECT "EMP"."ENAME"
+FROM "scott"."EMP" AS "EMP",
+        "scott"."DEPT" AS "DEPT"
+ORDER BY "EMP"."DEPTNO"
+!explain-validated-on all
+
+# Test CONNECT BY (Oracle only)
+!if (false) {
+SELECT *
+FROM emp
+START WITH mgr IS NULL
+CONNECT BY empno = PRIOR mgr;
+select(...)
+!explain-validated-on oracle
+!}
+
+# WITH RECURSIVE (Oracle, MySQL 8 onwards)
+!if (false) {
+WITH RECURSIVE t(n) AS (
+    VALUES (1)
+  UNION ALL
+    SELECT n+1 FROM t WHERE n < 100
+)
+SELECT sum(n) FROM t;
+select(...)
+!explain-validated-on mysql8+ oracle
+!}
+
+# End select.iq

http://git-wip-us.apache.org/repos/asf/calcite/blob/3e50a532/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java b/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
index 48079eb..122b39e 100644
--- a/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
+++ b/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
@@ -754,6 +754,9 @@ public interface CalciteResource {
 
   @BaseMessage("Type ''{0}'' not found")
   ExInst<SqlValidatorException> typeNotFound(String name);
+
+  @BaseMessage("Dialect does not support feature: ''{0}''")
+  ExInst<SqlValidatorException> dialectDoesNotSupportFeature(String featureName);
 }
 
 // End CalciteResource.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/3e50a532/core/src/main/java/org/apache/calcite/sql/validate/SqlAbstractConformance.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlAbstractConformance.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlAbstractConformance.java
index c4d0f2d..059127b 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlAbstractConformance.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlAbstractConformance.java
@@ -23,6 +23,10 @@ package org.apache.calcite.sql.validate;
  * and behaves the same as in {@link SqlConformanceEnum#DEFAULT}.
  */
 public abstract class SqlAbstractConformance implements SqlConformance {
+  public boolean isLiberal() {
+    return SqlConformanceEnum.DEFAULT.isLiberal();
+  }
+
   public boolean isGroupByAlias() {
     return SqlConformanceEnum.DEFAULT.isGroupByAlias();
   }

http://git-wip-us.apache.org/repos/asf/calcite/blob/3e50a532/core/src/main/java/org/apache/calcite/sql/validate/SqlConformance.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlConformance.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlConformance.java
index 84eee35..9fa0f5e 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlConformance.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlConformance.java
@@ -62,10 +62,17 @@ public interface SqlConformance {
   SqlConformanceEnum PRAGMATIC_2003 = SqlConformanceEnum.PRAGMATIC_2003;
 
   /**
+   * Whether this dialect supports features from a wide variety of
+   * dialects. This is enabled for the Babel parser, disabled otherwise.
+   */
+  boolean isLiberal();
+
+  /**
    * Whether to allow aliases from the {@code SELECT} clause to be used as
    * column names in the {@code GROUP BY} clause.
    *
    * <p>Among the built-in conformance levels, true in
+   * {@link SqlConformanceEnum#BABEL},
    * {@link SqlConformanceEnum#LENIENT},
    * {@link SqlConformanceEnum#MYSQL_5};
    * false otherwise.
@@ -77,6 +84,7 @@ public interface SqlConformance {
    * in the select list'.
    *
    * <p>Among the built-in conformance levels, true in
+   * {@link SqlConformanceEnum#BABEL},
    * {@link SqlConformanceEnum#LENIENT},
    * {@link SqlConformanceEnum#MYSQL_5};
    * false otherwise.
@@ -88,6 +96,7 @@ public interface SqlConformance {
    * column names in the {@code HAVING} clause.
    *
    * <p>Among the built-in conformance levels, true in
+   * {@link SqlConformanceEnum#BABEL},
    * {@link SqlConformanceEnum#LENIENT},
    * {@link SqlConformanceEnum#MYSQL_5};
    * false otherwise.
@@ -100,6 +109,7 @@ public interface SqlConformance {
    *
    * <p>Among the built-in conformance levels, true in
    * {@link SqlConformanceEnum#DEFAULT},
+   * {@link SqlConformanceEnum#BABEL},
    * {@link SqlConformanceEnum#LENIENT},
    * {@link SqlConformanceEnum#MYSQL_5},
    * {@link SqlConformanceEnum#ORACLE_10},
@@ -118,6 +128,7 @@ public interface SqlConformance {
    *
    * <p>Among the built-in conformance levels, true in
    * {@link SqlConformanceEnum#DEFAULT},
+   * {@link SqlConformanceEnum#BABEL},
    * {@link SqlConformanceEnum#LENIENT},
    * {@link SqlConformanceEnum#MYSQL_5},
    * {@link SqlConformanceEnum#ORACLE_10},
@@ -156,6 +167,7 @@ public interface SqlConformance {
    * the parser.
    *
    * <p>Among the built-in conformance levels, true in
+   * {@link SqlConformanceEnum#BABEL},
    * {@link SqlConformanceEnum#LENIENT},
    * {@link SqlConformanceEnum#MYSQL_5},
    * {@link SqlConformanceEnum#ORACLE_10};
@@ -169,6 +181,7 @@ public interface SqlConformance {
    * {@code mod} function.
    *
    * <p>Among the built-in conformance levels, true in
+   * {@link SqlConformanceEnum#BABEL},
    * {@link SqlConformanceEnum#LENIENT},
    * {@link SqlConformanceEnum#MYSQL_5};
    * false otherwise.
@@ -180,6 +193,7 @@ public interface SqlConformance {
    * the parser.
    *
    * <p>Among the built-in conformance levels, true in
+   * {@link SqlConformanceEnum#BABEL},
    * {@link SqlConformanceEnum#LENIENT},
    * {@link SqlConformanceEnum#ORACLE_10};
    * {@link SqlConformanceEnum#ORACLE_12};
@@ -207,6 +221,7 @@ public interface SqlConformance {
    * </ul>
    *
    * <p>Among the built-in conformance levels, true in
+   * {@link SqlConformanceEnum#BABEL},
    * {@link SqlConformanceEnum#LENIENT},
    * {@link SqlConformanceEnum#SQL_SERVER_2008};
    * {@link SqlConformanceEnum#ORACLE_12};
@@ -228,6 +243,7 @@ public interface SqlConformance {
    * column is not declared {@code NOT NULL}.
    *
    * <p>Among the built-in conformance levels, true in
+   * {@link SqlConformanceEnum#BABEL},
    * {@link SqlConformanceEnum#LENIENT},
    * {@link SqlConformanceEnum#PRAGMATIC_99},
    * {@link SqlConformanceEnum#PRAGMATIC_2003};
@@ -254,6 +270,7 @@ public interface SqlConformance {
    * not.
    *
    * <p>Among the built-in conformance levels, true in
+   * {@link SqlConformanceEnum#BABEL},
    * {@link SqlConformanceEnum#LENIENT},
    * {@link SqlConformanceEnum#MYSQL_5};
    * false otherwise.
@@ -293,6 +310,7 @@ public interface SqlConformance {
    * </blockquote>
    *
    * <p>Among the built-in conformance levels, true in
+   * {@link SqlConformanceEnum#BABEL},
    * {@link SqlConformanceEnum#LENIENT};
    * false otherwise.
    */
@@ -308,6 +326,7 @@ public interface SqlConformance {
    * <p>MySQL and CUBRID allow this behavior.
    *
    * <p>Among the built-in conformance levels, true in
+   * {@link SqlConformanceEnum#BABEL},
    * {@link SqlConformanceEnum#LENIENT},
    * {@link SqlConformanceEnum#MYSQL_5};
    * false otherwise.
@@ -318,6 +337,7 @@ public interface SqlConformance {
    * Whether to allow geo-spatial extensions, including the GEOMETRY type.
    *
    * <p>Among the built-in conformance levels, true in
+   * {@link SqlConformanceEnum#BABEL},
    * {@link SqlConformanceEnum#LENIENT},
    * {@link SqlConformanceEnum#MYSQL_5},
    * {@link SqlConformanceEnum#SQL_SERVER_2008};

http://git-wip-us.apache.org/repos/asf/calcite/blob/3e50a532/core/src/main/java/org/apache/calcite/sql/validate/SqlConformanceEnum.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlConformanceEnum.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlConformanceEnum.java
index c52d39c..192e58b 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlConformanceEnum.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlConformanceEnum.java
@@ -23,9 +23,14 @@ public enum SqlConformanceEnum implements SqlConformance {
   /** Calcite's default SQL behavior. */
   DEFAULT,
 
-  /** Conformance value that allows just about everything. */
+  /** Conformance value that allows just about everything supported by
+   * Calcite. */
   LENIENT,
 
+  /** Conformance value that allows anything supported by any dialect.
+   * Even more liberal than {@link #LENIENT}. */
+  BABEL,
+
   /** Conformance value that instructs Calcite to use SQL semantics strictly
    * consistent with the SQL:92 standard. */
   STRICT_92,
@@ -66,8 +71,18 @@ public enum SqlConformanceEnum implements SqlConformance {
    * consistent with Microsoft SQL Server version 2008. */
   SQL_SERVER_2008;
 
+  public boolean isLiberal() {
+    switch (this) {
+    case BABEL:
+      return true;
+    default:
+      return false;
+    }
+  }
+
   public boolean isGroupByAlias() {
     switch (this) {
+    case BABEL:
     case LENIENT:
     case MYSQL_5:
       return true;
@@ -78,6 +93,7 @@ public enum SqlConformanceEnum implements SqlConformance {
 
   public boolean isGroupByOrdinal() {
     switch (this) {
+    case BABEL:
     case LENIENT:
     case MYSQL_5:
       return true;
@@ -88,6 +104,7 @@ public enum SqlConformanceEnum implements SqlConformance {
 
   public boolean isHavingAlias() {
     switch (this) {
+    case BABEL:
     case LENIENT:
     case MYSQL_5:
       return true;
@@ -99,6 +116,7 @@ public enum SqlConformanceEnum implements SqlConformance {
   public boolean isSortByOrdinal() {
     switch (this) {
     case DEFAULT:
+    case BABEL:
     case LENIENT:
     case MYSQL_5:
     case ORACLE_10:
@@ -116,6 +134,7 @@ public enum SqlConformanceEnum implements SqlConformance {
   public boolean isSortByAlias() {
     switch (this) {
     case DEFAULT:
+    case BABEL:
     case LENIENT:
     case MYSQL_5:
     case ORACLE_10:
@@ -148,6 +167,7 @@ public enum SqlConformanceEnum implements SqlConformance {
   public boolean isBangEqualAllowed() {
     switch (this) {
     case LENIENT:
+    case BABEL:
     case MYSQL_5:
     case ORACLE_10:
     case ORACLE_12:
@@ -159,6 +179,7 @@ public enum SqlConformanceEnum implements SqlConformance {
 
   @Override public boolean isMinusAllowed() {
     switch (this) {
+    case BABEL:
     case LENIENT:
     case ORACLE_10:
     case ORACLE_12:
@@ -170,6 +191,7 @@ public enum SqlConformanceEnum implements SqlConformance {
 
   @Override public boolean isPercentRemainderAllowed() {
     switch (this) {
+    case BABEL:
     case LENIENT:
     case MYSQL_5:
       return true;
@@ -180,6 +202,7 @@ public enum SqlConformanceEnum implements SqlConformance {
 
   public boolean isApplyAllowed() {
     switch (this) {
+    case BABEL:
     case LENIENT:
     case SQL_SERVER_2008:
     case ORACLE_12:
@@ -191,6 +214,7 @@ public enum SqlConformanceEnum implements SqlConformance {
 
   public boolean isInsertSubsetColumnsAllowed() {
     switch (this) {
+    case BABEL:
     case LENIENT:
     case PRAGMATIC_99:
     case PRAGMATIC_2003:
@@ -202,6 +226,7 @@ public enum SqlConformanceEnum implements SqlConformance {
 
   public boolean allowNiladicParentheses() {
     switch (this) {
+    case BABEL:
     case LENIENT:
     case MYSQL_5:
       return true;
@@ -222,6 +247,7 @@ public enum SqlConformanceEnum implements SqlConformance {
 
   public boolean allowExtend() {
     switch (this) {
+    case BABEL:
     case LENIENT:
       return true;
     default:
@@ -231,6 +257,7 @@ public enum SqlConformanceEnum implements SqlConformance {
 
   public boolean isLimitStartCountAllowed() {
     switch (this) {
+    case BABEL:
     case LENIENT:
     case MYSQL_5:
       return true;
@@ -241,6 +268,7 @@ public enum SqlConformanceEnum implements SqlConformance {
 
   public boolean allowGeometry() {
     switch (this) {
+    case BABEL:
     case LENIENT:
     case MYSQL_5:
     case SQL_SERVER_2008:

http://git-wip-us.apache.org/repos/asf/calcite/blob/3e50a532/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
----------------------------------------------------------------------
diff --git a/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties b/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
index d1ea199..bf83769 100644
--- a/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
+++ b/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
@@ -245,4 +245,5 @@ ViewExists=View ''{0}'' already exists and REPLACE not specified
 SchemaNotFound=Schema ''{0}'' not found
 ViewNotFound=View ''{0}'' not found
 TypeNotFound=Type ''{0}'' not found
+DialectDoesNotSupportFeature=Dialect does not support feature: ''{0}''
 # End CalciteResource.properties

http://git-wip-us.apache.org/repos/asf/calcite/blob/3e50a532/core/src/test/java/org/apache/calcite/test/CalciteAssert.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/CalciteAssert.java b/core/src/test/java/org/apache/calcite/test/CalciteAssert.java
index 9056ecc..171fed4 100644
--- a/core/src/test/java/org/apache/calcite/test/CalciteAssert.java
+++ b/core/src/test/java/org/apache/calcite/test/CalciteAssert.java
@@ -670,35 +670,31 @@ public class CalciteAssert {
   }
 
   public static SchemaPlus addSchema(SchemaPlus rootSchema, SchemaSpec schema) {
-    SchemaPlus foodmart;
-    SchemaPlus jdbcScott;
+    final SchemaPlus foodmart;
+    final SchemaPlus jdbcScott;
     final ConnectionSpec cs;
     final DataSource dataSource;
     switch (schema) {
     case REFLECTIVE_FOODMART:
-      return rootSchema.add("foodmart",
+      return rootSchema.add(schema.schemaName,
           new ReflectiveSchema(new JdbcTest.FoodmartSchema()));
     case JDBC_SCOTT:
       cs = DatabaseInstance.HSQLDB.scott;
       dataSource = JdbcSchema.dataSource(cs.url, cs.driver, cs.username,
           cs.password);
-      return rootSchema.add("JDBC_SCOTT",
-          JdbcSchema.create(rootSchema, "JDBC_SCOTT", dataSource, cs.catalog,
-              cs.schema));
+      return rootSchema.add(schema.schemaName,
+          JdbcSchema.create(rootSchema, schema.schemaName, dataSource,
+              cs.catalog, cs.schema));
     case JDBC_FOODMART:
       cs = DB.foodmart;
       dataSource =
           JdbcSchema.dataSource(cs.url, cs.driver, cs.username, cs.password);
-      return rootSchema.add("foodmart",
-          JdbcSchema.create(rootSchema, "foodmart", dataSource, cs.catalog,
-              cs.schema));
+      return rootSchema.add(schema.schemaName,
+          JdbcSchema.create(rootSchema, schema.schemaName, dataSource,
+              cs.catalog, cs.schema));
     case JDBC_FOODMART_WITH_LATTICE:
-      foodmart = rootSchema.getSubSchema("foodmart");
-      if (foodmart == null) {
-        foodmart =
-            CalciteAssert.addSchema(rootSchema, SchemaSpec.JDBC_FOODMART);
-      }
-      foodmart.add("lattice",
+      foodmart = addSchemaIfNotExists(rootSchema, SchemaSpec.JDBC_FOODMART);
+      foodmart.add(schema.schemaName,
           Lattice.create(foodmart.unwrap(CalciteSchema.class),
               "select 1 from \"foodmart\".\"sales_fact_1997\" as s\n"
                   + "join \"foodmart\".\"time_by_day\" as t using (\"time_id\")\n"
@@ -708,23 +704,16 @@ public class CalciteAssert {
               true));
       return foodmart;
     case SCOTT:
-      jdbcScott = rootSchema.getSubSchema("jdbc_scott");
-      if (jdbcScott == null) {
-        jdbcScott =
-            CalciteAssert.addSchema(rootSchema, SchemaSpec.JDBC_SCOTT);
-      }
-      return rootSchema.add("scott", new CloneSchema(jdbcScott));
+      jdbcScott = addSchemaIfNotExists(rootSchema, SchemaSpec.JDBC_SCOTT);
+      return rootSchema.add(schema.schemaName, new CloneSchema(jdbcScott));
     case CLONE_FOODMART:
-      foodmart = rootSchema.getSubSchema("foodmart");
-      if (foodmart == null) {
-        foodmart =
-            CalciteAssert.addSchema(rootSchema, SchemaSpec.JDBC_FOODMART);
-      }
+      foodmart = addSchemaIfNotExists(rootSchema, SchemaSpec.JDBC_FOODMART);
       return rootSchema.add("foodmart2", new CloneSchema(foodmart));
     case GEO:
       ModelHandler.addFunctions(rootSchema, null, ImmutableList.of(),
           GeoFunctions.class.getName(), "*", true);
-      final SchemaPlus s = rootSchema.add("GEO", new AbstractSchema());
+      final SchemaPlus s =
+          rootSchema.add(schema.schemaName, new AbstractSchema());
       ModelHandler.addFunctions(s, "countries", ImmutableList.of(),
           CountriesTableFunction.class.getName(), null, false);
       final String sql = "select * from table(\"countries\"(true))";
@@ -733,21 +722,23 @@ public class CalciteAssert {
       s.add("countries", viewMacro);
       return s;
     case HR:
-      return rootSchema.add("hr",
+      return rootSchema.add(schema.schemaName,
           new ReflectiveSchema(new JdbcTest.HrSchema()));
     case LINGUAL:
-      return rootSchema.add("SALES",
+      return rootSchema.add(schema.schemaName,
           new ReflectiveSchema(new JdbcTest.LingualSchema()));
     case BLANK:
-      return rootSchema.add("BLANK", new AbstractSchema());
+      return rootSchema.add(schema.schemaName, new AbstractSchema());
     case ORINOCO:
-      final SchemaPlus orinoco = rootSchema.add("ORINOCO", new AbstractSchema());
+      final SchemaPlus orinoco =
+          rootSchema.add(schema.schemaName, new AbstractSchema());
       orinoco.add("ORDERS",
           new StreamTest.OrdersHistoryTable(
               StreamTest.OrdersStreamTableFactory.getRowList()));
       return orinoco;
     case POST:
-      final SchemaPlus post = rootSchema.add("POST", new AbstractSchema());
+      final SchemaPlus post =
+          rootSchema.add(schema.schemaName, new AbstractSchema());
       post.add("EMP",
           ViewTable.viewMacro(post,
               "select * from (values\n"
@@ -789,6 +780,15 @@ public class CalciteAssert {
     }
   }
 
+  private static SchemaPlus addSchemaIfNotExists(SchemaPlus rootSchema,
+        SchemaSpec schemaSpec) {
+    final SchemaPlus schema = rootSchema.getSubSchema(schemaSpec.schemaName);
+    if (schema != null) {
+      return schema;
+    }
+    return addSchema(rootSchema, schemaSpec);
+  }
+
   /**
    * Asserts that two objects are equal. If they are not, an
    * {@link AssertionError} is thrown with the given message. If
@@ -909,6 +909,12 @@ public class CalciteAssert {
           connectionFactory.with(new AddSchemaPostProcessor(name, schema)));
     }
 
+    /** Sets the default schema of the connection. Schema name may be null. */
+    public AssertThat withDefaultSchema(String schema) {
+      return new AssertThat(
+          connectionFactory.with(new DefaultSchemaPostProcessor(schema)));
+    }
+
     public AssertThat with(ConnectionPostProcessor postProcessor) {
       return new AssertThat(connectionFactory.with(postProcessor));
     }
@@ -1032,12 +1038,6 @@ public class CalciteAssert {
       }
     }
 
-    public AssertThat withDefaultSchema(String schema) {
-      return new AssertThat(
-          connectionFactory.with(
-              new AddSchemaPostProcessor(schema, null)));
-    }
-
     /** Use sparingly. Does not close the connection. */
     public Connection connect() throws SQLException {
       return connectionFactory.createConnection();
@@ -1100,8 +1100,8 @@ public class CalciteAssert {
     private final Schema schema;
 
     public AddSchemaPostProcessor(String name, Schema schema) {
-      this.name = name;
-      this.schema = schema;
+      this.name = Objects.requireNonNull(name);
+      this.schema = Objects.requireNonNull(schema);
     }
 
     public Connection apply(Connection connection) throws SQLException {
@@ -1115,6 +1115,21 @@ public class CalciteAssert {
     }
   }
 
+  /** Sets a default schema name. */
+  public static class DefaultSchemaPostProcessor
+      implements ConnectionPostProcessor {
+    private final String name;
+
+    public DefaultSchemaPostProcessor(String name) {
+      this.name = name;
+    }
+
+    public Connection apply(Connection connection) throws SQLException {
+      connection.setSchema(name);
+      return connection;
+    }
+  }
+
   /** Adds {@link SchemaSpec} (set of schemes) to a connection. */
   public static class AddSchemaSpecPostProcessor
       implements ConnectionPostProcessor {
@@ -1135,9 +1150,7 @@ public class CalciteAssert {
       default:
         addSchema(rootSchema, schemaSpec);
       }
-      if (schemaSpec == SchemaSpec.CLONE_FOODMART) {
-        con.setSchema("foodmart2");
-      }
+      con.setSchema(schemaSpec.schemaName);
       return connection;
     }
   }
@@ -1755,18 +1768,26 @@ public class CalciteAssert {
 
   /** Specification for common test schemas. */
   public enum SchemaSpec {
-    REFLECTIVE_FOODMART,
-    JDBC_FOODMART,
-    CLONE_FOODMART,
-    JDBC_FOODMART_WITH_LATTICE,
-    GEO,
-    HR,
-    JDBC_SCOTT,
-    SCOTT,
-    BLANK,
-    LINGUAL,
-    POST,
-    ORINOCO
+    REFLECTIVE_FOODMART("foodmart"),
+    JDBC_FOODMART("foodmart"),
+    CLONE_FOODMART("foodmart2"),
+    JDBC_FOODMART_WITH_LATTICE("lattice"),
+    GEO("GEO"),
+    HR("hr"),
+    JDBC_SCOTT("JDBC_SCOTT"),
+    SCOTT("scott"),
+    BLANK("BLANK"),
+    LINGUAL("SALES"),
+    POST("POST"),
+    ORINOCO("ORINOCO");
+
+    /** The name of the schema that is usually created from this specification.
+     * (Names are not unique, and you can use another name if you wish.) */
+    public final String schemaName;
+
+    SchemaSpec(String schemaName) {
+      this.schemaName = schemaName;
+    }
   }
 
   /** Converts a {@link ResultSet} to string. */

http://git-wip-us.apache.org/repos/asf/calcite/blob/3e50a532/core/src/test/java/org/apache/calcite/test/JdbcFrontJdbcBackTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/JdbcFrontJdbcBackTest.java b/core/src/test/java/org/apache/calcite/test/JdbcFrontJdbcBackTest.java
index a0a753c..3f9ec26 100644
--- a/core/src/test/java/org/apache/calcite/test/JdbcFrontJdbcBackTest.java
+++ b/core/src/test/java/org/apache/calcite/test/JdbcFrontJdbcBackTest.java
@@ -137,7 +137,6 @@ public class JdbcFrontJdbcBackTest {
   @Test public void testCase() {
     that()
         .with(CalciteAssert.Config.JDBC_FOODMART)
-        .withDefaultSchema("foodmart")
         .query("select\n"
             + "  case when \"sales_fact_1997\".\"promotion_id\" = 1 then 0\n"
             + "  else \"sales_fact_1997\".\"store_sales\" end as \"c0\"\n"

http://git-wip-us.apache.org/repos/asf/calcite/blob/3e50a532/core/src/test/java/org/apache/calcite/test/QuidemTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/QuidemTest.java b/core/src/test/java/org/apache/calcite/test/QuidemTest.java
index e70c7e9..6bf727f 100644
--- a/core/src/test/java/org/apache/calcite/test/QuidemTest.java
+++ b/core/src/test/java/org/apache/calcite/test/QuidemTest.java
@@ -35,6 +35,7 @@ import org.apache.calcite.util.Util;
 import com.google.common.collect.Lists;
 import com.google.common.io.PatternFilenameFilter;
 
+import net.hydromatic.quidem.CommandHandler;
 import net.hydromatic.quidem.Quidem;
 
 import org.junit.Test;
@@ -152,7 +153,11 @@ public abstract class QuidemTest {
     try (final Reader reader = Util.reader(inFile);
          final Writer writer = Util.printWriter(outFile);
          final Closer closer = new Closer()) {
-      new Quidem(reader, writer, QuidemTest::getEnv, createConnectionFactory())
+      final Quidem.Config config = Quidem.configBuilder()
+          .withReader(reader)
+          .withWriter(writer)
+          .withConnectionFactory(createConnectionFactory())
+          .withCommandHandler(createCommandHandler())
           .withPropertyHandler((propertyName, value) -> {
             if (propertyName.equals("bindable")) {
               final boolean b = value instanceof Boolean
@@ -165,7 +170,9 @@ public abstract class QuidemTest {
               closer.add(Prepare.THREAD_EXPAND.push(b));
             }
           })
-          .execute();
+          .withEnv(QuidemTest::getEnv)
+          .build();
+      new Quidem(config).execute();
     }
     final String diff = DiffTestCase.diff(inFile, outFile);
     if (!diff.isEmpty()) {
@@ -174,6 +181,11 @@ public abstract class QuidemTest {
     }
   }
 
+  /** Creates a command handler. */
+  protected CommandHandler createCommandHandler() {
+    return Quidem.EMPTY_COMMAND_HANDLER;
+  }
+
   /** Creates a connection factory. */
   protected Quidem.ConnectionFactory createConnectionFactory() {
     return new QuidemConnectionFactory();
@@ -246,7 +258,6 @@ public abstract class QuidemTest {
         return CalciteAssert.that()
             .with(CalciteAssert.Config.REGULAR)
             .with(CalciteAssert.SchemaSpec.POST)
-            .withDefaultSchema("POST")
             .connect();
       case "catchall":
         return CalciteAssert.that()
@@ -257,7 +268,6 @@ public abstract class QuidemTest {
       case "orinoco":
         return CalciteAssert.that()
             .with(CalciteAssert.SchemaSpec.ORINOCO)
-            .withDefaultSchema("ORINOCO")
             .connect();
       case "blank":
         return CalciteAssert.that()
@@ -265,7 +275,6 @@ public abstract class QuidemTest {
                 "org.apache.calcite.sql.parser.parserextensiontesting"
                     + ".ExtensionSqlParserImpl#FACTORY")
             .with(CalciteAssert.SchemaSpec.BLANK)
-            .withDefaultSchema("BLANK")
             .connect();
       case "seq":
         final Connection connection = CalciteAssert.that()

http://git-wip-us.apache.org/repos/asf/calcite/blob/3e50a532/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 8ac7193..bea1327 100644
--- a/pom.xml
+++ b/pom.xml
@@ -136,6 +136,18 @@ limitations under the License.
     <xerces.version>2.9.1</xerces.version>
     <sketches.version>0.9.0</sketches.version>
     <fmpp.version>0.9.14</fmpp.version>
+
+    <!-- Other properties (not version numbers) -->
+    <maven-javadoc-plugin.excludePackageNames>
+      org.apache.calcite.benchmarks.generated,
+      org.apache.calcite.sql.parser.babel,
+      org.apache.calcite.sql.parser.ddl,
+      org.apache.calcite.sql.parser.impl,
+      org.apache.calcite.sql.parser.parserextensiontesting,
+      org.apache.calcite.piglet.parser,
+      org.openjdk.jmh,org.apache.calcite.adapter.elasticsearch2
+    </maven-javadoc-plugin.excludePackageNames>
+    <maven-javadoc-plugin.link>https://docs.oracle.com/javase/9/docs/api/</maven-javadoc-plugin.link>
   </properties>
 
   <issueManagement>
@@ -151,6 +163,7 @@ limitations under the License.
   </scm>
 
   <modules>
+    <module>babel</module>
     <module>cassandra</module>
     <module>core</module>
     <module>druid</module>
@@ -720,9 +733,9 @@ limitations under the License.
         <configuration>
           <additionalparam>-html5</additionalparam>
           <links>
-            <link>https://docs.oracle.com/javase/9/docs/api/</link>
+            <link>${maven-javadoc-plugin.link}</link>
           </links>
-          <excludePackageNames>org.apache.calcite.benchmarks.generated,org.apache.calcite.sql.parser.ddl,org.apache.calcite.sql.parser.impl,org.apache.calcite.sql.parser.parserextensiontesting,org.apache.calcite.piglet.parser,org.openjdk.jmh,org.apache.calcite.adapter.elasticsearch2</excludePackageNames>
+          <excludePackageNames>${maven-javadoc-plugin.excludePackageNames}</excludePackageNames>
           <show>private</show>
         </configuration>
       </plugin>
@@ -960,9 +973,9 @@ limitations under the License.
         <version>${maven-javadoc-plugin.version}</version>
         <configuration>
           <links>
-            <link>https://docs.oracle.com/javase/8/docs/api/</link>
+            <link>${maven-javadoc-plugin.link}</link>
           </links>
-          <excludePackageNames>org.apache.calcite.benchmarks.generated,org.apache.calcite.sql.parser.ddl,org.apache.calcite.sql.parser.impl,org.apache.calcite.sql.parser.parserextensiontesting,org.apache.calcite.piglet.parser,org.openjdk.jmh,org.apache.calcite.adapter.elasticsearch2</excludePackageNames>
+          <excludePackageNames>${maven-javadoc-plugin.excludePackageNames}</excludePackageNames>
           <notimestamp>true</notimestamp>
           <windowtitle>Apache Calcite API</windowtitle>
         </configuration>

http://git-wip-us.apache.org/repos/asf/calcite/blob/3e50a532/server/src/test/resources/sql/materialized_view.iq
----------------------------------------------------------------------
diff --git a/server/src/test/resources/sql/materialized_view.iq b/server/src/test/resources/sql/materialized_view.iq
index e587313..0e02a18 100644
--- a/server/src/test/resources/sql/materialized_view.iq
+++ b/server/src/test/resources/sql/materialized_view.iq
@@ -93,12 +93,12 @@ drop materialized view if exists v;
 
 # Create materialized view without AS - fails
 create materialized view d;
-Encountered "<EOF>" at line 1, column 27.
+Encountered "<EOF>" at line 1, column 26.
 !error
 
 # Create materialized view without AS - fails
 create materialized view d (x, y);
-Encountered "<EOF>" at line 1, column 34.
+Encountered "<EOF>" at line 1, column 33.
 !error
 
 # Create materialized view without AS - fails

http://git-wip-us.apache.org/repos/asf/calcite/blob/3e50a532/server/src/test/resources/sql/schema.iq
----------------------------------------------------------------------
diff --git a/server/src/test/resources/sql/schema.iq b/server/src/test/resources/sql/schema.iq
index 65c6396..82ad470 100755
--- a/server/src/test/resources/sql/schema.iq
+++ b/server/src/test/resources/sql/schema.iq
@@ -91,7 +91,7 @@ Encountered "library" at line 1, column 18.
 !error
 
 create foreign schema fs;
-Encountered "<EOF>" at line 1, column 25.
+Encountered "<EOF>" at line 1, column 24.
 Was expecting one of:
     "TYPE" ...
     "LIBRARY" ...