
org.apache.calcite.sql.test.SqlTester Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.calcite.sql.test;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.RelRoot;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.sql.SqlNode;
import org.apache.calcite.sql.SqlOperator;
import org.apache.calcite.sql.parser.SqlParseException;
import org.apache.calcite.sql.parser.StringAndPos;
import org.apache.calcite.sql.validate.SqlValidator;
import org.apache.calcite.test.DiffRepository;
import org.apache.calcite.util.Pair;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.sql.ResultSet;
import java.util.function.Consumer;
import static java.util.Objects.requireNonNull;
/**
* Callback for testing SQL queries and expressions.
*
* The idea is that when you define an operator (or another piece of SQL
* functionality), you can define the logical behavior of that operator once, as
* part of that operator. Later you can define one or more physical
* implementations of that operator, and test them all using the same set of
* tests.
*
*
Specific implementations of SqlTester
might evaluate the
* queries in different ways, for example, using a C++ versus Java calculator.
* An implementation might even ignore certain calls altogether.
*/
public interface SqlTester extends AutoCloseable {
//~ Enums ------------------------------------------------------------------
/**
* Name of a virtual machine that can potentially implement an operator.
*/
enum VmName {
FENNEL, JAVA, EXPAND
}
//~ Methods ----------------------------------------------------------------
/** Given a scalar expression, generates a sequence of SQL queries that
* evaluate it, and calls a given action with each.
*
* @param factory Factory
* @param expression Scalar expression
* @param consumer Action to be called for each query
*/
void forEachQuery(SqlTestFactory factory, String expression,
Consumer consumer);
/** Parses a query. */
SqlNode parseQuery(SqlTestFactory factory, String sql)
throws SqlParseException;
/** Parses an expression. */
SqlNode parseExpression(SqlTestFactory factory, String expr)
throws SqlParseException;
/** Parses and validates a query, then calls an action on the result. */
void validateAndThen(SqlTestFactory factory, StringAndPos sap,
ValidatedNodeConsumer consumer);
/** Parses and validates a query, then calls a function on the result. */
R validateAndApply(SqlTestFactory factory, StringAndPos sap,
ValidatedNodeFunction function);
/**
* Checks that a query is valid, or, if invalid, throws the right
* message at the right location.
*
* If expectedMsgPattern
is null, the query must
* succeed.
*
*
If expectedMsgPattern
is not null, the query must
* fail, and give an error location of (expectedLine, expectedColumn)
* through (expectedEndLine, expectedEndColumn).
*
* @param factory Factory
* @param sap SQL statement
* @param expectedMsgPattern If this parameter is null the query must be
*/
void assertExceptionIsThrown(SqlTestFactory factory, StringAndPos sap,
@Nullable String expectedMsgPattern);
/**
* Returns the data type of the sole column of a SQL query.
*
*
For example, getResultType("VALUES (1")
returns
* INTEGER
.
*
*
Fails if query returns more than one column.
*
* @see #getResultType(SqlTestFactory, String)
*/
RelDataType getColumnType(SqlTestFactory factory, String sql);
/**
* Returns the data type of the row returned by a SQL query.
*
*
For example, getResultType("VALUES (1, 'foo')")
* returns RecordType(INTEGER EXPR$0, CHAR(3) EXPR#1)
.
*/
RelDataType getResultType(SqlTestFactory factory, String sql);
/**
* Checks that a query returns one column of an expected type. For example,
* checkType("VALUES (1 + 2)", "INTEGER NOT NULL")
.
*
* @param factory Factory
* @param sql Query expression
* @param type Type string
*/
void checkColumnType(SqlTestFactory factory,
String sql,
String type);
/**
* Tests that a SQL query returns a single column with the given type. For
* example,
*
*
* check("VALUES (1 + 2)", "3", SqlTypeName.Integer);
*
*
* If result
is null, the expression must yield the SQL NULL
* value. If result
is a {@link java.util.regex.Pattern}, the
* result must match that pattern.
*
* @param factory Factory
* @param query SQL query
* @param typeChecker Checks whether the result is the expected type
* @param resultChecker Checks whether the result has the expected value
*/
default void check(SqlTestFactory factory,
String query,
TypeChecker typeChecker,
ResultChecker resultChecker) {
check(factory, query, typeChecker, SqlTests.ANY_PARAMETER_CHECKER,
resultChecker);
}
/**
* Tests that a SQL query returns a result of expected type and value.
* Checking of type and value are abstracted using {@link TypeChecker}
* and {@link ResultChecker} functors.
*
* @param factory Factory
* @param query SQL query
* @param typeChecker Checks whether the result is the expected type
* @param parameterChecker Checks whether the parameters are of expected
* types
* @param resultChecker Checks whether the result has the expected value
*/
void check(SqlTestFactory factory,
String query,
TypeChecker typeChecker,
ParameterChecker parameterChecker,
ResultChecker resultChecker);
/**
* Declares that this test is for a given operator. So we can check that all
* operators are tested.
*
* @param operator Operator
* @param unimplementedVmNames Names of virtual machines for which this
*/
void setFor(
SqlOperator operator,
VmName... unimplementedVmNames);
/**
* Checks that an aggregate expression returns the expected result.
*
*
For example, checkAgg("AVG(DISTINCT x)", new String[] {"2", "3",
* null, "3" }, new Double(2.5), 0);
*
* @param factory Factory
* @param expr Aggregate expression, e.g. {@code SUM(DISTINCT x)}
* @param inputValues Array of input values, e.g. {@code ["1", null, "2"]}
* @param resultChecker Checks whether the result has the expected value
*/
void checkAgg(SqlTestFactory factory,
String expr,
String[] inputValues,
ResultChecker resultChecker);
/**
* Checks that a windowed aggregate expression returns the expected result.
*
*
For example, checkWinAgg("FIRST_VALUE(x)", new String[] {"2",
* "3", null, "3" }, "INTEGER NOT NULL", 2, 0d);
*
* @param factory Factory
* @param expr Aggregate expression, e.g. {@code SUM(DISTINCT x)}
* @param inputValues Array of input values, e.g. {@code ["1", null, "2"]}
* @param type Expected result type
* @param resultChecker Checks whether the result has the expected value
*/
void checkWinAgg(SqlTestFactory factory,
String expr,
String[] inputValues,
String windowSpec,
String type,
ResultChecker resultChecker);
/**
* Tests that an aggregate expression fails at run time.
*
* @param factory Factory
* @param expr An aggregate expression
* @param inputValues Array of input values
* @param expectedError Pattern for expected error
* @param runtime If true, must fail at runtime; if false, must fail at
* validate time
*/
void checkAggFails(SqlTestFactory factory,
String expr,
String[] inputValues,
String expectedError,
boolean runtime);
/**
* Tests that a scalar SQL expression fails at run time.
*
* @param factory Factory
* @param expression SQL scalar expression
* @param expectedError Pattern for expected error. If !runtime, must
* include an error location.
* @param runtime If true, must fail at runtime; if false, must fail at
* validate time
*/
void checkFails(SqlTestFactory factory,
StringAndPos expression,
String expectedError,
boolean runtime);
/** As {@link #checkFails(SqlTestFactory, StringAndPos, String, boolean)},
* but with a string that contains carets. */
default void checkFails(SqlTestFactory factory,
String expression,
String expectedError,
boolean runtime) {
checkFails(factory, StringAndPos.of(expression), expectedError, runtime);
}
/**
* Tests that a SQL query fails at prepare time.
*
* @param factory Factory
* @param sap SQL query and error position
* @param expectedError Pattern for expected error. Must
* include an error location.
*/
void checkQueryFails(SqlTestFactory factory, StringAndPos sap,
String expectedError);
/**
* Converts a SQL string to a {@link RelNode} tree.
*
* @param factory Factory
* @param sql SQL statement
* @param decorrelate Whether to decorrelate
* @param trim Whether to trim
* @return Relational expression, never null
*/
default RelRoot convertSqlToRel(SqlTestFactory factory,
String sql, boolean decorrelate, boolean trim) {
Pair pair =
convertSqlToRel2(factory, sql, decorrelate, trim);
return requireNonNull(pair.right);
}
/** Converts a SQL string to a (SqlValidator, RelNode) pair. */
Pair convertSqlToRel2(SqlTestFactory factory,
String sql, boolean decorrelate, boolean trim);
/**
* Checks that a SQL statement converts to a given plan, optionally
* trimming columns that are not needed.
*
* @param factory Factory
* @param diffRepos Diff repository
* @param sql SQL query or expression
* @param plan Expected plan
* @param trim Whether to trim columns that are not needed
* @param expression True if {@code sql} is an expression, false if it is a query
*/
void assertConvertsTo(SqlTestFactory factory, DiffRepository diffRepos,
String sql,
String plan,
boolean trim,
boolean expression,
boolean decorrelate);
/** Trims a RelNode. */
RelNode trimRelNode(SqlTestFactory factory, RelNode relNode);
//~ Inner Interfaces -------------------------------------------------------
/** Type checker. */
interface TypeChecker {
void checkType(RelDataType type);
}
/** Parameter checker. */
interface ParameterChecker {
void checkParameters(RelDataType parameterRowType);
}
/** Result checker. */
interface ResultChecker {
void checkResult(ResultSet result) throws Exception;
}
/** Action that is called after validation.
*
* @see #validateAndThen
*/
interface ValidatedNodeConsumer {
void accept(StringAndPos sap, SqlValidator validator,
SqlNode validatedNode);
}
/** A function to apply to the result of validation.
*
* @param Result type of the function
*
* @see AbstractSqlTester#validateAndApply */
interface ValidatedNodeFunction {
R apply(StringAndPos sap, SqlValidator validator, SqlNode validatedNode);
}
}