| /* |
| * 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.rel.RelNode; |
| import org.apache.calcite.rel.RelRoot; |
| import org.apache.calcite.sql.test.SqlTestFactory; |
| import org.apache.calcite.sql.test.SqlTester; |
| import org.apache.calcite.sql.test.SqlValidatorTester; |
| import org.apache.calcite.sql.util.SqlOperatorTables; |
| import org.apache.calcite.sql.validate.SqlConformance; |
| import org.apache.calcite.sql.validate.SqlValidatorUtil; |
| import org.apache.calcite.sql2rel.SqlToRelConverter; |
| import org.apache.calcite.test.catalog.MockCatalogReaderDynamic; |
| import org.apache.calcite.test.catalog.MockCatalogReaderExtended; |
| import org.apache.calcite.util.TestUtil; |
| |
| import org.checkerframework.checker.nullness.qual.Nullable; |
| |
| import java.util.function.Predicate; |
| import java.util.function.UnaryOperator; |
| |
| import static org.hamcrest.CoreMatchers.containsString; |
| import static org.hamcrest.CoreMatchers.is; |
| import static org.hamcrest.MatcherAssert.assertThat; |
| |
| import static java.util.Objects.requireNonNull; |
| |
| /** |
| * Parameters for a SQL-to-RelNode test. |
| */ |
| public class SqlToRelFixture { |
| public static final SqlTester TESTER = SqlValidatorTester.DEFAULT; |
| |
| public static final SqlToRelFixture DEFAULT = |
| new SqlToRelFixture("?", true, TESTER, SqlTestFactory.INSTANCE, false, |
| false, null) |
| .withFactory(f -> |
| f.withValidator((opTab, catalogReader, typeFactory, config) -> { |
| if (config.conformance().allowGeometry()) { |
| opTab = |
| SqlOperatorTables.chain(opTab, |
| SqlOperatorTables.spatialInstance()); |
| } |
| return SqlValidatorUtil.newValidator(opTab, catalogReader, |
| typeFactory, config.withIdentifierExpansion(true)); |
| }) |
| .withSqlToRelConfig(c -> |
| c.withTrimUnusedFields(true) |
| .withExpand(true) |
| .addRelBuilderConfigTransform(b -> |
| b.withAggregateUnique(true) |
| .withPruneInputOfAggregate(false)))); |
| |
| private final String sql; |
| private final @Nullable DiffRepository diffRepos; |
| private final boolean decorrelate; |
| private final SqlTester tester; |
| private final SqlTestFactory factory; |
| private final boolean trim; |
| private final boolean expression; |
| |
| SqlToRelFixture(String sql, boolean decorrelate, |
| SqlTester tester, SqlTestFactory factory, boolean trim, |
| boolean expression, |
| @Nullable DiffRepository diffRepos) { |
| this.sql = requireNonNull(sql, "sql"); |
| this.tester = requireNonNull(tester, "tester"); |
| this.factory = requireNonNull(factory, "factory"); |
| this.diffRepos = diffRepos; |
| if (sql.contains(" \n")) { |
| throw new AssertionError("trailing whitespace"); |
| } |
| this.decorrelate = decorrelate; |
| this.trim = trim; |
| this.expression = expression; |
| } |
| |
| public void ok() { |
| convertsTo("${plan}"); |
| } |
| |
| public void throws_(String message) { |
| try { |
| ok(); |
| } catch (Throwable throwable) { |
| assertThat(TestUtil.printStackTrace(throwable), containsString(message)); |
| } |
| } |
| |
| public void convertsTo(String plan) { |
| tester.assertConvertsTo(factory, diffRepos(), sql, plan, trim, expression, |
| decorrelate); |
| } |
| |
| public DiffRepository diffRepos() { |
| return DiffRepository.castNonNull(diffRepos); |
| } |
| |
| public SqlToRelFixture withSql(String sql) { |
| return sql.equals(this.sql) ? this |
| : new SqlToRelFixture(sql, decorrelate, tester, factory, trim, |
| expression, diffRepos); |
| } |
| |
| /** |
| * Sets whether this is an expression (as opposed to a whole query). |
| */ |
| public SqlToRelFixture expression(boolean expression) { |
| return this.expression == expression ? this |
| : new SqlToRelFixture(sql, decorrelate, tester, factory, trim, |
| expression, diffRepos); |
| } |
| |
| public SqlToRelFixture withConfig( |
| UnaryOperator<SqlToRelConverter.Config> transform) { |
| return withFactory(f -> f.withSqlToRelConfig(transform)); |
| } |
| |
| public SqlToRelFixture withExpand(boolean expand) { |
| return withConfig(b -> b.withExpand(expand)); |
| } |
| |
| public SqlToRelFixture withDecorrelate(boolean decorrelate) { |
| return new SqlToRelFixture(sql, decorrelate, tester, factory, trim, |
| expression, diffRepos); |
| } |
| |
| public SqlToRelFixture withFactory( |
| UnaryOperator<SqlTestFactory> transform) { |
| final SqlTestFactory factory = transform.apply(this.factory); |
| if (factory == this.factory) { |
| return this; |
| } |
| return new SqlToRelFixture(sql, decorrelate, tester, factory, trim, |
| expression, diffRepos); |
| } |
| |
| public SqlToRelFixture withCatalogReader( |
| SqlTestFactory.CatalogReaderFactory catalogReaderFactory) { |
| return withFactory(f -> f.withCatalogReader(catalogReaderFactory)); |
| } |
| |
| public SqlToRelFixture withExtendedTester() { |
| return withCatalogReader(MockCatalogReaderExtended::create); |
| } |
| |
| public SqlToRelFixture withDynamicTable() { |
| return withCatalogReader(MockCatalogReaderDynamic::create); |
| } |
| |
| public SqlToRelFixture withTrim(boolean trim) { |
| return new SqlToRelFixture(sql, decorrelate, tester, factory, trim, |
| expression, diffRepos); |
| } |
| |
| public SqlConformance getConformance() { |
| return factory.parserConfig().conformance(); |
| } |
| |
| public SqlToRelFixture withConformance(SqlConformance conformance) { |
| return withFactory(f -> |
| f.withParserConfig(c -> c.withConformance(conformance)) |
| .withValidatorConfig(c -> c.withConformance(conformance))); |
| } |
| |
| public SqlToRelFixture withDiffRepos(DiffRepository diffRepos) { |
| return new SqlToRelFixture(sql, decorrelate, tester, factory, trim, |
| expression, diffRepos); |
| } |
| |
| public RelRoot toRoot() { |
| return tester |
| .convertSqlToRel(factory, sql, decorrelate, trim); |
| } |
| |
| public RelNode toRel() { |
| return toRoot().rel; |
| } |
| |
| /** Returns a fixture that meets a given condition, applying a remedy if it |
| * does not already. */ |
| public SqlToRelFixture ensuring(Predicate<SqlToRelFixture> predicate, |
| UnaryOperator<SqlToRelFixture> remedy) { |
| SqlToRelFixture f = this; |
| if (!predicate.test(f)) { |
| f = remedy.apply(f); |
| assertThat("remedy failed", predicate.test(f), is(true)); |
| } |
| return f; |
| } |
| } |