[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/master Commit: 3e50a5325357720d109e5fa300dad7a918087139 Parents: c749f25 Author: Julian Hyde <[email protected]> Authored: Wed May 2 00:03:14 2018 -0700 Committer: Julian Hyde <[email protected]> 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" ...
