| /* |
| * 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.type.RelDataType; |
| import org.apache.calcite.rel.type.RelDataTypeField; |
| import org.apache.calcite.sql.SqlNode; |
| import org.apache.calcite.sql.SqlOperator; |
| import org.apache.calcite.sql.parser.StringAndPos; |
| import org.apache.calcite.sql.test.ResultCheckers; |
| import org.apache.calcite.sql.test.SqlOperatorFixture; |
| import org.apache.calcite.sql.test.SqlTestFactory; |
| import org.apache.calcite.sql.test.SqlTester; |
| import org.apache.calcite.sql.test.SqlTests; |
| import org.apache.calcite.sql.test.SqlValidatorTester; |
| import org.apache.calcite.sql.validate.SqlValidator; |
| import org.apache.calcite.util.JdbcType; |
| |
| import org.checkerframework.checker.nullness.qual.Nullable; |
| import org.hamcrest.Matcher; |
| |
| import java.util.List; |
| import java.util.function.UnaryOperator; |
| |
| import static org.apache.calcite.sql.test.ResultCheckers.isNullValue; |
| import static org.apache.calcite.sql.test.ResultCheckers.isSingle; |
| |
| import static org.hamcrest.CoreMatchers.is; |
| import static org.hamcrest.MatcherAssert.assertThat; |
| import static org.junit.jupiter.api.Assertions.assertEquals; |
| import static org.junit.jupiter.api.Assertions.assertNotNull; |
| |
| import static java.util.Objects.requireNonNull; |
| |
| /** |
| * Implementation of {@link SqlOperatorFixture}. |
| */ |
| class SqlOperatorFixtureImpl implements SqlOperatorFixture { |
| public static final SqlOperatorFixtureImpl DEFAULT = |
| new SqlOperatorFixtureImpl(SqlTestFactory.INSTANCE, |
| SqlValidatorTester.DEFAULT, false); |
| |
| private final SqlTestFactory factory; |
| private final SqlTester tester; |
| private final boolean brokenTestsEnabled; |
| |
| SqlOperatorFixtureImpl(SqlTestFactory factory, SqlTester tester, |
| boolean brokenTestsEnabled) { |
| this.factory = requireNonNull(factory, "factory"); |
| this.tester = requireNonNull(tester, "tester"); |
| this.brokenTestsEnabled = brokenTestsEnabled; |
| } |
| |
| @Override public void close() { |
| } |
| |
| @Override public SqlTestFactory getFactory() { |
| return factory; |
| } |
| |
| @Override public SqlTester getTester() { |
| return tester; |
| } |
| |
| @Override public SqlOperatorFixture withFactory( |
| UnaryOperator<SqlTestFactory> transform) { |
| final SqlTestFactory factory = transform.apply(this.factory); |
| if (factory == this.factory) { |
| return this; |
| } |
| return new SqlOperatorFixtureImpl(factory, tester, brokenTestsEnabled); |
| } |
| |
| @Override public SqlOperatorFixture withTester( |
| UnaryOperator<SqlTester> transform) { |
| final SqlTester tester = transform.apply(this.tester); |
| if (tester == this.tester) { |
| return this; |
| } |
| return new SqlOperatorFixtureImpl(factory, tester, brokenTestsEnabled); |
| } |
| |
| @Override public boolean brokenTestsEnabled() { |
| return brokenTestsEnabled; |
| } |
| |
| @Override public SqlOperatorFixture withBrokenTestsEnabled( |
| boolean brokenTestsEnabled) { |
| if (brokenTestsEnabled == this.brokenTestsEnabled) { |
| return this; |
| } |
| return new SqlOperatorFixtureImpl(factory, tester, brokenTestsEnabled); |
| } |
| |
| @Override public SqlOperatorFixture setFor(SqlOperator operator, |
| VmName... unimplementedVmNames) { |
| return this; |
| } |
| |
| SqlNode parseAndValidate(SqlValidator validator, String sql) { |
| SqlNode sqlNode; |
| try { |
| sqlNode = tester.parseQuery(factory, sql); |
| } catch (Throwable e) { |
| throw new RuntimeException("Error while parsing query: " + sql, e); |
| } |
| return validator.validate(sqlNode); |
| } |
| |
| @Override public void checkColumnType(String sql, String expected) { |
| tester.validateAndThen(factory, StringAndPos.of(sql), |
| checkColumnTypeAction(is(expected))); |
| } |
| |
| @Override public void checkType(String expression, String type) { |
| forEachQueryValidateAndThen(StringAndPos.of(expression), |
| checkColumnTypeAction(is(type))); |
| } |
| |
| private static SqlTester.ValidatedNodeConsumer checkColumnTypeAction( |
| Matcher<String> matcher) { |
| return (sql, validator, validatedNode) -> { |
| final RelDataType rowType = |
| validator.getValidatedNodeType(validatedNode); |
| final List<RelDataTypeField> fields = rowType.getFieldList(); |
| assertEquals(1, fields.size(), "expected query to return 1 field"); |
| final RelDataType actualType = fields.get(0).getType(); |
| String actual = SqlTests.getTypeString(actualType); |
| assertThat("Query: " + sql.sql, actual, matcher); |
| }; |
| } |
| |
| @Override public void checkQuery(String sql) { |
| tester.assertExceptionIsThrown(factory, StringAndPos.of(sql), null); |
| } |
| |
| void forEachQueryValidateAndThen(StringAndPos expression, |
| SqlTester.ValidatedNodeConsumer consumer) { |
| tester.forEachQuery(factory, expression.addCarets(), query -> |
| tester.validateAndThen(factory, StringAndPos.of(query), consumer)); |
| } |
| |
| @Override public void checkFails(StringAndPos sap, String expectedError, |
| boolean runtime) { |
| final String sql = "values (" + sap.addCarets() + ")"; |
| if (runtime) { |
| // We need to test that the expression fails at runtime. |
| // Ironically, that means that it must succeed at prepare time. |
| SqlValidator validator = factory.createValidator(); |
| SqlNode n = parseAndValidate(validator, sql); |
| assertNotNull(n); |
| tester.checkFails(factory, sap, expectedError, runtime); |
| } else { |
| checkQueryFails(StringAndPos.of(sql), |
| expectedError); |
| } |
| } |
| |
| @Override public void checkQueryFails(StringAndPos sap, |
| String expectedError) { |
| tester.assertExceptionIsThrown(factory, sap, expectedError); |
| } |
| |
| @Override public void checkAggFails( |
| String expr, |
| String[] inputValues, |
| String expectedError, |
| boolean runtime) { |
| final String sql = |
| SqlTests.generateAggQuery(expr, inputValues); |
| if (runtime) { |
| SqlValidator validator = factory.createValidator(); |
| SqlNode n = parseAndValidate(validator, sql); |
| assertNotNull(n); |
| tester.checkAggFails(factory, expr, inputValues, expectedError, runtime); |
| } else { |
| checkQueryFails(StringAndPos.of(sql), expectedError); |
| } |
| } |
| |
| @Override public void checkAgg(String expr, String[] inputValues, |
| SqlTester.ResultChecker checker) { |
| String query = |
| SqlTests.generateAggQuery(expr, inputValues); |
| tester.check(factory, query, SqlTests.ANY_TYPE_CHECKER, checker); |
| } |
| |
| @Override public void checkAggWithMultipleArgs( |
| String expr, |
| String[][] inputValues, |
| SqlTester.ResultChecker resultChecker) { |
| String query = |
| SqlTests.generateAggQueryWithMultipleArgs(expr, inputValues); |
| tester.check(factory, query, SqlTests.ANY_TYPE_CHECKER, resultChecker); |
| } |
| |
| @Override public void checkWinAgg( |
| String expr, |
| String[] inputValues, |
| String windowSpec, |
| String type, |
| SqlTester.ResultChecker resultChecker) { |
| String query = |
| SqlTests.generateWinAggQuery(expr, windowSpec, inputValues); |
| tester.check(factory, query, SqlTests.ANY_TYPE_CHECKER, resultChecker); |
| } |
| |
| @Override public void checkScalar(String expression, |
| SqlTester.TypeChecker typeChecker, |
| SqlTester.ResultChecker resultChecker) { |
| tester.forEachQuery(factory, expression, sql -> |
| tester.check(factory, sql, typeChecker, resultChecker)); |
| } |
| |
| @Override public void checkScalarExact(String expression, |
| String expectedType, SqlTester.ResultChecker resultChecker) { |
| final SqlTester.TypeChecker typeChecker = |
| new SqlTests.StringTypeChecker(expectedType); |
| tester.forEachQuery(factory, expression, sql -> |
| tester.check(factory, sql, typeChecker, resultChecker)); |
| } |
| |
| @Override public void checkScalarApprox( |
| String expression, |
| String expectedType, |
| Object result) { |
| SqlTester.TypeChecker typeChecker = |
| new SqlTests.StringTypeChecker(expectedType); |
| final SqlTester.ResultChecker checker = ResultCheckers.createChecker(result); |
| tester.forEachQuery(factory, expression, sql -> |
| tester.check(factory, sql, typeChecker, checker)); |
| } |
| |
| @Override public void checkBoolean( |
| String expression, |
| @Nullable Boolean result) { |
| if (null == result) { |
| checkNull(expression); |
| } else { |
| SqlTester.ResultChecker resultChecker = |
| ResultCheckers.createChecker(is(result), JdbcType.BOOLEAN); |
| tester.forEachQuery(factory, expression, sql -> |
| tester.check(factory, sql, SqlTests.BOOLEAN_TYPE_CHECKER, |
| SqlTests.ANY_PARAMETER_CHECKER, resultChecker)); |
| } |
| } |
| |
| @Override public void checkString( |
| String expression, |
| String result, |
| String expectedType) { |
| SqlTester.TypeChecker typeChecker = |
| new SqlTests.StringTypeChecker(expectedType); |
| SqlTester.ResultChecker resultChecker = isSingle(result); |
| tester.forEachQuery(factory, expression, sql -> |
| tester.check(factory, sql, typeChecker, resultChecker)); |
| } |
| |
| @Override public void checkNull(String expression) { |
| tester.forEachQuery(factory, expression, sql -> |
| tester.check(factory, sql, SqlTests.ANY_TYPE_CHECKER, isNullValue())); |
| } |
| } |