
org.apache.calcite.test.SqlOperatorTest 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.test;
import org.apache.calcite.avatica.util.DateTimeUtils;
import org.apache.calcite.config.CalciteConnectionProperty;
import org.apache.calcite.linq4j.Linq4j;
import org.apache.calcite.plan.Strong;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.runtime.CalciteContextException;
import org.apache.calcite.runtime.CalciteException;
import org.apache.calcite.runtime.Hook;
import org.apache.calcite.sql.SqlAggFunction;
import org.apache.calcite.sql.SqlCall;
import org.apache.calcite.sql.SqlCallBinding;
import org.apache.calcite.sql.SqlFunction;
import org.apache.calcite.sql.SqlIdentifier;
import org.apache.calcite.sql.SqlJdbcFunctionCall;
import org.apache.calcite.sql.SqlLiteral;
import org.apache.calcite.sql.SqlNode;
import org.apache.calcite.sql.SqlNodeList;
import org.apache.calcite.sql.SqlOperandCountRange;
import org.apache.calcite.sql.SqlOperator;
import org.apache.calcite.sql.SqlSyntax;
import org.apache.calcite.sql.dialect.AnsiSqlDialect;
import org.apache.calcite.sql.fun.SqlLibrary;
import org.apache.calcite.sql.fun.SqlLibraryOperators;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
import org.apache.calcite.sql.parser.SqlParseException;
import org.apache.calcite.sql.parser.SqlParserPos;
import org.apache.calcite.sql.pretty.SqlPrettyWriter;
import org.apache.calcite.sql.test.AbstractSqlTester;
import org.apache.calcite.sql.test.SqlOperatorFixture;
import org.apache.calcite.sql.test.SqlOperatorFixture.VmName;
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.type.BasicSqlType;
import org.apache.calcite.sql.type.SqlOperandTypeChecker;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.sql.type.SqlTypeUtil;
import org.apache.calcite.sql.util.SqlString;
import org.apache.calcite.sql.validate.SqlConformanceEnum;
import org.apache.calcite.sql.validate.SqlNameMatchers;
import org.apache.calcite.sql.validate.SqlValidatorImpl;
import org.apache.calcite.sql.validate.SqlValidatorScope;
import org.apache.calcite.util.Bug;
import org.apache.calcite.util.Holder;
import org.apache.calcite.util.Pair;
import org.apache.calcite.util.TestUtil;
import org.apache.calcite.util.TimestampString;
import org.apache.calcite.util.Util;
import org.apache.calcite.util.trace.CalciteTrace;
import com.google.common.base.Throwables;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.TimeZone;
import java.util.function.Consumer;
import java.util.function.UnaryOperator;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import static org.apache.calcite.rel.type.RelDataTypeImpl.NON_NULLABLE_SUFFIX;
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.PI;
import static org.apache.calcite.sql.test.ResultCheckers.isExactly;
import static org.apache.calcite.sql.test.ResultCheckers.isNullValue;
import static org.apache.calcite.sql.test.ResultCheckers.isSet;
import static org.apache.calcite.sql.test.ResultCheckers.isSingle;
import static org.apache.calcite.sql.test.ResultCheckers.isWithin;
import static org.apache.calcite.sql.test.SqlOperatorFixture.BAD_DATETIME_MESSAGE;
import static org.apache.calcite.sql.test.SqlOperatorFixture.DIVISION_BY_ZERO_MESSAGE;
import static org.apache.calcite.sql.test.SqlOperatorFixture.INVALID_ARGUMENTS_NUMBER;
import static org.apache.calcite.sql.test.SqlOperatorFixture.INVALID_CHAR_MESSAGE;
import static org.apache.calcite.sql.test.SqlOperatorFixture.INVALID_EXTRACT_UNIT_CONVERTLET_ERROR;
import static org.apache.calcite.sql.test.SqlOperatorFixture.INVALID_EXTRACT_UNIT_VALIDATION_ERROR;
import static org.apache.calcite.sql.test.SqlOperatorFixture.LITERAL_OUT_OF_RANGE_MESSAGE;
import static org.apache.calcite.sql.test.SqlOperatorFixture.OUT_OF_RANGE_MESSAGE;
import static org.apache.calcite.sql.test.SqlOperatorFixture.STRING_TRUNC_MESSAGE;
import static org.apache.calcite.util.DateTimeStringUtils.getDateFormatter;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
/**
* Contains unit tests for all operators. Each of the methods is named after an
* operator.
*
* To run, you also need an execution mechanism: parse, validate, and execute
* expressions on the operators. This is left to a {@link SqlTester} object
* which is obtained via the {@link #fixture()} method. The default tester
* merely validates calls to operators, but {@code CalciteSqlOperatorTest}
* uses a tester that executes calls and checks that results are valid.
*
*
Different implementations of {@link SqlTester} are possible, such as:
*
*
* - Execute against a JDBC database;
*
- Parse and validate but do not evaluate expressions;
*
- Generate a SQL script;
*
- Analyze which operators are adequately tested.
*
*
* A typical method will be named after the operator it is testing (say
* testSubstringFunc
). It first calls
* {@link SqlOperatorFixture#setFor(SqlOperator, VmName...)}
* to declare which operator it is testing.
*
*
*
* public void testSubstringFunc() {
* tester.setFor(SqlStdOperatorTable.substringFunc);
* tester.checkScalar("sin(0)", "0");
* tester.checkScalar("sin(1.5707)", "1");
* }
*
*
* The rest of the method contains calls to the various {@code checkXxx}
* methods in the {@link SqlTester} interface. For an operator
* to be adequately tested, there need to be tests for:
*
*
* - Parsing all of its the syntactic variants.
*
- Deriving the type of in all combinations of arguments.
*
*
* - Pay particular attention to nullability. For example, the result of the
* "+" operator is NOT NULL if and only if both of its arguments are NOT
* NULL.
* - Also pay attention to precision/scale/length. For example, the maximum
* length of the "||" operator is the sum of the maximum lengths of its
* arguments.
*
*
* - Executing the function. Pay particular attention to corner cases such as
* null arguments or null results.
*
*/
public class SqlOperatorTest {
//~ Static fields/initializers ---------------------------------------------
public static final TesterImpl TESTER = new TesterImpl();
private static final Logger LOGGER =
CalciteTrace.getTestTracer(SqlOperatorTest.class);
public static final boolean TODO = false;
/**
* Regular expression for a SQL TIME(0) value.
*/
public static final Pattern TIME_PATTERN =
Pattern.compile(
"[0-9][0-9]:[0-9][0-9]:[0-9][0-9]");
/**
* Regular expression for a SQL TIMESTAMP(0) value.
*/
public static final Pattern TIMESTAMP_PATTERN =
Pattern.compile(
"[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] "
+ "[0-9][0-9]:[0-9][0-9]:[0-9][0-9]");
/**
* Regular expression for a SQL DATE value.
*/
public static final Pattern DATE_PATTERN =
Pattern.compile(
"[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]");
/** Minimum and maximum values for each exact and approximate numeric
* type. */
enum Numeric {
TINYINT("TINYINT", Long.toString(Byte.MIN_VALUE),
Long.toString(Byte.MIN_VALUE - 1),
Long.toString(Byte.MAX_VALUE),
Long.toString(Byte.MAX_VALUE + 1)),
SMALLINT("SMALLINT", Long.toString(Short.MIN_VALUE),
Long.toString(Short.MIN_VALUE - 1),
Long.toString(Short.MAX_VALUE),
Long.toString(Short.MAX_VALUE + 1)),
INTEGER("INTEGER", Long.toString(Integer.MIN_VALUE),
Long.toString((long) Integer.MIN_VALUE - 1),
Long.toString(Integer.MAX_VALUE),
Long.toString((long) Integer.MAX_VALUE + 1)),
BIGINT("BIGINT", Long.toString(Long.MIN_VALUE),
new BigDecimal(Long.MIN_VALUE).subtract(BigDecimal.ONE).toString(),
Long.toString(Long.MAX_VALUE),
new BigDecimal(Long.MAX_VALUE).add(BigDecimal.ONE).toString()),
DECIMAL5_2("DECIMAL(5, 2)", "-999.99",
"-1000.00", "999.99", "1000.00"),
REAL("REAL", "1E-37", // or Float.toString(Float.MIN_VALUE)
"1e-46", "3.4028234E38", // or Float.toString(Float.MAX_VALUE)
"1e39"),
FLOAT("FLOAT", "2E-307", // or Double.toString(Double.MIN_VALUE)
"1e-324", "1.79769313486231E308", // or Double.toString(Double.MAX_VALUE)
"-1e309"),
DOUBLE("DOUBLE", "2E-307", // or Double.toString(Double.MIN_VALUE)
"1e-324", "1.79769313486231E308", // or Double.toString(Double.MAX_VALUE)
"1e309");
private final String typeName;
/** For Float and Double Java types, MIN_VALUE
* is the smallest positive value, not the smallest negative value.
* For REAL, FLOAT, DOUBLE, Win32 takes smaller values from
* win32_values.h. */
private final String minNumericString;
private final String minOverflowNumericString;
/** For REAL, FLOAT and DOUBLE SQL types (Flaot and Double Java types), we
* use something slightly less than MAX_VALUE because round-tripping string
* to approx to string doesn't preserve MAX_VALUE on win32. */
private final String maxNumericString;
private final String maxOverflowNumericString;
Numeric(String typeName, String minNumericString,
String minOverflowNumericString, String maxNumericString,
String maxOverflowNumericString) {
this.typeName = typeName;
this.minNumericString = minNumericString;
this.minOverflowNumericString = minOverflowNumericString;
this.maxNumericString = maxNumericString;
this.maxOverflowNumericString = maxOverflowNumericString;
}
/** Calls a consumer for each value. Similar effect to a {@code for}
* loop, but the calling line number will show up in the call stack. */
static void forEach(Consumer consumer) {
consumer.accept(TINYINT);
consumer.accept(SMALLINT);
consumer.accept(INTEGER);
consumer.accept(BIGINT);
consumer.accept(DECIMAL5_2);
consumer.accept(REAL);
consumer.accept(FLOAT);
consumer.accept(DOUBLE);
}
double maxNumericAsDouble() {
return Double.parseDouble(maxNumericString);
}
double minNumericAsDouble() {
return Double.parseDouble(minNumericString);
}
}
private static final boolean[] FALSE_TRUE = {false, true};
private static final VmName VM_FENNEL = VmName.FENNEL;
private static final VmName VM_JAVA = VmName.JAVA;
private static final VmName VM_EXPAND = VmName.EXPAND;
protected static final TimeZone UTC_TZ = TimeZone.getTimeZone("GMT");
// time zone for the LOCAL_{DATE,TIME,TIMESTAMP} functions
protected static final TimeZone LOCAL_TZ = TimeZone.getDefault();
// time zone for the CURRENT{DATE,TIME,TIMESTAMP} functions
protected static final TimeZone CURRENT_TZ = LOCAL_TZ;
private static final Pattern INVALID_ARG_FOR_POWER = Pattern.compile(
"(?s).*Invalid argument\\(s\\) for 'POWER' function.*");
private static final Pattern CODE_2201F = Pattern.compile(
"(?s).*could not calculate results for the following row.*PC=5 Code=2201F.*");
/**
* Whether DECIMAL type is implemented.
*/
public static final boolean DECIMAL = false;
/** Function object that returns a string with 2 copies of each character.
* For example, {@code DOUBLER.apply("xy")} returns {@code "xxyy"}. */
private static final UnaryOperator DOUBLER =
new UnaryOperator() {
final Pattern pattern = Pattern.compile("(.)");
@Override public String apply(String s) {
return pattern.matcher(s).replaceAll("$1$1");
}
};
/** Sub-classes should override to run tests in a different environment. */
protected SqlOperatorFixture fixture() {
return SqlOperatorFixtureImpl.DEFAULT;
}
//--- Tests -----------------------------------------------------------
/**
* For development. Put any old code in here.
*/
@Test void testDummy() {
}
@Test void testSqlOperatorOverloading() {
final SqlStdOperatorTable operatorTable = SqlStdOperatorTable.instance();
for (SqlOperator sqlOperator : operatorTable.getOperatorList()) {
String operatorName = sqlOperator.getName();
List routines = new ArrayList<>();
final SqlIdentifier id =
new SqlIdentifier(operatorName, SqlParserPos.ZERO);
operatorTable.lookupOperatorOverloads(id, null, sqlOperator.getSyntax(),
routines, SqlNameMatchers.withCaseSensitive(true));
routines.removeIf(operator ->
!sqlOperator.getClass().isInstance(operator));
assertThat(routines.size(), equalTo(1));
assertThat(sqlOperator, equalTo(routines.get(0)));
}
}
@Test void testBetween() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.BETWEEN, VmName.EXPAND);
f.checkBoolean("2 between 1 and 3", true);
f.checkBoolean("2 between 3 and 2", false);
f.checkBoolean("2 between symmetric 3 and 2", true);
f.checkBoolean("3 between 1 and 3", true);
f.checkBoolean("4 between 1 and 3", false);
f.checkBoolean("1 between 4 and -3", false);
f.checkBoolean("1 between -1 and -3", false);
f.checkBoolean("1 between -1 and 3", true);
f.checkBoolean("1 between 1 and 1", true);
f.checkBoolean("1.5 between 1 and 3", true);
f.checkBoolean("1.2 between 1.1 and 1.3", true);
f.checkBoolean("1.5 between 2 and 3", false);
f.checkBoolean("1.5 between 1.6 and 1.7", false);
f.checkBoolean("1.2e1 between 1.1 and 1.3", false);
f.checkBoolean("1.2e0 between 1.1 and 1.3", true);
f.checkBoolean("1.5e0 between 2 and 3", false);
f.checkBoolean("1.5e0 between 2e0 and 3e0", false);
f.checkBoolean("1.5e1 between 1.6e1 and 1.7e1", false);
f.checkBoolean("x'' between x'' and x''", true);
f.checkNull("cast(null as integer) between -1 and 2");
f.checkNull("1 between -1 and cast(null as integer)");
f.checkNull("1 between cast(null as integer) and cast(null as integer)");
f.checkNull("1 between cast(null as integer) and 1");
f.checkBoolean("x'0A00015A' between x'0A000130' and x'0A0001B0'",
true);
f.checkBoolean("x'0A00015A' between x'0A0001A0' and x'0A0001B0'",
false);
}
@Test void testNotBetween() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.NOT_BETWEEN, VM_EXPAND);
f.checkBoolean("2 not between 1 and 3", false);
f.checkBoolean("3 not between 1 and 3", false);
f.checkBoolean("4 not between 1 and 3", true);
f.checkBoolean("1.2e0 not between 1.1 and 1.3", false);
f.checkBoolean("1.2e1 not between 1.1 and 1.3", true);
f.checkBoolean("1.5e0 not between 2 and 3", true);
f.checkBoolean("1.5e0 not between 2e0 and 3e0", true);
f.checkBoolean("x'0A00015A' not between x'0A000130' and x'0A0001B0'",
false);
f.checkBoolean("x'0A00015A' not between x'0A0001A0' and x'0A0001B0'",
true);
}
@Test void testCastToString() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.CAST, VmName.EXPAND);
f.checkCastToString("cast(cast('abc' as char(4)) as varchar(6))", null,
"abc ");
// integer
f.checkCastToString("123", "CHAR(3)", "123");
f.checkCastToString("0", "CHAR", "0");
f.checkCastToString("-123", "CHAR(4)", "-123");
// decimal
f.checkCastToString("123.4", "CHAR(5)", "123.4");
f.checkCastToString("-0.0", "CHAR(2)", ".0");
f.checkCastToString("-123.4", "CHAR(6)", "-123.4");
f.checkString("cast(1.29 as varchar(10))", "1.29", "VARCHAR(10) NOT NULL");
f.checkString("cast(.48 as varchar(10))", ".48", "VARCHAR(10) NOT NULL");
if (Bug.CALCITE_2539_FIXED) {
f.checkFails("cast(2.523 as char(2))", STRING_TRUNC_MESSAGE, true);
}
f.checkString("cast(-0.29 as varchar(10))",
"-.29", "VARCHAR(10) NOT NULL");
f.checkString("cast(-1.29 as varchar(10))",
"-1.29", "VARCHAR(10) NOT NULL");
// approximate
f.checkCastToString("1.23E45", "CHAR(7)", "1.23E45");
f.checkCastToString("CAST(0 AS DOUBLE)", "CHAR(3)", "0E0");
f.checkCastToString("-1.20e-07", "CHAR(7)", "-1.2E-7");
f.checkCastToString("cast(0e0 as varchar(5))", "CHAR(3)", "0E0");
if (TODO) {
f.checkCastToString("cast(-45e-2 as varchar(17))", "CHAR(7)",
"-4.5E-1");
}
if (TODO) {
f.checkCastToString("cast(4683442.3432498375e0 as varchar(20))",
"CHAR(19)",
"4.683442343249838E6");
}
if (TODO) {
f.checkCastToString("cast(-0.1 as real)", "CHAR(5)", "-1E-1");
}
if (Bug.CALCITE_2539_FIXED) {
f.checkFails("cast(1.3243232e0 as varchar(4))", STRING_TRUNC_MESSAGE,
true);
f.checkFails("cast(1.9e5 as char(4))", STRING_TRUNC_MESSAGE,
true);
}
// string
f.checkCastToString("'abc'", "CHAR(1)", "a");
f.checkCastToString("'abc'", "CHAR(3)", "abc");
f.checkCastToString("cast('abc' as varchar(6))", "CHAR(3)", "abc");
f.checkCastToString("cast(' abc ' as varchar(10))", null, " abc ");
f.checkCastToString("cast(cast('abc' as char(4)) as varchar(6))", null,
"abc ");
f.checkString("cast(cast('a' as char(2)) as varchar(3)) || 'x' ",
"a x", "VARCHAR(4) NOT NULL");
f.checkString("cast(cast('a' as char(3)) as varchar(5)) || 'x' ",
"a x", "VARCHAR(6) NOT NULL");
f.checkString("cast('a' as char(3)) || 'x'", "a x",
"CHAR(4) NOT NULL");
f.checkScalar("char_length(cast(' x ' as char(4)))", 4,
"INTEGER NOT NULL");
f.checkScalar("char_length(cast(' x ' as varchar(3)))", 3,
"INTEGER NOT NULL");
f.checkScalar("char_length(cast(' x ' as varchar(4)))", 3,
"INTEGER NOT NULL");
f.checkScalar("char_length(cast(cast(' x ' as char(4)) as varchar(5)))",
4, "INTEGER NOT NULL");
f.checkScalar("char_length(cast(' x ' as varchar(3)))", 3,
"INTEGER NOT NULL");
// date & time
f.checkCastToString("date '2008-01-01'", "CHAR(10)", "2008-01-01");
f.checkCastToString("time '1:2:3'", "CHAR(8)", "01:02:03");
f.checkCastToString("timestamp '2008-1-1 1:2:3'", "CHAR(19)",
"2008-01-01 01:02:03");
f.checkCastToString("timestamp '2008-1-1 1:2:3'", "VARCHAR(30)",
"2008-01-01 01:02:03");
f.checkCastToString("interval '3-2' year to month", "CHAR(5)", "+3-02");
f.checkCastToString("interval '32' month", "CHAR(3)", "+32");
f.checkCastToString("interval '1 2:3:4' day to second", "CHAR(11)",
"+1 02:03:04");
f.checkCastToString("interval '1234.56' second(4,2)", "CHAR(8)",
"+1234.56");
f.checkCastToString("interval '60' day", "CHAR(8)", "+60 ");
// boolean
f.checkCastToString("True", "CHAR(4)", "TRUE");
f.checkCastToString("True", "CHAR(6)", "TRUE ");
f.checkCastToString("True", "VARCHAR(6)", "TRUE");
f.checkCastToString("False", "CHAR(5)", "FALSE");
if (Bug.CALCITE_2539_FIXED) {
f.checkFails("cast(true as char(3))", INVALID_CHAR_MESSAGE, true);
f.checkFails("cast(false as char(4))", INVALID_CHAR_MESSAGE, true);
f.checkFails("cast(true as varchar(3))", INVALID_CHAR_MESSAGE, true);
f.checkFails("cast(false as varchar(4))", INVALID_CHAR_MESSAGE, true);
}
}
@Test void testCastExactNumericLimits() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.CAST, VmName.EXPAND);
// Test casting for min,max, out of range for exact numeric types
Numeric.forEach(numeric -> {
final String type = numeric.typeName;
switch (numeric) {
case DOUBLE:
case FLOAT:
case REAL:
// Skip approx types
return;
default:
// fall through
}
// Convert from literal to type
f.checkCastToScalarOkay(numeric.maxNumericString, type);
f.checkCastToScalarOkay(numeric.minNumericString, type);
// Overflow test
if (numeric == Numeric.BIGINT) {
// Literal of range
f.checkCastFails(numeric.maxOverflowNumericString,
type, LITERAL_OUT_OF_RANGE_MESSAGE, false);
f.checkCastFails(numeric.minOverflowNumericString,
type, LITERAL_OUT_OF_RANGE_MESSAGE, false);
} else {
if (Bug.CALCITE_2539_FIXED) {
f.checkCastFails(numeric.maxOverflowNumericString,
type, OUT_OF_RANGE_MESSAGE, true);
f.checkCastFails(numeric.minOverflowNumericString,
type, OUT_OF_RANGE_MESSAGE, true);
}
}
// Convert from string to type
f.checkCastToScalarOkay("'" + numeric.maxNumericString + "'",
type, numeric.maxNumericString);
f.checkCastToScalarOkay("'" + numeric.minNumericString + "'",
type, numeric.minNumericString);
if (Bug.CALCITE_2539_FIXED) {
f.checkCastFails("'" + numeric.maxOverflowNumericString + "'",
type, OUT_OF_RANGE_MESSAGE, true);
f.checkCastFails("'" + numeric.minOverflowNumericString + "'",
type, OUT_OF_RANGE_MESSAGE, true);
}
// Convert from type to string
f.checkCastToString(numeric.maxNumericString, null, null);
f.checkCastToString(numeric.maxNumericString, type, null);
f.checkCastToString(numeric.minNumericString, null, null);
f.checkCastToString(numeric.minNumericString, type, null);
if (Bug.CALCITE_2539_FIXED) {
f.checkCastFails("'notnumeric'", type, INVALID_CHAR_MESSAGE, true);
}
});
}
@Test void testCastToExactNumeric() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.CAST, VmName.EXPAND);
f.checkCastToScalarOkay("1", "BIGINT");
f.checkCastToScalarOkay("1", "INTEGER");
f.checkCastToScalarOkay("1", "SMALLINT");
f.checkCastToScalarOkay("1", "TINYINT");
f.checkCastToScalarOkay("1", "DECIMAL(4, 0)");
f.checkCastToScalarOkay("-1", "BIGINT");
f.checkCastToScalarOkay("-1", "INTEGER");
f.checkCastToScalarOkay("-1", "SMALLINT");
f.checkCastToScalarOkay("-1", "TINYINT");
f.checkCastToScalarOkay("-1", "DECIMAL(4, 0)");
f.checkCastToScalarOkay("1.234E3", "INTEGER", "1234");
f.checkCastToScalarOkay("-9.99E2", "INTEGER", "-999");
f.checkCastToScalarOkay("'1'", "INTEGER", "1");
f.checkCastToScalarOkay("' 01 '", "INTEGER", "1");
f.checkCastToScalarOkay("'-1'", "INTEGER", "-1");
f.checkCastToScalarOkay("' -00 '", "INTEGER", "0");
// string to integer
f.checkScalarExact("cast('6543' as integer)", 6543);
f.checkScalarExact("cast(' -123 ' as int)", -123);
f.checkScalarExact("cast('654342432412312' as bigint)",
"BIGINT NOT NULL",
"654342432412312");
}
@Test void testCastStringToDecimal() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.CAST, VmName.EXPAND);
if (!DECIMAL) {
return;
}
// string to decimal
f.checkScalarExact("cast('1.29' as decimal(2,1))",
"DECIMAL(2, 1) NOT NULL",
"1.3");
f.checkScalarExact("cast(' 1.25 ' as decimal(2,1))",
"DECIMAL(2, 1) NOT NULL",
"1.3");
f.checkScalarExact("cast('1.21' as decimal(2,1))",
"DECIMAL(2, 1) NOT NULL",
"1.2");
f.checkScalarExact("cast(' -1.29 ' as decimal(2,1))",
"DECIMAL(2, 1) NOT NULL",
"-1.3");
f.checkScalarExact("cast('-1.25' as decimal(2,1))",
"DECIMAL(2, 1) NOT NULL",
"-1.3");
f.checkScalarExact("cast(' -1.21 ' as decimal(2,1))",
"DECIMAL(2, 1) NOT NULL",
"-1.2");
f.checkFails("cast(' -1.21e' as decimal(2,1))", INVALID_CHAR_MESSAGE,
true);
}
@Test void testCastIntervalToNumeric() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.CAST, VmName.EXPAND);
// interval to decimal
if (DECIMAL) {
f.checkScalarExact("cast(INTERVAL '1.29' second(1,2) as decimal(2,1))",
"DECIMAL(2, 1) NOT NULL",
"1.3");
f.checkScalarExact("cast(INTERVAL '1.25' second as decimal(2,1))",
"DECIMAL(2, 1) NOT NULL",
"1.3");
f.checkScalarExact("cast(INTERVAL '-1.29' second as decimal(2,1))",
"DECIMAL(2, 1) NOT NULL",
"-1.3");
f.checkScalarExact("cast(INTERVAL '-1.25' second as decimal(2,1))",
"DECIMAL(2, 1) NOT NULL",
"-1.3");
f.checkScalarExact("cast(INTERVAL '-1.21' second as decimal(2,1))",
"DECIMAL(2, 1) NOT NULL",
"-1.2");
f.checkScalarExact("cast(INTERVAL '5' minute as decimal(2,1))",
"DECIMAL(2, 1) NOT NULL",
"5.0");
f.checkScalarExact("cast(INTERVAL '5' hour as decimal(2,1))",
"DECIMAL(2, 1) NOT NULL",
"5.0");
f.checkScalarExact("cast(INTERVAL '5' day as decimal(2,1))",
"DECIMAL(2, 1) NOT NULL",
"5.0");
f.checkScalarExact("cast(INTERVAL '5' month as decimal(2,1))",
"DECIMAL(2, 1) NOT NULL",
"5.0");
f.checkScalarExact("cast(INTERVAL '5' year as decimal(2,1))",
"DECIMAL(2, 1) NOT NULL",
"5.0");
f.checkScalarExact("cast(INTERVAL '-5' day as decimal(2,1))",
"DECIMAL(2, 1) NOT NULL",
"-5.0");
}
// Interval to bigint
f.checkScalarExact("cast(INTERVAL '1.25' second as bigint)",
"BIGINT NOT NULL",
"1");
f.checkScalarExact("cast(INTERVAL '-1.29' second(1,2) as bigint)",
"BIGINT NOT NULL",
"-1");
f.checkScalarExact("cast(INTERVAL '5' day as bigint)",
"BIGINT NOT NULL",
"5");
// Interval to integer
f.checkScalarExact("cast(INTERVAL '1.25' second as integer)",
"INTEGER NOT NULL",
"1");
f.checkScalarExact("cast(INTERVAL '-1.29' second(1,2) as integer)",
"INTEGER NOT NULL",
"-1");
f.checkScalarExact("cast(INTERVAL '5' day as integer)",
"INTEGER NOT NULL",
"5");
f.checkScalarExact("cast(INTERVAL '1' year as integer)",
"INTEGER NOT NULL",
"1");
f.checkScalarExact(
"cast((INTERVAL '1' year - INTERVAL '2' year) as integer)",
"INTEGER NOT NULL",
"-1");
f.checkScalarExact("cast(INTERVAL '1' month as integer)",
"INTEGER NOT NULL",
"1");
f.checkScalarExact(
"cast((INTERVAL '1' month - INTERVAL '2' month) as integer)",
"INTEGER NOT NULL",
"-1");
f.checkScalarExact("cast(INTERVAL '1' day as integer)",
"INTEGER NOT NULL",
"1");
f.checkScalarExact("cast((INTERVAL '1' day - INTERVAL '2' day) as integer)",
"INTEGER NOT NULL",
"-1");
f.checkScalarExact("cast(INTERVAL '1' hour as integer)",
"INTEGER NOT NULL",
"1");
f.checkScalarExact(
"cast((INTERVAL '1' hour - INTERVAL '2' hour) as integer)",
"INTEGER NOT NULL",
"-1");
f.checkScalarExact(
"cast(INTERVAL '1' hour as integer)",
"INTEGER NOT NULL",
"1");
f.checkScalarExact(
"cast((INTERVAL '1' minute - INTERVAL '2' minute) as integer)",
"INTEGER NOT NULL",
"-1");
f.checkScalarExact("cast(INTERVAL '1' minute as integer)",
"INTEGER NOT NULL",
"1");
f.checkScalarExact(
"cast((INTERVAL '1' second - INTERVAL '2' second) as integer)",
"INTEGER NOT NULL",
"-1");
}
@Test void testCastToInterval() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.CAST, VmName.EXPAND);
f.checkScalar(
"cast(5 as interval second)",
"+5.000000",
"INTERVAL SECOND NOT NULL");
f.checkScalar(
"cast(5 as interval minute)",
"+5",
"INTERVAL MINUTE NOT NULL");
f.checkScalar(
"cast(5 as interval hour)",
"+5",
"INTERVAL HOUR NOT NULL");
f.checkScalar(
"cast(5 as interval day)",
"+5",
"INTERVAL DAY NOT NULL");
f.checkScalar(
"cast(5 as interval month)",
"+5",
"INTERVAL MONTH NOT NULL");
f.checkScalar(
"cast(5 as interval year)",
"+5",
"INTERVAL YEAR NOT NULL");
if (DECIMAL) {
// Due to DECIMAL rounding bugs, currently returns "+5"
f.checkScalar(
"cast(5.7 as interval day)",
"+6",
"INTERVAL DAY NOT NULL");
f.checkScalar(
"cast(-5.7 as interval day)",
"-6",
"INTERVAL DAY NOT NULL");
} else {
// An easier case
f.checkScalar(
"cast(6.2 as interval day)",
"+6",
"INTERVAL DAY NOT NULL");
}
f.checkScalar(
"cast(3456 as interval month(4))",
"+3456",
"INTERVAL MONTH(4) NOT NULL");
f.checkScalar(
"cast(-5723 as interval minute(4))",
"-5723",
"INTERVAL MINUTE(4) NOT NULL");
}
@Test void testCastIntervalToInterval() {
final SqlOperatorFixture f = fixture();
f.checkScalar("cast(interval '2 5' day to hour as interval hour to minute)",
"+53:00",
"INTERVAL HOUR TO MINUTE NOT NULL");
f.checkScalar("cast(interval '2 5' day to hour as interval day to minute)",
"+2 05:00",
"INTERVAL DAY TO MINUTE NOT NULL");
f.checkScalar("cast(interval '2 5' day to hour as interval hour to second)",
"+53:00:00.000000",
"INTERVAL HOUR TO SECOND NOT NULL");
f.checkScalar("cast(interval '2 5' day to hour as interval hour)",
"+53",
"INTERVAL HOUR NOT NULL");
f.checkScalar("cast(interval '-29:15' hour to minute as interval day to hour)",
"-1 05",
"INTERVAL DAY TO HOUR NOT NULL");
}
@Test void testCastWithRoundingToScalar() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.CAST, VmName.EXPAND);
f.checkCastToScalarOkay("1.25", "INTEGER", "1");
f.checkCastToScalarOkay("1.25E0", "INTEGER", "1");
if (!f.brokenTestsEnabled()) {
return;
}
f.checkCastToScalarOkay("1.5", "INTEGER", "2");
f.checkCastToScalarOkay("5E-1", "INTEGER", "1");
f.checkCastToScalarOkay("1.75", "INTEGER", "2");
f.checkCastToScalarOkay("1.75E0", "INTEGER", "2");
f.checkCastToScalarOkay("-1.25", "INTEGER", "-1");
f.checkCastToScalarOkay("-1.25E0", "INTEGER", "-1");
f.checkCastToScalarOkay("-1.5", "INTEGER", "-2");
f.checkCastToScalarOkay("-5E-1", "INTEGER", "-1");
f.checkCastToScalarOkay("-1.75", "INTEGER", "-2");
f.checkCastToScalarOkay("-1.75E0", "INTEGER", "-2");
f.checkCastToScalarOkay("1.23454", "DECIMAL(8, 4)", "1.2345");
f.checkCastToScalarOkay("1.23454E0", "DECIMAL(8, 4)", "1.2345");
f.checkCastToScalarOkay("1.23455", "DECIMAL(8, 4)", "1.2346");
f.checkCastToScalarOkay("5E-5", "DECIMAL(8, 4)", "0.0001");
f.checkCastToScalarOkay("1.99995", "DECIMAL(8, 4)", "2.0000");
f.checkCastToScalarOkay("1.99995E0", "DECIMAL(8, 4)", "2.0000");
f.checkCastToScalarOkay("-1.23454", "DECIMAL(8, 4)", "-1.2345");
f.checkCastToScalarOkay("-1.23454E0", "DECIMAL(8, 4)", "-1.2345");
f.checkCastToScalarOkay("-1.23455", "DECIMAL(8, 4)", "-1.2346");
f.checkCastToScalarOkay("-5E-5", "DECIMAL(8, 4)", "-0.0001");
f.checkCastToScalarOkay("-1.99995", "DECIMAL(8, 4)", "-2.0000");
f.checkCastToScalarOkay("-1.99995E0", "DECIMAL(8, 4)", "-2.0000");
// 9.99 round to 10.0, should give out of range error
f.checkFails("cast(9.99 as decimal(2,1))", OUT_OF_RANGE_MESSAGE,
true);
}
@Test void testCastDecimalToDoubleToInteger() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.CAST, VmName.EXPAND);
f.checkScalarExact("cast( cast(1.25 as double) as integer)", 1);
f.checkScalarExact("cast( cast(-1.25 as double) as integer)", -1);
if (!f.brokenTestsEnabled()) {
return;
}
f.checkScalarExact("cast( cast(1.75 as double) as integer)", 2);
f.checkScalarExact("cast( cast(-1.75 as double) as integer)", -2);
f.checkScalarExact("cast( cast(1.5 as double) as integer)", 2);
f.checkScalarExact("cast( cast(-1.5 as double) as integer)", -2);
}
@Test void testCastApproxNumericLimits() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.CAST, VmName.EXPAND);
// Test casting for min, max, out of range for approx numeric types
Numeric.forEach(numeric -> {
String type = numeric.typeName;
boolean isFloat;
switch (numeric) {
case DOUBLE:
case FLOAT:
isFloat = false;
break;
case REAL:
isFloat = true;
break;
default:
// Skip non-approx types
return;
}
if (!f.brokenTestsEnabled()) {
return;
}
// Convert from literal to type
f.checkCastToApproxOkay(numeric.maxNumericString, type,
isFloat
? isWithin(numeric.maxNumericAsDouble(), 1E32)
: isExactly(numeric.maxNumericAsDouble()));
f.checkCastToApproxOkay(numeric.minNumericString, type,
isExactly(numeric.minNumericString));
if (isFloat) {
f.checkCastFails(numeric.maxOverflowNumericString, type,
OUT_OF_RANGE_MESSAGE, true);
} else {
// Double: Literal out of range
f.checkCastFails(numeric.maxOverflowNumericString, type,
LITERAL_OUT_OF_RANGE_MESSAGE, false);
}
// Underflow: goes to 0
f.checkCastToApproxOkay(numeric.minOverflowNumericString, type,
isExactly(0));
// Convert from string to type
f.checkCastToApproxOkay("'" + numeric.maxNumericString + "'", type,
isFloat
? isWithin(numeric.maxNumericAsDouble(), 1E32)
: isExactly(numeric.maxNumericAsDouble()));
f.checkCastToApproxOkay("'" + numeric.minNumericString + "'", type,
isExactly(numeric.minNumericAsDouble()));
f.checkCastFails("'" + numeric.maxOverflowNumericString + "'", type,
OUT_OF_RANGE_MESSAGE, true);
// Underflow: goes to 0
f.checkCastToApproxOkay("'" + numeric.minOverflowNumericString + "'",
type, isExactly(0));
// Convert from type to string
// Treated as DOUBLE
f.checkCastToString(numeric.maxNumericString, null,
isFloat ? null : "1.79769313486231E308");
// TODO: The following tests are slightly different depending on
// whether the java or fennel calc are used.
// Try to make them the same
if (false /* fennel calc*/) { // Treated as FLOAT or DOUBLE
f.checkCastToString(numeric.maxNumericString, type,
// Treated as DOUBLE
isFloat ? "3.402824E38" : "1.797693134862316E308");
f.checkCastToString(numeric.minNumericString, null,
// Treated as FLOAT or DOUBLE
isFloat ? null : "4.940656458412465E-324");
f.checkCastToString(numeric.minNumericString, type,
isFloat ? "1.401299E-45" : "4.940656458412465E-324");
} else if (false /* JavaCalc */) {
// Treated as FLOAT or DOUBLE
f.checkCastToString(numeric.maxNumericString, type,
// Treated as DOUBLE
isFloat ? "3.402823E38" : "1.797693134862316E308");
f.checkCastToString(numeric.minNumericString, null,
isFloat ? null : null); // Treated as FLOAT or DOUBLE
f.checkCastToString(numeric.minNumericString, type,
isFloat ? "1.401298E-45" : null);
}
f.checkCastFails("'notnumeric'", type, INVALID_CHAR_MESSAGE, true);
});
}
@Test void testCastToApproxNumeric() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.CAST, VmName.EXPAND);
f.checkCastToApproxOkay("1", "DOUBLE", isExactly(1));
f.checkCastToApproxOkay("1.0", "DOUBLE", isExactly(1));
f.checkCastToApproxOkay("-2.3", "FLOAT", isWithin(-2.3, 0.000001));
f.checkCastToApproxOkay("'1'", "DOUBLE", isExactly(1));
f.checkCastToApproxOkay("' -1e-37 '", "DOUBLE", isExactly("-1.0E-37"));
f.checkCastToApproxOkay("1e0", "DOUBLE", isExactly(1));
f.checkCastToApproxOkay("0e0", "REAL", isExactly(0));
}
@Test void testCastNull() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.CAST, VmName.EXPAND);
// null
f.checkNull("cast(null as integer)");
if (DECIMAL) {
f.checkNull("cast(null as decimal(4,3))");
}
f.checkNull("cast(null as double)");
f.checkNull("cast(null as varchar(10))");
f.checkNull("cast(null as char(10))");
f.checkNull("cast(null as date)");
f.checkNull("cast(null as time)");
f.checkNull("cast(null as timestamp)");
f.checkNull("cast(null as interval year to month)");
f.checkNull("cast(null as interval day to second(3))");
f.checkNull("cast(null as boolean)");
}
/** Test case for
* [CALCITE-1439]
* Handling errors during constant reduction. */
@Test void testCastInvalid() {
// Before CALCITE-1439 was fixed, constant reduction would kick in and
// generate Java constants that throw when the class is loaded, thus
// ExceptionInInitializerError.
final SqlOperatorFixture f = fixture();
f.checkScalarExact("cast('15' as integer)", "INTEGER NOT NULL", "15");
if (Bug.CALCITE_2539_FIXED) {
f.checkFails("cast('15.4' as integer)", "xxx", true);
f.checkFails("cast('15.6' as integer)", "xxx", true);
f.checkFails("cast('ue' as boolean)", "xxx", true);
f.checkFails("cast('' as boolean)", "xxx", true);
f.checkFails("cast('' as integer)", "xxx", true);
f.checkFails("cast('' as real)", "xxx", true);
f.checkFails("cast('' as double)", "xxx", true);
f.checkFails("cast('' as smallint)", "xxx", true);
}
}
@Test void testCastDateTime() {
// Test cast for date/time/timestamp
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.CAST, VmName.EXPAND);
f.checkScalar("cast(TIMESTAMP '1945-02-24 12:42:25.34' as TIMESTAMP)",
"1945-02-24 12:42:25", "TIMESTAMP(0) NOT NULL");
f.checkScalar("cast(TIME '12:42:25.34' as TIME)",
"12:42:25", "TIME(0) NOT NULL");
// test rounding
if (f.brokenTestsEnabled()) {
f.checkScalar("cast(TIME '12:42:25.9' as TIME)",
"12:42:26", "TIME(0) NOT NULL");
}
if (Bug.FRG282_FIXED) {
// test precision
f.checkScalar("cast(TIME '12:42:25.34' as TIME(2))",
"12:42:25.34", "TIME(2) NOT NULL");
}
f.checkScalar("cast(DATE '1945-02-24' as DATE)",
"1945-02-24", "DATE NOT NULL");
// timestamp <-> time
f.checkScalar("cast(TIMESTAMP '1945-02-24 12:42:25.34' as TIME)",
"12:42:25", "TIME(0) NOT NULL");
// time <-> string
f.checkCastToString("TIME '12:42:25'", null, "12:42:25");
if (TODO) {
f.checkCastToString("TIME '12:42:25.34'", null, "12:42:25.34");
}
// Generate the current date as a string, e.g. "2007-04-18". The value
// is guaranteed to be good for at least 2 minutes, which should give
// us time to run the rest of the tests.
final String today =
new SimpleDateFormat("yyyy-MM-dd", Locale.ROOT).format(
getCalendarNotTooNear(Calendar.DAY_OF_MONTH).getTime());
f.checkScalar("cast(DATE '1945-02-24' as TIMESTAMP)",
"1945-02-24 00:00:00", "TIMESTAMP(0) NOT NULL");
// Note: Casting to time(0) should lose date info and fractional
// seconds, then casting back to timestamp should initialize to
// current_date.
f.checkScalar(
"cast(cast(TIMESTAMP '1945-02-24 12:42:25.34' as TIME) as TIMESTAMP)",
today + " 12:42:25", "TIMESTAMP(0) NOT NULL");
f.checkScalar("cast(TIME '12:42:25.34' as TIMESTAMP)",
today + " 12:42:25", "TIMESTAMP(0) NOT NULL");
// timestamp <-> date
f.checkScalar("cast(TIMESTAMP '1945-02-24 12:42:25.34' as DATE)",
"1945-02-24", "DATE NOT NULL");
// Note: casting to Date discards Time fields
f.checkScalar(
"cast(cast(TIMESTAMP '1945-02-24 12:42:25.34' as DATE) as TIMESTAMP)",
"1945-02-24 00:00:00", "TIMESTAMP(0) NOT NULL");
}
@Test void testCastStringToDateTime() {
final SqlOperatorFixture f = fixture();
f.checkScalar("cast('12:42:25' as TIME)",
"12:42:25", "TIME(0) NOT NULL");
f.checkScalar("cast('1:42:25' as TIME)",
"01:42:25", "TIME(0) NOT NULL");
f.checkScalar("cast('1:2:25' as TIME)",
"01:02:25", "TIME(0) NOT NULL");
f.checkScalar("cast(' 12:42:25 ' as TIME)",
"12:42:25", "TIME(0) NOT NULL");
f.checkScalar("cast('12:42:25.34' as TIME)",
"12:42:25", "TIME(0) NOT NULL");
if (Bug.FRG282_FIXED) {
f.checkScalar("cast('12:42:25.34' as TIME(2))",
"12:42:25.34", "TIME(2) NOT NULL");
}
f.checkFails("cast('nottime' as TIME)", BAD_DATETIME_MESSAGE, true);
if (Bug.CALCITE_2539_FIXED) {
f.checkFails("cast('1241241' as TIME)", BAD_DATETIME_MESSAGE, true);
f.checkFails("cast('12:54:78' as TIME)", BAD_DATETIME_MESSAGE, true);
f.checkFails("cast('12:34:5' as TIME)", BAD_DATETIME_MESSAGE, true);
f.checkFails("cast('12:3:45' as TIME)", BAD_DATETIME_MESSAGE, true);
f.checkFails("cast('1:23:45' as TIME)", BAD_DATETIME_MESSAGE, true);
}
// timestamp <-> string
f.checkCastToString("TIMESTAMP '1945-02-24 12:42:25'", null,
"1945-02-24 12:42:25");
if (TODO) {
// TODO: casting allows one to discard precision without error
f.checkCastToString("TIMESTAMP '1945-02-24 12:42:25.34'",
null, "1945-02-24 12:42:25.34");
}
f.checkScalar("cast('1945-02-24 12:42:25' as TIMESTAMP)",
"1945-02-24 12:42:25", "TIMESTAMP(0) NOT NULL");
f.checkScalar("cast('1945-2-2 12:2:5' as TIMESTAMP)",
"1945-02-02 12:02:05", "TIMESTAMP(0) NOT NULL");
f.checkScalar("cast(' 1945-02-24 12:42:25 ' as TIMESTAMP)",
"1945-02-24 12:42:25", "TIMESTAMP(0) NOT NULL");
f.checkScalar("cast('1945-02-24 12:42:25.34' as TIMESTAMP)",
"1945-02-24 12:42:25", "TIMESTAMP(0) NOT NULL");
f.checkScalar("cast('1945-12-31' as TIMESTAMP)",
"1945-12-31 00:00:00", "TIMESTAMP(0) NOT NULL");
f.checkScalar("cast('2004-02-29' as TIMESTAMP)",
"2004-02-29 00:00:00", "TIMESTAMP(0) NOT NULL");
if (Bug.FRG282_FIXED) {
f.checkScalar("cast('1945-02-24 12:42:25.34' as TIMESTAMP(2))",
"1945-02-24 12:42:25.34", "TIMESTAMP(2) NOT NULL");
}
f.checkFails("cast('nottime' as TIMESTAMP)", BAD_DATETIME_MESSAGE, true);
if (Bug.CALCITE_2539_FIXED) {
f.checkFails("cast('1241241' as TIMESTAMP)",
BAD_DATETIME_MESSAGE, true);
f.checkFails("cast('1945-20-24 12:42:25.34' as TIMESTAMP)",
BAD_DATETIME_MESSAGE, true);
f.checkFails("cast('1945-01-24 25:42:25.34' as TIMESTAMP)",
BAD_DATETIME_MESSAGE, true);
f.checkFails("cast('1945-1-24 12:23:34.454' as TIMESTAMP)",
BAD_DATETIME_MESSAGE, true);
}
// date <-> string
f.checkCastToString("DATE '1945-02-24'", null, "1945-02-24");
f.checkCastToString("DATE '1945-2-24'", null, "1945-02-24");
f.checkScalar("cast('1945-02-24' as DATE)", "1945-02-24", "DATE NOT NULL");
f.checkScalar("cast(' 1945-2-4 ' as DATE)", "1945-02-04", "DATE NOT NULL");
f.checkScalar("cast(' 1945-02-24 ' as DATE)",
"1945-02-24", "DATE NOT NULL");
f.checkFails("cast('notdate' as DATE)", BAD_DATETIME_MESSAGE, true);
if (Bug.CALCITE_2539_FIXED) {
f.checkFails("cast('52534253' as DATE)", BAD_DATETIME_MESSAGE, true);
f.checkFails("cast('1945-30-24' as DATE)", BAD_DATETIME_MESSAGE, true);
}
// cast null
f.checkNull("cast(null as date)");
f.checkNull("cast(null as timestamp)");
f.checkNull("cast(null as time)");
f.checkNull("cast(cast(null as varchar(10)) as time)");
f.checkNull("cast(cast(null as varchar(10)) as date)");
f.checkNull("cast(cast(null as varchar(10)) as timestamp)");
f.checkNull("cast(cast(null as date) as timestamp)");
f.checkNull("cast(cast(null as time) as timestamp)");
f.checkNull("cast(cast(null as timestamp) as date)");
f.checkNull("cast(cast(null as timestamp) as time)");
}
private static Calendar getFixedCalendar() {
Calendar calendar = Util.calendar();
calendar.set(Calendar.YEAR, 2014);
calendar.set(Calendar.MONTH, 8);
calendar.set(Calendar.DATE, 7);
calendar.set(Calendar.HOUR_OF_DAY, 17);
calendar.set(Calendar.MINUTE, 8);
calendar.set(Calendar.SECOND, 48);
calendar.set(Calendar.MILLISECOND, 15);
return calendar;
}
/**
* Returns a Calendar that is the current time, pausing if we are within 2
* minutes of midnight or the top of the hour.
*
* @param timeUnit Time unit
* @return calendar
*/
protected static Calendar getCalendarNotTooNear(int timeUnit) {
final Calendar cal = Util.calendar();
while (true) {
cal.setTimeInMillis(System.currentTimeMillis());
try {
switch (timeUnit) {
case Calendar.DAY_OF_MONTH:
// Within two minutes of the end of the day. Wait in 10s
// increments until calendar moves into the next next day.
if ((cal.get(Calendar.HOUR_OF_DAY) == 23)
&& (cal.get(Calendar.MINUTE) >= 58)) {
Thread.sleep(10 * 1000);
continue;
}
return cal;
case Calendar.HOUR_OF_DAY:
// Within two minutes of the top of the hour. Wait in 10s
// increments until calendar moves into the next next day.
if (cal.get(Calendar.MINUTE) >= 58) {
Thread.sleep(10 * 1000);
continue;
}
return cal;
default:
throw new AssertionError("unexpected time unit: " + timeUnit);
}
} catch (InterruptedException e) {
throw TestUtil.rethrow(e);
}
}
}
@Test void testCastToBoolean() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.CAST, VmName.EXPAND);
// string to boolean
f.checkBoolean("cast('true' as boolean)", true);
f.checkBoolean("cast('false' as boolean)", false);
f.checkBoolean("cast(' trUe' as boolean)", true);
f.checkBoolean("cast(' tr' || 'Ue' as boolean)", true);
f.checkBoolean("cast(' fALse' as boolean)", false);
f.checkFails("cast('unknown' as boolean)", INVALID_CHAR_MESSAGE, true);
f.checkBoolean("cast(cast('true' as varchar(10)) as boolean)", true);
f.checkBoolean("cast(cast('false' as varchar(10)) as boolean)", false);
f.checkFails("cast(cast('blah' as varchar(10)) as boolean)",
INVALID_CHAR_MESSAGE, true);
}
@Test void testCase() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.CASE, VmName.EXPAND);
f.checkScalarExact("case when 'a'='a' then 1 end", 1);
f.checkString("case 2 when 1 then 'a' when 2 then 'bcd' end",
"bcd", "CHAR(3)");
f.checkString("case 1 when 1 then 'a' when 2 then 'bcd' end",
"a ", "CHAR(3)");
f.checkString("case 1 when 1 then cast('a' as varchar(1)) "
+ "when 2 then cast('bcd' as varchar(3)) end",
"a", "VARCHAR(3)");
if (DECIMAL) {
f.checkScalarExact("case 2 when 1 then 11.2 "
+ "when 2 then 4.543 else null end",
"DECIMAL(5, 3)", "4.543");
f.checkScalarExact("case 1 when 1 then 11.2 "
+ "when 2 then 4.543 else null end",
"DECIMAL(5, 3)", "11.200");
}
f.checkScalarExact("case 'a' when 'a' then 1 end", 1);
f.checkScalarApprox("case 1 when 1 then 11.2e0 "
+ "when 2 then cast(4 as bigint) else 3 end",
"DOUBLE NOT NULL", isExactly("11.2"));
f.checkScalarApprox("case 1 when 1 then 11.2e0 "
+ "when 2 then 4 else null end",
"DOUBLE", isExactly("11.2"));
f.checkScalarApprox("case 2 when 1 then 11.2e0 "
+ "when 2 then 4 else null end",
"DOUBLE", isExactly(4));
f.checkScalarApprox("case 1 when 1 then 11.2e0 "
+ "when 2 then 4.543 else null end",
"DOUBLE", isExactly("11.2"));
f.checkScalarApprox("case 2 when 1 then 11.2e0 "
+ "when 2 then 4.543 else null end",
"DOUBLE", isExactly("4.543"));
f.checkNull("case 'a' when 'b' then 1 end");
// Per spec, 'case x when y then ...'
// translates to 'case when x = y then ...'
// so nulls do not match.
// (Unlike Oracle's 'decode(null, null, ...)', by the way.)
f.checkString("case cast(null as int)\n"
+ "when cast(null as int) then 'nulls match'\n"
+ "else 'nulls do not match' end",
"nulls do not match",
"CHAR(18) NOT NULL");
f.checkScalarExact("case when 'a'=cast(null as varchar(1)) then 1 "
+ "else 2 end",
2);
// equivalent to "nullif('a',cast(null as varchar(1)))"
f.checkString("case when 'a' = cast(null as varchar(1)) then null "
+ "else 'a' end",
"a", "CHAR(1)");
if (TODO) {
f.checkScalar("case 1 when 1 then row(1,2) when 2 then row(2,3) end",
"ROW(INTEGER NOT NULL, INTEGER NOT NULL)", "row(1,2)");
f.checkScalar("case 1 when 1 then row('a','b') "
+ "when 2 then row('ab','cd') end",
"ROW(CHAR(2) NOT NULL, CHAR(2) NOT NULL)", "row('a ','b ')");
}
// multiple values in some cases (introduced in SQL:2011)
f.checkString("case 1 "
+ "when 1, 2 then '1 or 2' "
+ "when 2 then 'not possible' "
+ "when 3, 2 then '3' "
+ "else 'none of the above' "
+ "end",
"1 or 2 ",
"CHAR(17) NOT NULL");
f.checkString("case 2 "
+ "when 1, 2 then '1 or 2' "
+ "when 2 then 'not possible' "
+ "when 3, 2 then '3' "
+ "else 'none of the above' "
+ "end",
"1 or 2 ",
"CHAR(17) NOT NULL");
f.checkString("case 3 "
+ "when 1, 2 then '1 or 2' "
+ "when 2 then 'not possible' "
+ "when 3, 2 then '3' "
+ "else 'none of the above' "
+ "end",
"3 ",
"CHAR(17) NOT NULL");
f.checkString("case 4 "
+ "when 1, 2 then '1 or 2' "
+ "when 2 then 'not possible' "
+ "when 3, 2 then '3' "
+ "else 'none of the above' "
+ "end",
"none of the above",
"CHAR(17) NOT NULL");
// tests with SqlConformance
final SqlOperatorFixture f2 =
f.withConformance(SqlConformanceEnum.PRAGMATIC_2003);
f2.checkString("case 2 when 1 then 'a' when 2 then 'bcd' end",
"bcd", "VARCHAR(3)");
f2.checkString("case 1 when 1 then 'a' when 2 then 'bcd' end",
"a", "VARCHAR(3)");
f2.checkString("case 1 when 1 then cast('a' as varchar(1)) "
+ "when 2 then cast('bcd' as varchar(3)) end",
"a", "VARCHAR(3)");
f2.checkString("case cast(null as int) when cast(null as int)"
+ " then 'nulls match'"
+ " else 'nulls do not match' end",
"nulls do not match",
"VARCHAR(18) NOT NULL");
f2.checkScalarExact("case when 'a'=cast(null as varchar(1)) then 1 "
+ "else 2 end",
2);
// equivalent to "nullif('a',cast(null as varchar(1)))"
f2.checkString("case when 'a' = cast(null as varchar(1)) then null "
+ "else 'a' end",
"a", "CHAR(1)");
// multiple values in some cases (introduced in SQL:2011)
f2.checkString("case 1 "
+ "when 1, 2 then '1 or 2' "
+ "when 2 then 'not possible' "
+ "when 3, 2 then '3' "
+ "else 'none of the above' "
+ "end",
"1 or 2", "VARCHAR(17) NOT NULL");
f2.checkString("case 2 "
+ "when 1, 2 then '1 or 2' "
+ "when 2 then 'not possible' "
+ "when 3, 2 then '3' "
+ "else 'none of the above' "
+ "end",
"1 or 2", "VARCHAR(17) NOT NULL");
f2.checkString("case 3 "
+ "when 1, 2 then '1 or 2' "
+ "when 2 then 'not possible' "
+ "when 3, 2 then '3' "
+ "else 'none of the above' "
+ "end",
"3", "VARCHAR(17) NOT NULL");
f2.checkString("case 4 "
+ "when 1, 2 then '1 or 2' "
+ "when 2 then 'not possible' "
+ "when 3, 2 then '3' "
+ "else 'none of the above' "
+ "end",
"none of the above", "VARCHAR(17) NOT NULL");
// TODO: Check case with multisets
}
@Test void testCaseNull() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.CASE, VmName.EXPAND);
f.checkScalarExact("case when 1 = 1 then 10 else null end", 10);
f.checkNull("case when 1 = 2 then 10 else null end");
}
@Test void testCaseType() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.CASE, VmName.EXPAND);
f.checkType("case 1 when 1 then current_timestamp else null end",
"TIMESTAMP(0)");
f.checkType("case 1 when 1 then current_timestamp "
+ "else current_timestamp end",
"TIMESTAMP(0) NOT NULL");
f.checkType("case when true then current_timestamp else null end",
"TIMESTAMP(0)");
f.checkType("case when true then current_timestamp end",
"TIMESTAMP(0)");
f.checkType("case 'x' when 'a' then 3 when 'b' then null else 4.5 end",
"DECIMAL(11, 1)");
}
/**
* Tests support for JDBC functions.
*
* See FRG-97 "Support for JDBC escape syntax is incomplete".
*/
@Test void testJdbcFn() {
final SqlOperatorFixture f = fixture();
f.setFor(new SqlJdbcFunctionCall("dummy"), VmName.EXPAND);
// There follows one test for each function in appendix C of the JDBC
// 3.0 specification. The test is 'if-false'd out if the function is
// not implemented or is broken.
// Numeric Functions
f.checkScalar("{fn ABS(-3)}", 3, "INTEGER NOT NULL");
f.checkScalarApprox("{fn ACOS(0.2)}", "DOUBLE NOT NULL",
isWithin(1.36943, 0.001));
f.checkScalarApprox("{fn ASIN(0.2)}", "DOUBLE NOT NULL",
isWithin(0.20135, 0.001));
f.checkScalarApprox("{fn ATAN(0.2)}", "DOUBLE NOT NULL",
isWithin(0.19739, 0.001));
f.checkScalarApprox("{fn ATAN2(-2, 2)}", "DOUBLE NOT NULL",
isWithin(-0.78539, 0.001));
f.checkScalar("{fn CBRT(8)}", 2.0, "DOUBLE NOT NULL");
f.checkScalar("{fn CEILING(-2.6)}", -2, "DECIMAL(2, 0) NOT NULL");
f.checkScalarApprox("{fn COS(0.2)}", "DOUBLE NOT NULL",
isWithin(0.98007, 0.001));
f.checkScalarApprox("{fn COT(0.2)}", "DOUBLE NOT NULL",
isWithin(4.93315, 0.001));
f.checkScalarApprox("{fn DEGREES(-1)}", "DOUBLE NOT NULL",
isWithin(-57.29578, 0.001));
f.checkScalarApprox("{fn EXP(2)}", "DOUBLE NOT NULL",
isWithin(7.389, 0.001));
f.checkScalar("{fn FLOOR(2.6)}", 2, "DECIMAL(2, 0) NOT NULL");
f.checkScalarApprox("{fn LOG(10)}", "DOUBLE NOT NULL",
isWithin(2.30258, 0.001));
f.checkScalarApprox("{fn LOG10(100)}", "DOUBLE NOT NULL", isExactly(2));
f.checkScalar("{fn MOD(19, 4)}", 3, "INTEGER NOT NULL");
f.checkScalarApprox("{fn PI()}", "DOUBLE NOT NULL",
isWithin(3.14159, 0.0001));
f.checkScalarApprox("{fn POWER(2, 3)}", "DOUBLE NOT NULL",
isWithin(8.0, 0.001));
f.checkScalarApprox("{fn RADIANS(90)}", "DOUBLE NOT NULL",
isWithin(1.57080, 0.001));
f.checkScalarApprox("{fn RAND(42)}", "DOUBLE NOT NULL",
isWithin(0.63708, 0.001));
f.checkScalar("{fn ROUND(1251, -2)}", 1300, "INTEGER NOT NULL");
f.checkFails("^{fn ROUND(1251)}^", "Cannot apply '\\{fn ROUND\\}' to "
+ "arguments of type '\\{fn ROUND\\}\\(\\)'.*", false);
f.checkScalar("{fn SIGN(-1)}", -1, "INTEGER NOT NULL");
f.checkScalarApprox("{fn SIN(0.2)}", "DOUBLE NOT NULL",
isWithin(0.19867, 0.001));
f.checkScalarApprox("{fn SQRT(4.2)}", "DOUBLE NOT NULL",
isWithin(2.04939, 0.001));
f.checkScalarApprox("{fn TAN(0.2)}", "DOUBLE NOT NULL",
isWithin(0.20271, 0.001));
f.checkScalar("{fn TRUNCATE(12.34, 1)}", 12.3, "DECIMAL(4, 2) NOT NULL");
f.checkScalar("{fn TRUNCATE(-12.34, -1)}", -10, "DECIMAL(4, 2) NOT NULL");
// String Functions
f.checkScalar("{fn ASCII('a')}", 97, "INTEGER NOT NULL");
f.checkScalar("{fn ASCII('ABC')}", "65", "INTEGER NOT NULL");
f.checkNull("{fn ASCII(cast(null as varchar(1)))}");
if (false) {
f.checkScalar("{fn CHAR(code)}", null, "");
}
f.checkScalar("{fn CONCAT('foo', 'bar')}", "foobar", "CHAR(6) NOT NULL");
f.checkScalar("{fn DIFFERENCE('Miller', 'miller')}", "4",
"INTEGER NOT NULL");
f.checkNull("{fn DIFFERENCE('muller', cast(null as varchar(1)))}");
f.checkString("{fn REVERSE('abc')}", "cba", "VARCHAR(3) NOT NULL");
f.checkNull("{fn REVERSE(cast(null as varchar(1)))}");
f.checkString("{fn LEFT('abcd', 3)}", "abc", "VARCHAR(4) NOT NULL");
f.checkString("{fn LEFT('abcd', 4)}", "abcd", "VARCHAR(4) NOT NULL");
f.checkString("{fn LEFT('abcd', 5)}", "abcd", "VARCHAR(4) NOT NULL");
f.checkNull("{fn LEFT(cast(null as varchar(1)), 3)}");
f.checkString("{fn RIGHT('abcd', 3)}", "bcd", "VARCHAR(4) NOT NULL");
f.checkString("{fn RIGHT('abcd', 4)}", "abcd", "VARCHAR(4) NOT NULL");
f.checkString("{fn RIGHT('abcd', 5)}", "abcd", "VARCHAR(4) NOT NULL");
f.checkNull("{fn RIGHT(cast(null as varchar(1)), 3)}");
// REVIEW: is this result correct? I think it should be "abcCdef"
f.checkScalar("{fn INSERT('abc', 1, 2, 'ABCdef')}",
"ABCdefc", "VARCHAR(9) NOT NULL");
f.checkScalar("{fn LCASE('foo' || 'bar')}",
"foobar", "CHAR(6) NOT NULL");
if (false) {
f.checkScalar("{fn LENGTH(string)}", null, "");
}
f.checkScalar("{fn LOCATE('ha', 'alphabet')}", 4, "INTEGER NOT NULL");
f.checkScalar("{fn LOCATE('ha', 'alphabet', 6)}", 0, "INTEGER NOT NULL");
f.checkScalar("{fn LTRIM(' xxx ')}", "xxx ", "VARCHAR(6) NOT NULL");
f.checkScalar("{fn REPEAT('a', -100)}", "", "VARCHAR(1) NOT NULL");
f.checkNull("{fn REPEAT('abc', cast(null as integer))}");
f.checkNull("{fn REPEAT(cast(null as varchar(1)), cast(null as integer))}");
f.checkString("{fn REPLACE('JACK and JUE','J','BL')}",
"BLACK and BLUE", "VARCHAR(12) NOT NULL");
// REPLACE returns NULL in Oracle but not in Postgres or in Calcite.
// When [CALCITE-815] is implemented and SqlConformance#emptyStringIsNull is
// enabled, it will return empty string as NULL.
f.checkString("{fn REPLACE('ciao', 'ciao', '')}", "",
"VARCHAR(4) NOT NULL");
f.checkString("{fn REPLACE('hello world', 'o', '')}", "hell wrld",
"VARCHAR(11) NOT NULL");
f.checkNull("{fn REPLACE(cast(null as varchar(5)), 'ciao', '')}");
f.checkNull("{fn REPLACE('ciao', cast(null as varchar(3)), 'zz')}");
f.checkNull("{fn REPLACE('ciao', 'bella', cast(null as varchar(3)))}");
f.checkScalar(
"{fn RTRIM(' xxx ')}",
" xxx",
"VARCHAR(6) NOT NULL");
f.checkScalar("{fn SOUNDEX('Miller')}", "M460", "VARCHAR(4) NOT NULL");
f.checkNull("{fn SOUNDEX(cast(null as varchar(1)))}");
f.checkScalar("{fn SPACE(-100)}", "", "VARCHAR(2000) NOT NULL");
f.checkNull("{fn SPACE(cast(null as integer))}");
f.checkScalar(
"{fn SUBSTRING('abcdef', 2, 3)}",
"bcd",
"VARCHAR(6) NOT NULL");
f.checkScalar("{fn UCASE('xxx')}", "XXX", "CHAR(3) NOT NULL");
// Time and Date Functions
f.checkType("{fn CURDATE()}", "DATE NOT NULL");
f.checkType("{fn CURTIME()}", "TIME(0) NOT NULL");
f.checkScalar("{fn DAYNAME(DATE '2014-12-10')}",
// Day names in root locale changed from long to short in JDK 9
TestUtil.getJavaMajorVersion() <= 8 ? "Wednesday" : "Wed",
"VARCHAR(2000) NOT NULL");
f.checkScalar("{fn DAYOFMONTH(DATE '2014-12-10')}", 10,
"BIGINT NOT NULL");
if (Bug.CALCITE_2539_FIXED) {
f.checkFails("{fn DAYOFWEEK(DATE '2014-12-10')}",
"cannot translate call EXTRACT.*",
true);
f.checkFails("{fn DAYOFYEAR(DATE '2014-12-10')}",
"cannot translate call EXTRACT.*",
true);
}
f.checkScalar("{fn HOUR(TIMESTAMP '2014-12-10 12:34:56')}", 12,
"BIGINT NOT NULL");
f.checkScalar("{fn MINUTE(TIMESTAMP '2014-12-10 12:34:56')}", 34,
"BIGINT NOT NULL");
f.checkScalar("{fn MONTH(DATE '2014-12-10')}", 12, "BIGINT NOT NULL");
f.checkScalar("{fn MONTHNAME(DATE '2014-12-10')}",
// Month names in root locale changed from long to short in JDK 9
TestUtil.getJavaMajorVersion() <= 8 ? "December" : "Dec",
"VARCHAR(2000) NOT NULL");
f.checkType("{fn NOW()}", "TIMESTAMP(0) NOT NULL");
f.checkScalar("{fn QUARTER(DATE '2014-12-10')}", "4",
"BIGINT NOT NULL");
f.checkScalar("{fn SECOND(TIMESTAMP '2014-12-10 12:34:56')}", 56,
"BIGINT NOT NULL");
f.checkScalar("{fn TIMESTAMPADD(HOUR, 5,"
+ " TIMESTAMP '2014-03-29 12:34:56')}",
"2014-03-29 17:34:56", "TIMESTAMP(0) NOT NULL");
f.checkScalar("{fn TIMESTAMPDIFF(HOUR,"
+ " TIMESTAMP '2014-03-29 12:34:56',"
+ " TIMESTAMP '2014-03-29 12:34:56')}", "0", "INTEGER NOT NULL");
f.checkScalar("{fn TIMESTAMPDIFF(MONTH,"
+ " TIMESTAMP '2019-09-01 00:00:00',"
+ " TIMESTAMP '2020-03-01 00:00:00')}", "6", "INTEGER NOT NULL");
if (Bug.CALCITE_2539_FIXED) {
f.checkFails("{fn WEEK(DATE '2014-12-10')}",
"cannot translate call EXTRACT.*",
true);
}
f.checkScalar("{fn YEAR(DATE '2014-12-10')}", 2014, "BIGINT NOT NULL");
// System Functions
f.checkType("{fn DATABASE()}", "VARCHAR(2000) NOT NULL");
f.checkString("{fn IFNULL('a', 'b')}", "a", "CHAR(1) NOT NULL");
f.checkString("{fn USER()}", "sa", "VARCHAR(2000) NOT NULL");
// Conversion Functions
// Legacy JDBC style
f.checkScalar("{fn CONVERT('123', INTEGER)}", 123, "INTEGER NOT NULL");
// ODBC/JDBC style
f.checkScalar("{fn CONVERT('123', SQL_INTEGER)}", 123,
"INTEGER NOT NULL");
f.checkScalar("{fn CONVERT(INTERVAL '1' DAY, SQL_INTERVAL_DAY_TO_SECOND)}",
"+1 00:00:00.000000", "INTERVAL DAY TO SECOND NOT NULL");
}
@Test void testChr() {
final SqlOperatorFixture f0 = fixture()
.setFor(SqlLibraryOperators.CHR, VM_FENNEL, VM_JAVA);
final SqlOperatorFixture f = f0.withLibrary(SqlLibrary.ORACLE);
f.checkScalar("chr(97)", "a", "CHAR(1) NOT NULL");
f.checkScalar("chr(48)", "0", "CHAR(1) NOT NULL");
f.checkScalar("chr(0)", String.valueOf('\u0000'), "CHAR(1) NOT NULL");
f0.checkFails("^chr(97.1)^",
"No match found for function signature CHR\\(\\)", false);
}
@Test void testSelect() {
final SqlOperatorFixture f = fixture();
f.check("select * from (values(1))", SqlTests.INTEGER_TYPE_CHECKER, 1);
// Check return type on scalar sub-query in select list. Note return
// type is always nullable even if sub-query select value is NOT NULL.
// Bug FRG-189 causes this test to fail only in SqlOperatorTest; not
// in subtypes.
if (Bug.FRG189_FIXED) {
f.checkType("SELECT *,\n"
+ " (SELECT * FROM (VALUES(1)))\n"
+ "FROM (VALUES(2))",
"RecordType(INTEGER NOT NULL EXPR$0, INTEGER EXPR$1) NOT NULL");
f.checkType("SELECT *,\n"
+ " (SELECT * FROM (VALUES(CAST(10 as BIGINT))))\n"
+ "FROM (VALUES(CAST(10 as bigint)))",
"RecordType(BIGINT NOT NULL EXPR$0, BIGINT EXPR$1) NOT NULL");
f.checkType("SELECT *,\n"
+ " (SELECT * FROM (VALUES(10.5)))\n"
+ "FROM (VALUES(10.5))",
"RecordType(DECIMAL(3, 1) NOT NULL EXPR$0, DECIMAL(3, 1) EXPR$1) NOT NULL");
f.checkType("SELECT *,\n"
+ " (SELECT * FROM (VALUES('this is a char')))\n"
+ "FROM (VALUES('this is a char too'))",
"RecordType(CHAR(18) NOT NULL EXPR$0, CHAR(14) EXPR$1) NOT NULL");
f.checkType("SELECT *,\n"
+ " (SELECT * FROM (VALUES(true)))\n"
+ "FROM (values(false))",
"RecordType(BOOLEAN NOT NULL EXPR$0, BOOLEAN EXPR$1) NOT NULL");
f.checkType(" SELECT *,\n"
+ " (SELECT * FROM (VALUES(cast('abcd' as varchar(10)))))\n"
+ "FROM (VALUES(CAST('abcd' as varchar(10))))",
"RecordType(VARCHAR(10) NOT NULL EXPR$0, VARCHAR(10) EXPR$1) NOT NULL");
f.checkType("SELECT *,\n"
+ " (SELECT * FROM (VALUES(TIMESTAMP '2006-01-01 12:00:05')))\n"
+ "FROM (VALUES(TIMESTAMP '2006-01-01 12:00:05'))",
"RecordType(TIMESTAMP(0) NOT NULL EXPR$0, TIMESTAMP(0) EXPR$1) NOT NULL");
}
}
@Test void testLiteralChain() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.LITERAL_CHAIN, VM_EXPAND);
f.checkString("'buttered'\n"
+ "' toast'",
"buttered toast",
"CHAR(14) NOT NULL");
f.checkString("'corned'\n"
+ "' beef'\n"
+ "' on'\n"
+ "' rye'",
"corned beef on rye",
"CHAR(18) NOT NULL");
f.checkString("_latin1'Spaghetti'\n"
+ "' all''Amatriciana'",
"Spaghetti all'Amatriciana",
"CHAR(25) NOT NULL");
f.checkBoolean("x'1234'\n"
+ "'abcd' = x'1234abcd'", true);
f.checkBoolean("x'1234'\n"
+ "'' = x'1234'", true);
f.checkBoolean("x''\n"
+ "'ab' = x'ab'", true);
}
@Test void testComplexLiteral() {
final SqlOperatorFixture f = fixture();
f.check("select 2 * 2 * x from (select 2 as x)",
SqlTests.INTEGER_TYPE_CHECKER, 8);
f.check("select 1 * 2 * 3 * x from (select 2 as x)",
SqlTests.INTEGER_TYPE_CHECKER, 12);
f.check("select 1 + 2 + 3 + 4 + x from (select 2 as x)",
SqlTests.INTEGER_TYPE_CHECKER, 12);
}
@Test void testRow() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.ROW, VM_FENNEL);
}
@Test void testAndOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.AND, VmName.EXPAND);
f.checkBoolean("true and false", false);
f.checkBoolean("true and true", true);
f.checkBoolean("cast(null as boolean) and false", false);
f.checkBoolean("false and cast(null as boolean)", false);
f.checkNull("cast(null as boolean) and true");
f.checkBoolean("true and (not false)", true);
}
@Test void testAndOperator2() {
final SqlOperatorFixture f = fixture();
f.checkBoolean("case when false then unknown else true end and true",
true);
f.checkBoolean("case when false then cast(null as boolean) "
+ "else true end and true",
true);
f.checkBoolean("case when false then null else true end and true",
true);
}
@Test void testAndOperatorLazy() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.AND, VmName.EXPAND);
// lazy eval returns FALSE;
// eager eval executes RHS of AND and throws;
// both are valid
f.check("values 1 > 2 and sqrt(-4) = -2",
SqlTests.BOOLEAN_TYPE_CHECKER, SqlTests.ANY_PARAMETER_CHECKER,
new ValueOrExceptionResultChecker(false, INVALID_ARG_FOR_POWER,
CODE_2201F));
}
@Test void testConcatOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.CONCAT, VmName.EXPAND);
f.checkString(" 'a'||'b' ", "ab", "CHAR(2) NOT NULL");
f.checkNull(" 'a' || cast(null as char(2)) ");
f.checkNull(" cast(null as char(2)) || 'b' ");
f.checkNull(" cast(null as char(1)) || cast(null as char(2)) ");
f.checkString(" x'fe'||x'df' ", "fedf", "BINARY(2) NOT NULL");
f.checkString(" cast('fe' as char(2)) || cast('df' as varchar)",
"fedf", "VARCHAR NOT NULL");
// Precision is larger than VARCHAR allows, so result is unbounded
f.checkString(" cast('fe' as char(2)) || cast('df' as varchar(65535))",
"fedf", "VARCHAR NOT NULL");
f.checkString(" cast('fe' as char(2)) || cast('df' as varchar(33333))",
"fedf", "VARCHAR(33335) NOT NULL");
f.checkNull("x'ff' || cast(null as varbinary)");
f.checkNull(" cast(null as ANY) || cast(null as ANY) ");
f.checkString("cast('a' as varchar) || cast('b' as varchar) "
+ "|| cast('c' as varchar)", "abc", "VARCHAR NOT NULL");
}
@Test void testConcatFunc() {
final SqlOperatorFixture f = fixture();
checkConcatFunc(f.withLibrary(SqlLibrary.MYSQL));
checkConcatFunc(f.withLibrary(SqlLibrary.POSTGRESQL));
checkConcat2Func(f.withLibrary(SqlLibrary.ORACLE));
}
private static void checkConcatFunc(SqlOperatorFixture f) {
f.setFor(SqlLibraryOperators.CONCAT_FUNCTION);
f.checkString("concat('a', 'b', 'c')", "abc", "VARCHAR(3) NOT NULL");
f.checkString("concat(cast('a' as varchar), cast('b' as varchar), "
+ "cast('c' as varchar))", "abc", "VARCHAR NOT NULL");
f.checkNull("concat('a', 'b', cast(null as char(2)))");
f.checkNull("concat(cast(null as ANY), 'b', cast(null as char(2)))");
f.checkString("concat('', '', 'a')", "a", "VARCHAR(1) NOT NULL");
f.checkString("concat('', '', '')", "", "VARCHAR(0) NOT NULL");
f.checkFails("^concat()^", INVALID_ARGUMENTS_NUMBER, false);
}
private static void checkConcat2Func(SqlOperatorFixture f) {
f.setFor(SqlLibraryOperators.CONCAT2);
f.checkString("concat(cast('fe' as char(2)), cast('df' as varchar(65535)))",
"fedf", "VARCHAR NOT NULL");
f.checkString("concat(cast('fe' as char(2)), cast('df' as varchar))",
"fedf", "VARCHAR NOT NULL");
f.checkString("concat(cast('fe' as char(2)), cast('df' as varchar(33333)))",
"fedf", "VARCHAR(33335) NOT NULL");
f.checkString("concat('', '')", "", "VARCHAR(0) NOT NULL");
f.checkString("concat('', 'a')", "a", "VARCHAR(1) NOT NULL");
f.checkString("concat('a', 'b')", "ab", "VARCHAR(2) NOT NULL");
f.checkNull("concat('a', cast(null as varchar))");
f.checkFails("^concat('a', 'b', 'c')^", INVALID_ARGUMENTS_NUMBER, false);
f.checkFails("^concat('a')^", INVALID_ARGUMENTS_NUMBER, false);
}
@Test void testModOperator() {
// "%" is allowed under MYSQL_5 SQL conformance level
final SqlOperatorFixture f0 = fixture();
final SqlOperatorFixture f = f0.withConformance(SqlConformanceEnum.MYSQL_5);
f.setFor(SqlStdOperatorTable.PERCENT_REMAINDER);
f.checkScalarExact("4%2", 0);
f.checkScalarExact("8%5", 3);
f.checkScalarExact("-12%7", -5);
f.checkScalarExact("-12%-7", -5);
f.checkScalarExact("12%-7", 5);
f.checkScalarExact("cast(12 as tinyint) % cast(-7 as tinyint)",
"TINYINT NOT NULL", "5");
if (!DECIMAL) {
return;
}
f.checkScalarExact("cast(9 as decimal(2, 0)) % 7",
"INTEGER NOT NULL", "2");
f.checkScalarExact("7 % cast(9 as decimal(2, 0))",
"DECIMAL(2, 0) NOT NULL", "7");
f.checkScalarExact("cast(-9 as decimal(2, 0)) % cast(7 as decimal(1, 0))",
"DECIMAL(1, 0) NOT NULL", "-2");
}
@Test void testModPrecedence() {
// "%" is allowed under MYSQL_5 SQL conformance level
final SqlOperatorFixture f0 = fixture();
final SqlOperatorFixture f = f0.withConformance(SqlConformanceEnum.MYSQL_5);
f.setFor(SqlStdOperatorTable.PERCENT_REMAINDER);
f.checkScalarExact("1 + 5 % 3 % 4 * 14 % 17", 12);
f.checkScalarExact("(1 + 5 % 3) % 4 + 14 % 17", 17);
}
@Test void testModOperatorNull() {
// "%" is allowed under MYSQL_5 SQL conformance level
final SqlOperatorFixture f0 = fixture();
final SqlOperatorFixture f = f0.withConformance(SqlConformanceEnum.MYSQL_5);
f.checkNull("cast(null as integer) % 2");
f.checkNull("4 % cast(null as tinyint)");
if (!DECIMAL) {
return;
}
f.checkNull("4 % cast(null as decimal(12,0))");
}
@Test void testModOperatorDivByZero() {
// "%" is allowed under MYSQL_5 SQL conformance level
final SqlOperatorFixture f0 = fixture();
final SqlOperatorFixture f = f0.withConformance(SqlConformanceEnum.MYSQL_5);
// The extra CASE expression is to fool Janino. It does constant
// reduction and will throw the divide by zero exception while
// compiling the expression. The test frame work would then issue
// unexpected exception occurred during "validation". You cannot
// submit as non-runtime because the janino exception does not have
// error position information and the framework is unhappy with that.
f.checkFails("3 % case 'a' when 'a' then 0 end",
DIVISION_BY_ZERO_MESSAGE, true);
}
@Test void testDivideOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.DIVIDE, VmName.EXPAND);
f.checkScalarExact("10 / 5", "INTEGER NOT NULL", "2");
f.checkScalarExact("-10 / 5", "INTEGER NOT NULL", "-2");
f.checkScalarExact("-10 / 5.0", "DECIMAL(17, 6) NOT NULL", "-2");
f.checkScalarApprox(" cast(10.0 as double) / 5", "DOUBLE NOT NULL",
isExactly(2));
f.checkScalarApprox(" cast(10.0 as real) / 4", "REAL NOT NULL",
isExactly("2.5"));
f.checkScalarApprox(" 6.0 / cast(10.0 as real) ", "DOUBLE NOT NULL",
isExactly("0.6"));
f.checkScalarExact("10.0 / 5.0", "DECIMAL(9, 6) NOT NULL", "2");
if (DECIMAL) {
f.checkScalarExact("1.0 / 3.0", "DECIMAL(8, 6) NOT NULL", "0.333333");
f.checkScalarExact("100.1 / 0.0001", "DECIMAL(14, 7) NOT NULL",
"1001000.0000000");
f.checkScalarExact("100.1 / 0.00000001", "DECIMAL(19, 8) NOT NULL",
"10010000000.00000000");
}
f.checkNull("1e1 / cast(null as float)");
if (Bug.CALCITE_2539_FIXED) {
f.checkFails("100.1 / 0.00000000000000001", OUT_OF_RANGE_MESSAGE,
true);
}
}
@Test void testDivideOperatorIntervals() {
final SqlOperatorFixture f = fixture();
f.checkScalar("interval '-2:2' hour to minute / 3",
"-0:41", "INTERVAL HOUR TO MINUTE NOT NULL");
f.checkScalar("interval '2:5:12' hour to second / 2 / -3",
"-0:20:52.000000", "INTERVAL HOUR TO SECOND NOT NULL");
f.checkNull("interval '2' day / cast(null as bigint)");
f.checkNull("cast(null as interval month) / 2");
f.checkScalar("interval '3-3' year to month / 15e-1",
"+2-02", "INTERVAL YEAR TO MONTH NOT NULL");
f.checkScalar("interval '3-4' year to month / 4.5",
"+0-09", "INTERVAL YEAR TO MONTH NOT NULL");
}
@Test void testEqualsOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.EQUALS, VmName.EXPAND);
f.checkBoolean("1=1", true);
f.checkBoolean("1=1.0", true);
f.checkBoolean("1.34=1.34", true);
f.checkBoolean("1=1.34", false);
f.checkBoolean("1e2=100e0", true);
f.checkBoolean("1e2=101", false);
f.checkBoolean(
"cast(1e2 as real)=cast(101 as bigint)",
false);
f.checkBoolean("'a'='b'", false);
f.checkBoolean("true = true", true);
f.checkBoolean("true = false", false);
f.checkBoolean("false = true", false);
f.checkBoolean("false = false", true);
f.checkBoolean("cast('a' as varchar(30))=cast('a' as varchar(30))", true);
f.checkBoolean("cast('a ' as varchar(30))=cast('a' as varchar(30))", false);
f.checkBoolean("cast(' a' as varchar(30))=cast(' a' as varchar(30))", true);
f.checkBoolean("cast('a ' as varchar(15))=cast('a ' as varchar(30))", true);
f.checkBoolean("cast(' ' as varchar(3))=cast(' ' as varchar(2))", true);
f.checkBoolean("cast('abcd' as varchar(2))='ab'", true);
f.checkBoolean("cast('a' as varchar(30))=cast('b' as varchar(30))", false);
f.checkBoolean("cast('a' as varchar(30))=cast('a' as varchar(15))", true);
f.checkNull("cast(null as boolean)=cast(null as boolean)");
f.checkNull("cast(null as integer)=1");
f.checkNull("cast(null as varchar(10))='a'");
}
@Test void testEqualsOperatorInterval() {
final SqlOperatorFixture f = fixture();
f.checkBoolean("interval '2' day = interval '1' day", false);
f.checkBoolean("interval '2' day = interval '2' day", true);
f.checkBoolean("interval '2:2:2' hour to second = interval '2' hour",
false);
f.checkNull("cast(null as interval hour) = interval '2' minute");
}
@Test void testGreaterThanOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.GREATER_THAN, VmName.EXPAND);
f.checkBoolean("1>2", false);
f.checkBoolean("cast(-1 as TINYINT)>cast(1 as TINYINT)", false);
f.checkBoolean("cast(1 as SMALLINT)>cast(1 as SMALLINT)", false);
f.checkBoolean("2>1", true);
f.checkBoolean("1.1>1.2", false);
f.checkBoolean("-1.1>-1.2", true);
f.checkBoolean("1.1>1.1", false);
f.checkBoolean("1.2>1", true);
f.checkBoolean("1.1e1>1.2e1", false);
f.checkBoolean("cast(-1.1 as real) > cast(-1.2 as real)", true);
f.checkBoolean("1.1e2>1.1e2", false);
f.checkBoolean("1.2e0>1", true);
f.checkBoolean("cast(1.2e0 as real)>1", true);
f.checkBoolean("true>false", true);
f.checkBoolean("true>true", false);
f.checkBoolean("false>false", false);
f.checkBoolean("false>true", false);
f.checkNull("3.0>cast(null as double)");
f.checkBoolean("DATE '2013-02-23' > DATE '1945-02-24'", true);
f.checkBoolean("DATE '2013-02-23' > CAST(NULL AS DATE)", null);
f.checkBoolean("x'0A000130'>x'0A0001B0'", false);
}
@Test void testGreaterThanOperatorIntervals() {
final SqlOperatorFixture f = fixture();
f.checkBoolean("interval '2' day > interval '1' day", true);
f.checkBoolean("interval '2' day > interval '5' day", false);
f.checkBoolean("interval '2 2:2:2' day to second > interval '2' day", true);
f.checkBoolean("interval '2' day > interval '2' day", false);
f.checkBoolean("interval '2' day > interval '-2' day", true);
f.checkBoolean("interval '2' day > interval '2' hour", true);
f.checkBoolean("interval '2' minute > interval '2' hour", false);
f.checkBoolean("interval '2' second > interval '2' minute", false);
f.checkNull("cast(null as interval hour) > interval '2' minute");
f.checkNull(
"interval '2:2' hour to minute > cast(null as interval second)");
}
@Test void testIsDistinctFromOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.IS_DISTINCT_FROM, VM_EXPAND);
f.checkBoolean("1 is distinct from 1", false);
f.checkBoolean("1 is distinct from 1.0", false);
f.checkBoolean("1 is distinct from 2", true);
f.checkBoolean("cast(null as integer) is distinct from 2", true);
f.checkBoolean(
"cast(null as integer) is distinct from cast(null as integer)",
false);
f.checkBoolean("1.23 is distinct from 1.23", false);
f.checkBoolean("1.23 is distinct from 5.23", true);
f.checkBoolean("-23e0 is distinct from -2.3e1", false);
// IS DISTINCT FROM not implemented for ROW yet
if (false) {
f.checkBoolean("row(1,1) is distinct from row(1,1)", true);
f.checkBoolean("row(1,1) is distinct from row(1,2)", false);
}
// Intervals
f.checkBoolean("interval '2' day is distinct from interval '1' day", true);
f.checkBoolean("interval '10' hour is distinct from interval '10' hour",
false);
}
@Test void testIsNotDistinctFromOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.IS_NOT_DISTINCT_FROM, VM_EXPAND);
f.checkBoolean("1 is not distinct from 1", true);
f.checkBoolean("1 is not distinct from 1.0", true);
f.checkBoolean("1 is not distinct from 2", false);
f.checkBoolean("cast(null as integer) is not distinct from 2", false);
f.checkBoolean(
"cast(null as integer) is not distinct from cast(null as integer)",
true);
f.checkBoolean("1.23 is not distinct from 1.23", true);
f.checkBoolean("1.23 is not distinct from 5.23", false);
f.checkBoolean("-23e0 is not distinct from -2.3e1", true);
// IS NOT DISTINCT FROM not implemented for ROW yet
if (false) {
f.checkBoolean("row(1,1) is not distinct from row(1,1)", false);
f.checkBoolean("row(1,1) is not distinct from row(1,2)", true);
}
// Intervals
f.checkBoolean("interval '2' day is not distinct from interval '1' day",
false);
f.checkBoolean("interval '10' hour is not distinct from interval '10' hour",
true);
}
@Test void testGreaterThanOrEqualOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.GREATER_THAN_OR_EQUAL, VmName.EXPAND);
f.checkBoolean("1>=2", false);
f.checkBoolean("-1>=1", false);
f.checkBoolean("1>=1", true);
f.checkBoolean("2>=1", true);
f.checkBoolean("1.1>=1.2", false);
f.checkBoolean("-1.1>=-1.2", true);
f.checkBoolean("1.1>=1.1", true);
f.checkBoolean("1.2>=1", true);
f.checkBoolean("1.2e4>=1e5", false);
f.checkBoolean("1.2e4>=cast(1e5 as real)", false);
f.checkBoolean("1.2>=cast(1e5 as double)", false);
f.checkBoolean("120000>=cast(1e5 as real)", true);
f.checkBoolean("true>=false", true);
f.checkBoolean("true>=true", true);
f.checkBoolean("false>=false", true);
f.checkBoolean("false>=true", false);
f.checkNull("cast(null as real)>=999");
f.checkBoolean("x'0A000130'>=x'0A0001B0'", false);
f.checkBoolean("x'0A0001B0'>=x'0A0001B0'", true);
}
@Test void testGreaterThanOrEqualOperatorIntervals() {
final SqlOperatorFixture f = fixture();
f.checkBoolean("interval '2' day >= interval '1' day", true);
f.checkBoolean("interval '2' day >= interval '5' day", false);
f.checkBoolean("interval '2 2:2:2' day to second >= interval '2' day",
true);
f.checkBoolean("interval '2' day >= interval '2' day", true);
f.checkBoolean("interval '2' day >= interval '-2' day", true);
f.checkBoolean("interval '2' day >= interval '2' hour", true);
f.checkBoolean("interval '2' minute >= interval '2' hour", false);
f.checkBoolean("interval '2' second >= interval '2' minute", false);
f.checkNull("cast(null as interval hour) >= interval '2' minute");
f.checkNull(
"interval '2:2' hour to minute >= cast(null as interval second)");
}
@Test void testInOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.IN, VM_EXPAND);
f.checkBoolean("1 in (0, 1, 2)", true);
f.checkBoolean("3 in (0, 1, 2)", false);
f.checkBoolean("cast(null as integer) in (0, 1, 2)", null);
f.checkBoolean("cast(null as integer) in (0, cast(null as integer), 2)",
null);
if (Bug.FRG327_FIXED) {
f.checkBoolean("cast(null as integer) in (0, null, 2)", null);
f.checkBoolean("1 in (0, null, 2)", null);
}
if (!f.brokenTestsEnabled()) {
return;
}
// AND has lower precedence than IN
f.checkBoolean("false and true in (false, false)", false);
if (!Bug.TODO_FIXED) {
return;
}
f.checkFails("'foo' in (^)^", "(?s).*Encountered \"\\)\" at .*", false);
}
@Test void testNotInOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.NOT_IN, VM_EXPAND);
f.checkBoolean("1 not in (0, 1, 2)", false);
f.checkBoolean("3 not in (0, 1, 2)", true);
if (!f.brokenTestsEnabled()) {
return;
}
f.checkBoolean("cast(null as integer) not in (0, 1, 2)", null);
f.checkBoolean("cast(null as integer) not in (0, cast(null as integer), 2)",
null);
if (Bug.FRG327_FIXED) {
f.checkBoolean("cast(null as integer) not in (0, null, 2)", null);
f.checkBoolean("1 not in (0, null, 2)", null);
}
// AND has lower precedence than NOT IN
f.checkBoolean("true and false not in (true, true)", true);
if (!Bug.TODO_FIXED) {
return;
}
f.checkFails("'foo' not in (^)^", "(?s).*Encountered \"\\)\" at .*", false);
}
@Test void testOverlapsOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.OVERLAPS, VM_EXPAND);
f.checkBoolean("(date '1-2-3', date '1-2-3') "
+ "overlaps (date '1-2-3', interval '1' year)", true);
f.checkBoolean("(date '1-2-3', date '1-2-3') "
+ "overlaps (date '4-5-6', interval '1' year)", false);
f.checkBoolean("(date '1-2-3', date '4-5-6') "
+ "overlaps (date '2-2-3', date '3-4-5')", true);
f.checkNull("(cast(null as date), date '1-2-3') "
+ "overlaps (date '1-2-3', interval '1' year)");
f.checkNull("(date '1-2-3', date '1-2-3') overlaps "
+ "(date '1-2-3', cast(null as date))");
f.checkBoolean("(time '1:2:3', interval '1' second) "
+ "overlaps (time '23:59:59', time '1:2:3')", true);
f.checkBoolean("(time '1:2:3', interval '1' second) "
+ "overlaps (time '23:59:59', time '1:2:2')", true);
f.checkBoolean("(time '1:2:3', interval '1' second) "
+ "overlaps (time '23:59:59', interval '2' hour)", false);
f.checkNull("(time '1:2:3', cast(null as time)) "
+ "overlaps (time '23:59:59', time '1:2:3')");
f.checkNull("(time '1:2:3', interval '1' second) "
+ "overlaps (time '23:59:59', cast(null as interval hour))");
f.checkBoolean("(timestamp '1-2-3 4:5:6', timestamp '1-2-3 4:5:6' ) "
+ "overlaps (timestamp '1-2-3 4:5:6',"
+ " interval '1 2:3:4.5' day to second)", true);
f.checkBoolean("(timestamp '1-2-3 4:5:6', timestamp '1-2-3 4:5:6' ) "
+ "overlaps (timestamp '2-2-3 4:5:6',"
+ " interval '1 2:3:4.5' day to second)", false);
f.checkNull("(timestamp '1-2-3 4:5:6', cast(null as interval day) ) "
+ "overlaps (timestamp '1-2-3 4:5:6',"
+ " interval '1 2:3:4.5' day to second)");
f.checkNull("(timestamp '1-2-3 4:5:6', timestamp '1-2-3 4:5:6' ) "
+ "overlaps (cast(null as timestamp),"
+ " interval '1 2:3:4.5' day to second)");
}
/** Test case for
* [CALCITE-715]
* Add PERIOD type constructor and period operators (CONTAINS, PRECEDES,
* etc.).
*
* Tests OVERLAP and similar period operators CONTAINS, EQUALS, PRECEDES,
* SUCCEEDS, IMMEDIATELY PRECEDES, IMMEDIATELY SUCCEEDS for DATE, TIME and
* TIMESTAMP values. */
@Test void testPeriodOperators() {
String[] times = {
"TIME '01:00:00'",
"TIME '02:00:00'",
"TIME '03:00:00'",
"TIME '04:00:00'",
};
String[] dates = {
"DATE '1970-01-01'",
"DATE '1970-02-01'",
"DATE '1970-03-01'",
"DATE '1970-04-01'",
};
String[] timestamps = {
"TIMESTAMP '1970-01-01 00:00:00'",
"TIMESTAMP '1970-02-01 00:00:00'",
"TIMESTAMP '1970-03-01 00:00:00'",
"TIMESTAMP '1970-04-01 00:00:00'",
};
final SqlOperatorFixture f = fixture();
checkOverlaps(new OverlapChecker(f, times));
checkOverlaps(new OverlapChecker(f, dates));
checkOverlaps(new OverlapChecker(f, timestamps));
}
static void checkOverlaps(OverlapChecker c) {
c.isTrue("($0,$0) OVERLAPS ($0,$0)");
c.isFalse("($0,$1) OVERLAPS ($2,$3)");
c.isTrue("($0,$1) OVERLAPS ($1,$2)");
c.isTrue("($0,$2) OVERLAPS ($1,$3)");
c.isTrue("($0,$2) OVERLAPS ($3,$1)");
c.isTrue("($2,$0) OVERLAPS ($3,$1)");
c.isFalse("($3,$2) OVERLAPS ($1,$0)");
c.isTrue("($2,$3) OVERLAPS ($0,$2)");
c.isTrue("($2,$3) OVERLAPS ($2,$0)");
c.isTrue("($3,$2) OVERLAPS ($2,$0)");
c.isTrue("($0,$2) OVERLAPS ($2,$0)");
c.isTrue("($0,$3) OVERLAPS ($1,$3)");
c.isTrue("($0,$3) OVERLAPS ($3,$3)");
c.isTrue("($0,$0) CONTAINS ($0,$0)");
c.isFalse("($0,$1) CONTAINS ($2,$3)");
c.isFalse("($0,$1) CONTAINS ($1,$2)");
c.isFalse("($0,$2) CONTAINS ($1,$3)");
c.isFalse("($0,$2) CONTAINS ($3,$1)");
c.isFalse("($2,$0) CONTAINS ($3,$1)");
c.isFalse("($3,$2) CONTAINS ($1,$0)");
c.isFalse("($2,$3) CONTAINS ($0,$2)");
c.isFalse("($2,$3) CONTAINS ($2,$0)");
c.isFalse("($3,$2) CONTAINS ($2,$0)");
c.isTrue("($0,$2) CONTAINS ($2,$0)");
c.isTrue("($0,$3) CONTAINS ($1,$3)");
c.isTrue("($0,$3) CONTAINS ($3,$3)");
c.isTrue("($3,$0) CONTAINS ($3,$3)");
c.isTrue("($3,$0) CONTAINS ($0,$0)");
c.isTrue("($0,$0) CONTAINS $0");
c.isTrue("($3,$0) CONTAINS $0");
c.isTrue("($3,$0) CONTAINS $1");
c.isTrue("($3,$0) CONTAINS $2");
c.isTrue("($3,$0) CONTAINS $3");
c.isTrue("($0,$3) CONTAINS $0");
c.isTrue("($0,$3) CONTAINS $1");
c.isTrue("($0,$3) CONTAINS $2");
c.isTrue("($0,$3) CONTAINS $3");
c.isFalse("($1,$3) CONTAINS $0");
c.isFalse("($1,$2) CONTAINS $3");
c.isTrue("($0,$0) EQUALS ($0,$0)");
c.isFalse("($0,$1) EQUALS ($2,$3)");
c.isFalse("($0,$1) EQUALS ($1,$2)");
c.isFalse("($0,$2) EQUALS ($1,$3)");
c.isFalse("($0,$2) EQUALS ($3,$1)");
c.isFalse("($2,$0) EQUALS ($3,$1)");
c.isFalse("($3,$2) EQUALS ($1,$0)");
c.isFalse("($2,$3) EQUALS ($0,$2)");
c.isFalse("($2,$3) EQUALS ($2,$0)");
c.isFalse("($3,$2) EQUALS ($2,$0)");
c.isTrue("($0,$2) EQUALS ($2,$0)");
c.isFalse("($0,$3) EQUALS ($1,$3)");
c.isFalse("($0,$3) EQUALS ($3,$3)");
c.isFalse("($3,$0) EQUALS ($3,$3)");
c.isFalse("($3,$0) EQUALS ($0,$0)");
c.isTrue("($0,$0) PRECEDES ($0,$0)");
c.isTrue("($0,$1) PRECEDES ($2,$3)");
c.isTrue("($0,$1) PRECEDES ($1,$2)");
c.isFalse("($0,$2) PRECEDES ($1,$3)");
c.isFalse("($0,$2) PRECEDES ($3,$1)");
c.isFalse("($2,$0) PRECEDES ($3,$1)");
c.isFalse("($3,$2) PRECEDES ($1,$0)");
c.isFalse("($2,$3) PRECEDES ($0,$2)");
c.isFalse("($2,$3) PRECEDES ($2,$0)");
c.isFalse("($3,$2) PRECEDES ($2,$0)");
c.isFalse("($0,$2) PRECEDES ($2,$0)");
c.isFalse("($0,$3) PRECEDES ($1,$3)");
c.isTrue("($0,$3) PRECEDES ($3,$3)");
c.isTrue("($3,$0) PRECEDES ($3,$3)");
c.isFalse("($3,$0) PRECEDES ($0,$0)");
c.isTrue("($0,$0) SUCCEEDS ($0,$0)");
c.isFalse("($0,$1) SUCCEEDS ($2,$3)");
c.isFalse("($0,$1) SUCCEEDS ($1,$2)");
c.isFalse("($0,$2) SUCCEEDS ($1,$3)");
c.isFalse("($0,$2) SUCCEEDS ($3,$1)");
c.isFalse("($2,$0) SUCCEEDS ($3,$1)");
c.isTrue("($3,$2) SUCCEEDS ($1,$0)");
c.isTrue("($2,$3) SUCCEEDS ($0,$2)");
c.isTrue("($2,$3) SUCCEEDS ($2,$0)");
c.isTrue("($3,$2) SUCCEEDS ($2,$0)");
c.isFalse("($0,$2) SUCCEEDS ($2,$0)");
c.isFalse("($0,$3) SUCCEEDS ($1,$3)");
c.isFalse("($0,$3) SUCCEEDS ($3,$3)");
c.isFalse("($3,$0) SUCCEEDS ($3,$3)");
c.isTrue("($3,$0) SUCCEEDS ($0,$0)");
c.isTrue("($0,$0) IMMEDIATELY PRECEDES ($0,$0)");
c.isFalse("($0,$1) IMMEDIATELY PRECEDES ($2,$3)");
c.isTrue("($0,$1) IMMEDIATELY PRECEDES ($1,$2)");
c.isFalse("($0,$2) IMMEDIATELY PRECEDES ($1,$3)");
c.isFalse("($0,$2) IMMEDIATELY PRECEDES ($3,$1)");
c.isFalse("($2,$0) IMMEDIATELY PRECEDES ($3,$1)");
c.isFalse("($3,$2) IMMEDIATELY PRECEDES ($1,$0)");
c.isFalse("($2,$3) IMMEDIATELY PRECEDES ($0,$2)");
c.isFalse("($2,$3) IMMEDIATELY PRECEDES ($2,$0)");
c.isFalse("($3,$2) IMMEDIATELY PRECEDES ($2,$0)");
c.isFalse("($0,$2) IMMEDIATELY PRECEDES ($2,$0)");
c.isFalse("($0,$3) IMMEDIATELY PRECEDES ($1,$3)");
c.isTrue("($0,$3) IMMEDIATELY PRECEDES ($3,$3)");
c.isTrue("($3,$0) IMMEDIATELY PRECEDES ($3,$3)");
c.isFalse("($3,$0) IMMEDIATELY PRECEDES ($0,$0)");
c.isTrue("($0,$0) IMMEDIATELY SUCCEEDS ($0,$0)");
c.isFalse("($0,$1) IMMEDIATELY SUCCEEDS ($2,$3)");
c.isFalse("($0,$1) IMMEDIATELY SUCCEEDS ($1,$2)");
c.isFalse("($0,$2) IMMEDIATELY SUCCEEDS ($1,$3)");
c.isFalse("($0,$2) IMMEDIATELY SUCCEEDS ($3,$1)");
c.isFalse("($2,$0) IMMEDIATELY SUCCEEDS ($3,$1)");
c.isFalse("($3,$2) IMMEDIATELY SUCCEEDS ($1,$0)");
c.isTrue("($2,$3) IMMEDIATELY SUCCEEDS ($0,$2)");
c.isTrue("($2,$3) IMMEDIATELY SUCCEEDS ($2,$0)");
c.isTrue("($3,$2) IMMEDIATELY SUCCEEDS ($2,$0)");
c.isFalse("($0,$2) IMMEDIATELY SUCCEEDS ($2,$0)");
c.isFalse("($0,$3) IMMEDIATELY SUCCEEDS ($1,$3)");
c.isFalse("($0,$3) IMMEDIATELY SUCCEEDS ($3,$3)");
c.isFalse("($3,$0) IMMEDIATELY SUCCEEDS ($3,$3)");
c.isTrue("($3,$0) IMMEDIATELY SUCCEEDS ($0,$0)");
}
@Test void testLessThanOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.LESS_THAN, VmName.EXPAND);
f.checkBoolean("1<2", true);
f.checkBoolean("-1<1", true);
f.checkBoolean("1<1", false);
f.checkBoolean("2<1", false);
f.checkBoolean("1.1<1.2", true);
f.checkBoolean("-1.1<-1.2", false);
f.checkBoolean("1.1<1.1", false);
f.checkBoolean("cast(1.1 as real)<1", false);
f.checkBoolean("cast(1.1 as real)<1.1", false);
f.checkBoolean("cast(1.1 as real) + ' with ' - '
f.checkScalar("timestamp '1969-04-29 0:0:0' +"
+ " (timestamp '2008-07-15 15:28:00' - "
+ " timestamp '1969-04-29 0:0:0') day to second / 2",
"1988-12-06 07:44:00", "TIMESTAMP(0) NOT NULL");
f.checkScalar("date '1969-04-29' +"
+ " (date '2008-07-15' - "
+ " date '1969-04-29') day / 2",
"1988-12-06", "DATE NOT NULL");
f.checkScalar("time '01:23:44' +"
+ " (time '15:28:00' - "
+ " time '01:23:44') hour to second / 2",
"08:25:52", "TIME(0) NOT NULL");
if (Bug.DT1684_FIXED) {
f.checkBoolean("(date '1969-04-29' +"
+ " (CURRENT_DATE - "
+ " date '1969-04-29') day / 2) is not null",
true);
}
// TODO: Add tests for year month intervals (currently not supported)
}
@Test void testMultiplyOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.MULTIPLY, VmName.EXPAND);
f.checkScalarExact("2*3", 6);
f.checkScalarExact("2*-3", -6);
f.checkScalarExact("+2*3", 6);
f.checkScalarExact("2*0", 0);
f.checkScalarApprox("cast(2.0 as float)*3",
"FLOAT NOT NULL", isExactly(6));
f.checkScalarApprox("3*cast(2.0 as real)",
"REAL NOT NULL", isExactly(6));
f.checkScalarApprox("cast(2.0 as real)*3.2",
"DOUBLE NOT NULL", isExactly("6.4"));
f.checkScalarExact("10.0 * 5.0",
"DECIMAL(5, 2) NOT NULL", "50.00");
f.checkScalarExact("19.68 * 4.2",
"DECIMAL(6, 3) NOT NULL", "82.656");
f.checkNull("cast(1 as real)*cast(null as real)");
f.checkNull("2e-3*cast(null as integer)");
f.checkNull("cast(null as tinyint) * cast(4 as smallint)");
if (Bug.FNL25_FIXED) {
// Should throw out of range error
f.checkFails("cast(100 as tinyint) * cast(-2 as tinyint)",
OUT_OF_RANGE_MESSAGE, true);
f.checkFails("cast(200 as smallint) * cast(200 as smallint)",
OUT_OF_RANGE_MESSAGE, true);
f.checkFails("cast(1.5e9 as integer) * cast(-2 as integer)",
OUT_OF_RANGE_MESSAGE, true);
f.checkFails("cast(5e9 as bigint) * cast(2e9 as bigint)",
OUT_OF_RANGE_MESSAGE, true);
f.checkFails("cast(2e9 as decimal(19,0)) * cast(-5e9 as decimal(19,0))",
OUT_OF_RANGE_MESSAGE, true);
f.checkFails("cast(5e4 as decimal(19,10)) * cast(2e4 as decimal(19,10))",
OUT_OF_RANGE_MESSAGE, true);
}
}
@Test void testMultiplyIntervals() {
final SqlOperatorFixture f = fixture();
f.checkScalar("interval '2:2' hour to minute * 3",
"+6:06", "INTERVAL HOUR TO MINUTE NOT NULL");
f.checkScalar("3 * 2 * interval '2:5:12' hour to second",
"+12:31:12.000000", "INTERVAL HOUR TO SECOND NOT NULL");
f.checkNull("interval '2' day * cast(null as bigint)");
f.checkNull("cast(null as interval month) * 2");
if (TODO) {
f.checkScalar("interval '3-2' year to month * 15e-1",
"+04-09", "INTERVAL YEAR TO MONTH NOT NULL");
f.checkScalar("interval '3-4' year to month * 4.5",
"+15-00", "INTERVAL YEAR TO MONTH NOT NULL");
}
}
@Test void testDatePlusInterval() {
final SqlOperatorFixture f = fixture();
f.checkScalar("date '2014-02-11' + interval '2' day",
"2014-02-13", "DATE NOT NULL");
// 60 days is more than 2^32 milliseconds
f.checkScalar("date '2014-02-11' + interval '60' day",
"2014-04-12", "DATE NOT NULL");
}
/** Test case for
* [CALCITE-1864]
* Allow NULL literal as argument. */
@Test void testNullOperand() {
final SqlOperatorFixture f = fixture();
checkNullOperand(f, "=");
checkNullOperand(f, ">");
checkNullOperand(f, "<");
checkNullOperand(f, "<=");
checkNullOperand(f, ">=");
checkNullOperand(f, "<>");
// "!=" is allowed under ORACLE_10 SQL conformance level
final SqlOperatorFixture f1 =
f.withConformance(SqlConformanceEnum.ORACLE_10);
checkNullOperand(f1, "<>");
}
private void checkNullOperand(SqlOperatorFixture f, String op) {
f.checkBoolean("1 " + op + " null", null);
f.checkBoolean("null " + op + " -3", null);
f.checkBoolean("null " + op + " null", null);
}
@Test void testNotEqualsOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.NOT_EQUALS, VmName.EXPAND);
f.checkBoolean("1<>1", false);
f.checkBoolean("'a'<>'A'", true);
f.checkBoolean("1e0<>1e1", true);
f.checkNull("'a'<>cast(null as varchar(1))");
// "!=" is not an acceptable alternative to "<>" under default SQL
// conformance level
f.checkFails("1 ^!=^ 1",
"Bang equal '!=' is not allowed under the current SQL conformance level",
false);
// "!=" is allowed under ORACLE_10 SQL conformance level
final SqlOperatorFixture f1 =
f.withConformance(SqlConformanceEnum.ORACLE_10);
f1.checkBoolean("1 <> 1", false);
f1.checkBoolean("1 != 1", false);
f1.checkBoolean("1 != null", null);
}
@Test void testNotEqualsOperatorIntervals() {
final SqlOperatorFixture f = fixture();
f.checkBoolean("interval '2' day <> interval '1' day", true);
f.checkBoolean("interval '2' day <> interval '2' day", false);
f.checkBoolean("interval '2:2:2' hour to second <> interval '2' hour",
true);
f.checkNull("cast(null as interval hour) <> interval '2' minute");
}
@Test void testOrOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.OR, VmName.EXPAND);
f.checkBoolean("true or false", true);
f.checkBoolean("false or false", false);
f.checkBoolean("true or cast(null as boolean)", true);
f.checkNull("false or cast(null as boolean)");
}
@Test void testOrOperatorLazy() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.OR, VmName.EXPAND);
// need to evaluate 2nd argument if first evaluates to null, therefore
// get error
f.check("values 1 < cast(null as integer) or sqrt(-4) = -2",
SqlTests.BOOLEAN_TYPE_CHECKER, SqlTests.ANY_PARAMETER_CHECKER,
new ValueOrExceptionResultChecker(null, INVALID_ARG_FOR_POWER,
CODE_2201F));
// Do not need to evaluate 2nd argument if first evaluates to true.
// In eager evaluation, get error;
// lazy evaluation returns true;
// both are valid.
f.check("values 1 < 2 or sqrt(-4) = -2",
SqlTests.BOOLEAN_TYPE_CHECKER, SqlTests.ANY_PARAMETER_CHECKER,
new ValueOrExceptionResultChecker(true, INVALID_ARG_FOR_POWER,
CODE_2201F));
// NULL OR FALSE --> NULL
// In eager evaluation, get error;
// lazy evaluation returns NULL;
// both are valid.
f.check("values 1 < cast(null as integer) or sqrt(4) = -2",
SqlTests.BOOLEAN_TYPE_CHECKER, SqlTests.ANY_PARAMETER_CHECKER,
new ValueOrExceptionResultChecker(null, INVALID_ARG_FOR_POWER,
CODE_2201F));
// NULL OR TRUE --> TRUE
f.checkBoolean("1 < cast(null as integer) or sqrt(4) = 2", true);
}
@Test void testPlusOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.PLUS, VmName.EXPAND);
f.checkScalarExact("1+2", 3);
f.checkScalarExact("-1+2", 1);
f.checkScalarExact("1+2+3", 6);
f.checkScalarApprox("1+cast(2.0 as double)", "DOUBLE NOT NULL",
isExactly(3));
f.checkScalarApprox("1+cast(2.0 as double)+cast(6.0 as float)",
"DOUBLE NOT NULL", isExactly(9));
f.checkScalarExact("10.0 + 5.0", "DECIMAL(4, 1) NOT NULL", "15.0");
f.checkScalarExact("19.68 + 4.2", "DECIMAL(5, 2) NOT NULL", "23.88");
f.checkScalarExact("19.68 + 4.2 + 6", "DECIMAL(13, 2) NOT NULL", "29.88");
f.checkScalarApprox("19.68 + cast(4.2 as float)", "DOUBLE NOT NULL",
isWithin(23.88, 0.02));
f.checkNull("cast(null as tinyint)+1");
f.checkNull("1e-2+cast(null as double)");
if (Bug.FNL25_FIXED) {
// Should throw out of range error
f.checkFails("cast(100 as tinyint) + cast(100 as tinyint)",
OUT_OF_RANGE_MESSAGE, true);
f.checkFails("cast(-20000 as smallint) + cast(-20000 as smallint)",
OUT_OF_RANGE_MESSAGE, true);
f.checkFails("cast(1.5e9 as integer) + cast(1.5e9 as integer)",
OUT_OF_RANGE_MESSAGE, true);
f.checkFails("cast(5e18 as bigint) + cast(5e18 as bigint)",
OUT_OF_RANGE_MESSAGE, true);
f.checkFails("cast(-5e18 as decimal(19,0))"
+ " + cast(-5e18 as decimal(19,0))",
OUT_OF_RANGE_MESSAGE, true);
f.checkFails("cast(5e8 as decimal(19,10)) + cast(5e8 as decimal(19,10))",
OUT_OF_RANGE_MESSAGE, true);
}
}
@Test void testPlusOperatorAny() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.PLUS, VmName.EXPAND);
f.checkScalar("1+CAST(2 AS ANY)", "3", "ANY NOT NULL");
}
@Test void testPlusIntervalOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.PLUS, VmName.EXPAND);
f.checkScalar("interval '2' day + interval '1' day",
"+3", "INTERVAL DAY NOT NULL");
f.checkScalar("interval '2' day + interval '1' minute",
"+2 00:01", "INTERVAL DAY TO MINUTE NOT NULL");
f.checkScalar("interval '2' day + interval '5' minute"
+ " + interval '-3' second",
"+2 00:04:57.000000", "INTERVAL DAY TO SECOND NOT NULL");
f.checkScalar("interval '2' year + interval '1' month",
"+2-01", "INTERVAL YEAR TO MONTH NOT NULL");
f.checkNull("interval '2' year + cast(null as interval month)");
// Datetime plus interval
f.checkScalar("time '12:03:01' + interval '1:1' hour to minute",
"13:04:01", "TIME(0) NOT NULL");
// Per [CALCITE-1632] Return types of datetime + interval
// make sure that TIME values say in range
f.checkScalar("time '12:03:01' + interval '1' day",
"12:03:01", "TIME(0) NOT NULL");
f.checkScalar("time '12:03:01' + interval '25' hour",
"13:03:01", "TIME(0) NOT NULL");
f.checkScalar("time '12:03:01' + interval '25:0:1' hour to second",
"13:03:02", "TIME(0) NOT NULL");
f.checkScalar("interval '5' day + date '2005-03-02'",
"2005-03-07", "DATE NOT NULL");
f.checkScalar("date '2005-03-02' + interval '5' day",
"2005-03-07", "DATE NOT NULL");
f.checkScalar("date '2005-03-02' + interval '5' hour",
"2005-03-02", "DATE NOT NULL");
f.checkScalar("date '2005-03-02' + interval '25' hour",
"2005-03-03", "DATE NOT NULL");
f.checkScalar("date '2005-03-02' + interval '25:45' hour to minute",
"2005-03-03", "DATE NOT NULL");
f.checkScalar("date '2005-03-02' + interval '25:45:54' hour to second",
"2005-03-03", "DATE NOT NULL");
f.checkScalar("timestamp '2003-08-02 12:54:01'"
+ " + interval '-4 2:4' day to minute",
"2003-07-29 10:50:01", "TIMESTAMP(0) NOT NULL");
// Datetime plus year-to-month interval
f.checkScalar("interval '5-3' year to month + date '2005-03-02'",
"2010-06-02", "DATE NOT NULL");
f.checkScalar("timestamp '2003-08-02 12:54:01'"
+ " + interval '5-3' year to month",
"2008-11-02 12:54:01", "TIMESTAMP(0) NOT NULL");
f.checkScalar("interval '5-3' year to month"
+ " + timestamp '2003-08-02 12:54:01'",
"2008-11-02 12:54:01", "TIMESTAMP(0) NOT NULL");
}
@Test void testDescendingOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.DESC, VM_EXPAND);
}
@Test void testIsNotNullOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.IS_NOT_NULL, VmName.EXPAND);
f.checkBoolean("true is not null", true);
f.checkBoolean("cast(null as boolean) is not null", false);
}
@Test void testIsNullOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.IS_NULL, VmName.EXPAND);
f.checkBoolean("true is null", false);
f.checkBoolean("cast(null as boolean) is null", true);
}
@Test void testIsNotTrueOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.IS_NOT_TRUE, VmName.EXPAND);
f.checkBoolean("true is not true", false);
f.checkBoolean("false is not true", true);
f.checkBoolean("cast(null as boolean) is not true", true);
f.checkFails("select ^'a string' is not true^ from (values (1))",
"(?s)Cannot apply 'IS NOT TRUE' to arguments of type "
+ "' IS NOT TRUE'. Supported form\\(s\\): "
+ "' IS NOT TRUE'.*",
false);
}
@Test void testIsTrueOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.IS_TRUE, VmName.EXPAND);
f.checkBoolean("true is true", true);
f.checkBoolean("false is true", false);
f.checkBoolean("cast(null as boolean) is true", false);
}
@Test void testIsNotFalseOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.IS_NOT_FALSE, VmName.EXPAND);
f.checkBoolean("false is not false", false);
f.checkBoolean("true is not false", true);
f.checkBoolean("cast(null as boolean) is not false", true);
}
@Test void testIsFalseOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.IS_FALSE, VmName.EXPAND);
f.checkBoolean("false is false", true);
f.checkBoolean("true is false", false);
f.checkBoolean("cast(null as boolean) is false", false);
}
@Test void testIsNotUnknownOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.IS_NOT_UNKNOWN, VM_EXPAND);
f.checkBoolean("false is not unknown", true);
f.checkBoolean("true is not unknown", true);
f.checkBoolean("cast(null as boolean) is not unknown", false);
f.checkBoolean("unknown is not unknown", false);
f.checkFails("^'abc' IS NOT UNKNOWN^",
"(?s).*Cannot apply 'IS NOT UNKNOWN'.*",
false);
}
@Test void testIsUnknownOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.IS_UNKNOWN, VM_EXPAND);
f.checkBoolean("false is unknown", false);
f.checkBoolean("true is unknown", false);
f.checkBoolean("cast(null as boolean) is unknown", true);
f.checkBoolean("unknown is unknown", true);
f.checkFails("0 = 1 AND ^2 IS UNKNOWN^ AND 3 > 4",
"(?s).*Cannot apply 'IS UNKNOWN'.*",
false);
}
@Test void testIsASetOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.IS_A_SET, VM_EXPAND);
f.checkBoolean("multiset[1] is a set", true);
f.checkBoolean("multiset[1, 1] is a set", false);
f.checkBoolean("multiset[cast(null as boolean), cast(null as boolean)]"
+ " is a set", false);
f.checkBoolean("multiset[cast(null as boolean)] is a set", true);
f.checkBoolean("multiset['a'] is a set", true);
f.checkBoolean("multiset['a', 'b'] is a set", true);
f.checkBoolean("multiset['a', 'b', 'a'] is a set", false);
}
@Test void testIsNotASetOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.IS_NOT_A_SET, VM_EXPAND);
f.checkBoolean("multiset[1] is not a set", false);
f.checkBoolean("multiset[1, 1] is not a set", true);
f.checkBoolean("multiset[cast(null as boolean), cast(null as boolean)]"
+ " is not a set", true);
f.checkBoolean("multiset[cast(null as boolean)] is not a set", false);
f.checkBoolean("multiset['a'] is not a set", false);
f.checkBoolean("multiset['a', 'b'] is not a set", false);
f.checkBoolean("multiset['a', 'b', 'a'] is not a set", true);
}
@Test void testIntersectOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.MULTISET_INTERSECT, VM_EXPAND);
f.checkScalar("multiset[1] multiset intersect multiset[1]",
"[1]", "INTEGER NOT NULL MULTISET NOT NULL");
f.checkScalar("multiset[2] multiset intersect all multiset[1]",
"[]", "INTEGER NOT NULL MULTISET NOT NULL");
f.checkScalar("multiset[2] multiset intersect distinct multiset[1]",
"[]", "INTEGER NOT NULL MULTISET NOT NULL");
f.checkScalar("multiset[1, 1] multiset intersect distinct multiset[1, 1]",
"[1]", "INTEGER NOT NULL MULTISET NOT NULL");
f.checkScalar("multiset[1, 1] multiset intersect all multiset[1, 1]",
"[1, 1]", "INTEGER NOT NULL MULTISET NOT NULL");
f.checkScalar("multiset[1, 1] multiset intersect distinct multiset[1, 1]",
"[1]", "INTEGER NOT NULL MULTISET NOT NULL");
f.checkScalar("multiset[cast(null as integer), cast(null as integer)] "
+ "multiset intersect distinct multiset[cast(null as integer)]",
"[null]", "INTEGER MULTISET NOT NULL");
f.checkScalar("multiset[cast(null as integer), cast(null as integer)] "
+ "multiset intersect all multiset[cast(null as integer)]",
"[null]", "INTEGER MULTISET NOT NULL");
f.checkScalar("multiset[cast(null as integer), cast(null as integer)] "
+ "multiset intersect distinct multiset[cast(null as integer)]",
"[null]", "INTEGER MULTISET NOT NULL");
}
@Test void testExceptOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.MULTISET_EXCEPT, VM_EXPAND);
f.checkScalar("multiset[1] multiset except multiset[1]",
"[]", "INTEGER NOT NULL MULTISET NOT NULL");
f.checkScalar("multiset[1] multiset except distinct multiset[1]",
"[]", "INTEGER NOT NULL MULTISET NOT NULL");
f.checkScalar("multiset[2] multiset except multiset[1]",
"[2]", "INTEGER NOT NULL MULTISET NOT NULL");
f.checkScalar("multiset[1,2,3] multiset except multiset[1]",
"[2, 3]", "INTEGER NOT NULL MULTISET NOT NULL");
f.checkScalar("cardinality(multiset[1,2,3,2]"
+ " multiset except distinct multiset[1])",
"2", "INTEGER NOT NULL");
f.checkScalar("cardinality(multiset[1,2,3,2]"
+ " multiset except all multiset[1])",
"3", "INTEGER NOT NULL");
f.checkBoolean("(multiset[1,2,3,2] multiset except distinct multiset[1])"
+ " submultiset of multiset[2, 3]", true);
f.checkBoolean("(multiset[1,2,3,2] multiset except distinct multiset[1])"
+ " submultiset of multiset[2, 3]", true);
f.checkBoolean("(multiset[1,2,3,2] multiset except all multiset[1])"
+ " submultiset of multiset[2, 2, 3]", true);
f.checkBoolean("(multiset[1,2,3] multiset except multiset[1]) is empty",
false);
f.checkBoolean("(multiset[1] multiset except multiset[1]) is empty", true);
}
@Test void testIsEmptyOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.IS_EMPTY, VM_EXPAND);
f.checkBoolean("multiset[1] is empty", false);
}
@Test void testIsNotEmptyOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.IS_NOT_EMPTY, VM_EXPAND);
f.checkBoolean("multiset[1] is not empty", true);
}
@Test void testExistsOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.EXISTS, VM_EXPAND);
}
@Test void testNotOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.NOT, VmName.EXPAND);
f.checkBoolean("not true", false);
f.checkBoolean("not false", true);
f.checkBoolean("not unknown", null);
f.checkNull("not cast(null as boolean)");
}
@Test void testPrefixMinusOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.UNARY_MINUS, VmName.EXPAND);
f.enableTypeCoercion(false)
.checkFails("'a' + ^- 'b'^ + 'c'",
"(?s)Cannot apply '-' to arguments of type '-'.*",
false);
f.checkType("'a' + - 'b' + 'c'", "DECIMAL(19, 9) NOT NULL");
f.checkScalarExact("-1", -1);
f.checkScalarExact("-1.23", "DECIMAL(3, 2) NOT NULL", "-1.23");
f.checkScalarApprox("-1.0e0", "DOUBLE NOT NULL", isExactly(-1));
f.checkNull("-cast(null as integer)");
f.checkNull("-cast(null as tinyint)");
}
@Test void testPrefixMinusOperatorIntervals() {
final SqlOperatorFixture f = fixture();
f.checkScalar("-interval '-6:2:8' hour to second",
"+6:02:08.000000", "INTERVAL HOUR TO SECOND NOT NULL");
f.checkScalar("- -interval '-6:2:8' hour to second",
"-6:02:08.000000", "INTERVAL HOUR TO SECOND NOT NULL");
f.checkScalar("-interval '5' month",
"-5", "INTERVAL MONTH NOT NULL");
f.checkNull("-cast(null as interval day to minute)");
}
@Test void testPrefixPlusOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.UNARY_PLUS, VM_EXPAND);
f.checkScalarExact("+1", 1);
f.checkScalarExact("+1.23", "DECIMAL(3, 2) NOT NULL", "1.23");
f.checkScalarApprox("+1.0e0", "DOUBLE NOT NULL", isExactly(1));
f.checkNull("+cast(null as integer)");
f.checkNull("+cast(null as tinyint)");
}
@Test void testPrefixPlusOperatorIntervals() {
final SqlOperatorFixture f = fixture();
f.checkScalar("+interval '-6:2:8' hour to second",
"-6:02:08.000000", "INTERVAL HOUR TO SECOND NOT NULL");
f.checkScalar("++interval '-6:2:8' hour to second",
"-6:02:08.000000", "INTERVAL HOUR TO SECOND NOT NULL");
if (Bug.FRG254_FIXED) {
f.checkScalar("+interval '6:2:8.234' hour to second",
"+06:02:08.234", "INTERVAL HOUR TO SECOND NOT NULL");
}
f.checkScalar("+interval '5' month",
"+5", "INTERVAL MONTH NOT NULL");
f.checkNull("+cast(null as interval day to minute)");
}
@Test void testExplicitTableOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.EXPLICIT_TABLE, VM_EXPAND);
}
@Test void testValuesOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.VALUES, VM_EXPAND);
f.check("select 'abc' from (values(true))",
"CHAR(3) NOT NULL", "abc");
}
@Test void testNotLikeOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.NOT_LIKE, VM_EXPAND);
f.checkBoolean("'abc' not like '_b_'", false);
f.checkBoolean("'ab\ncd' not like 'ab%'", false);
f.checkBoolean("'123\n\n45\n' not like '%'", false);
f.checkBoolean("'ab\ncd\nef' not like '%cd%'", false);
f.checkBoolean("'ab\ncd\nef' not like '%cde%'", true);
}
@Test void testRlikeOperator() {
SqlOperatorFixture f = fixture()
.setFor(SqlLibraryOperators.RLIKE, VM_EXPAND);
checkRlike(f.withLibrary(SqlLibrary.SPARK));
checkRlike(f.withLibrary(SqlLibrary.HIVE));
checkRlikeFails(f.withLibrary(SqlLibrary.MYSQL));
checkRlikeFails(f.withLibrary(SqlLibrary.ORACLE));
}
void checkRlike(SqlOperatorFixture f) {
f.checkBoolean("'[email protected]' rlike '.+@*\\.com'", true);
f.checkBoolean("'[email protected]' rlike '.com$'", true);
f.checkBoolean("'acbd' rlike '^ac+'", true);
f.checkBoolean("'acb' rlike 'acb|efg'", true);
f.checkBoolean("'acb|efg' rlike 'acb\\|efg'", true);
f.checkBoolean("'Acbd' rlike '^ac+'", false);
f.checkBoolean("'[email protected]' rlike 'Merrisa_'", false);
f.checkBoolean("'abcdef' rlike '%cd%'", false);
f.setFor(SqlLibraryOperators.NOT_RLIKE, VM_EXPAND);
f.checkBoolean("'Merrisagmail' not rlike '.+@*\\.com'", true);
f.checkBoolean("'acbd' not rlike '^ac+'", false);
f.checkBoolean("'acb|efg' not rlike 'acb\\|efg'", false);
f.checkBoolean("'[email protected]' not rlike 'Merrisa_'", true);
}
void checkRlikeFails(SqlOperatorFixture f) {
final String noRlike = "(?s).*No match found for function signature RLIKE";
f.checkFails("^'[email protected]' rlike '.+@*\\.com'^", noRlike, false);
f.checkFails("^'acb' rlike 'acb|efg'^", noRlike, false);
final String noNotRlike =
"(?s).*No match found for function signature NOT RLIKE";
f.checkFails("^'abcdef' not rlike '%cd%'^", noNotRlike, false);
f.checkFails("^'[email protected]' not rlike 'Merrisa_'^", noNotRlike, false);
}
@Test void testLikeEscape() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.LIKE, VmName.EXPAND);
f.checkBoolean("'a_c' like 'a#_c' escape '#'", true);
f.checkBoolean("'axc' like 'a#_c' escape '#'", false);
f.checkBoolean("'a_c' like 'a\\_c' escape '\\'", true);
f.checkBoolean("'axc' like 'a\\_c' escape '\\'", false);
f.checkBoolean("'a%c' like 'a\\%c' escape '\\'", true);
f.checkBoolean("'a%cde' like 'a\\%c_e' escape '\\'", true);
f.checkBoolean("'abbc' like 'a%c' escape '\\'", true);
f.checkBoolean("'abbc' like 'a\\%c' escape '\\'", false);
}
@Test void testIlikeEscape() {
final SqlOperatorFixture f =
fixture().setFor(SqlLibraryOperators.ILIKE, VmName.EXPAND)
.withLibrary(SqlLibrary.POSTGRESQL);
f.checkBoolean("'a_c' ilike 'a#_C' escape '#'", true);
f.checkBoolean("'axc' ilike 'a#_C' escape '#'", false);
f.checkBoolean("'a_c' ilike 'a\\_C' escape '\\'", true);
f.checkBoolean("'axc' ilike 'a\\_C' escape '\\'", false);
f.checkBoolean("'a%c' ilike 'a\\%C' escape '\\'", true);
f.checkBoolean("'a%cde' ilike 'a\\%C_e' escape '\\'", true);
f.checkBoolean("'abbc' ilike 'a%C' escape '\\'", true);
f.checkBoolean("'abbc' ilike 'a\\%C' escape '\\'", false);
}
@Disabled("[CALCITE-525] Exception-handling in built-in functions")
@Test void testLikeEscape2() {
final SqlOperatorFixture f = fixture();
f.checkBoolean("'x' not like 'x' escape 'x'", true);
f.checkBoolean("'xyz' not like 'xyz' escape 'xyz'", true);
}
@Test void testLikeOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.LIKE, VmName.EXPAND);
f.checkBoolean("'' like ''", true);
f.checkBoolean("'a' like 'a'", true);
f.checkBoolean("'a' like 'b'", false);
f.checkBoolean("'a' like 'A'", false);
f.checkBoolean("'a' like 'a_'", false);
f.checkBoolean("'a' like '_a'", false);
f.checkBoolean("'a' like '%a'", true);
f.checkBoolean("'a' like '%a%'", true);
f.checkBoolean("'a' like 'a%'", true);
f.checkBoolean("'ab' like 'a_'", true);
f.checkBoolean("'abc' like 'a_'", false);
f.checkBoolean("'abcd' like 'a%'", true);
f.checkBoolean("'ab' like '_b'", true);
f.checkBoolean("'abcd' like '_d'", false);
f.checkBoolean("'abcd' like '%d'", true);
f.checkBoolean("'ab\ncd' like 'ab%'", true);
f.checkBoolean("'abc\ncd' like 'ab%'", true);
f.checkBoolean("'123\n\n45\n' like '%'", true);
f.checkBoolean("'ab\ncd\nef' like '%cd%'", true);
f.checkBoolean("'ab\ncd\nef' like '%cde%'", false);
}
@Test void testIlikeOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlLibraryOperators.ILIKE, VmName.EXPAND);
final String noLike = "No match found for function signature ILIKE";
f.checkFails("^'a' ilike 'b'^", noLike, false);
f.checkFails("^'a' ilike 'b' escape 'c'^", noLike, false);
final String noNotLike = "No match found for function signature NOT ILIKE";
f.checkFails("^'a' not ilike 'b'^", noNotLike, false);
f.checkFails("^'a' not ilike 'b' escape 'c'^", noNotLike, false);
final SqlOperatorFixture f1 = f.withLibrary(SqlLibrary.POSTGRESQL);
f1.checkBoolean("'' ilike ''", true);
f1.checkBoolean("'a' ilike 'a'", true);
f1.checkBoolean("'a' ilike 'b'", false);
f1.checkBoolean("'a' ilike 'A'", true);
f1.checkBoolean("'a' ilike 'a_'", false);
f1.checkBoolean("'a' ilike '_a'", false);
f1.checkBoolean("'a' ilike '%a'", true);
f1.checkBoolean("'a' ilike '%A'", true);
f1.checkBoolean("'a' ilike '%a%'", true);
f1.checkBoolean("'a' ilike '%A%'", true);
f1.checkBoolean("'a' ilike 'a%'", true);
f1.checkBoolean("'a' ilike 'A%'", true);
f1.checkBoolean("'ab' ilike 'a_'", true);
f1.checkBoolean("'ab' ilike 'A_'", true);
f1.checkBoolean("'abc' ilike 'a_'", false);
f1.checkBoolean("'abcd' ilike 'a%'", true);
f1.checkBoolean("'abcd' ilike 'A%'", true);
f1.checkBoolean("'ab' ilike '_b'", true);
f1.checkBoolean("'ab' ilike '_B'", true);
f1.checkBoolean("'abcd' ilike '_d'", false);
f1.checkBoolean("'abcd' ilike '%d'", true);
f1.checkBoolean("'abcd' ilike '%D'", true);
f1.checkBoolean("'ab\ncd' ilike 'ab%'", true);
f1.checkBoolean("'ab\ncd' ilike 'aB%'", true);
f1.checkBoolean("'abc\ncd' ilike 'ab%'", true);
f1.checkBoolean("'abc\ncd' ilike 'Ab%'", true);
f1.checkBoolean("'123\n\n45\n' ilike '%'", true);
f1.checkBoolean("'ab\ncd\nef' ilike '%cd%'", true);
f1.checkBoolean("'ab\ncd\nef' ilike '%CD%'", true);
f1.checkBoolean("'ab\ncd\nef' ilike '%cde%'", false);
}
/** Test case for
* [CALCITE-1898]
* LIKE must match '.' (period) literally. */
@Test void testLikeDot() {
final SqlOperatorFixture f = fixture();
f.checkBoolean("'abc' like 'a.c'", false);
f.checkBoolean("'abcde' like '%c.e'", false);
f.checkBoolean("'abc.e' like '%c.e'", true);
}
@Test void testIlikeDot() {
final SqlOperatorFixture f = fixture()
.setFor(SqlLibraryOperators.ILIKE, VmName.EXPAND)
.withLibrary(SqlLibrary.POSTGRESQL);
f.checkBoolean("'abc' ilike 'a.c'", false);
f.checkBoolean("'abcde' ilike '%c.e'", false);
f.checkBoolean("'abc.e' ilike '%c.e'", true);
f.checkBoolean("'abc.e' ilike '%c.E'", true);
}
@Test void testNotSimilarToOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.NOT_SIMILAR_TO, VM_EXPAND);
f.checkBoolean("'ab' not similar to 'a_'", false);
f.checkBoolean("'aabc' not similar to 'ab*c+d'", true);
f.checkBoolean("'ab' not similar to 'a' || '_'", false);
f.checkBoolean("'ab' not similar to 'ba_'", true);
f.checkBoolean("cast(null as varchar(2)) not similar to 'a_'", null);
f.checkBoolean("cast(null as varchar(3))"
+ " not similar to cast(null as char(2))", null);
}
@Test void testSimilarToOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.SIMILAR_TO, VmName.EXPAND);
// like LIKE
f.checkBoolean("'' similar to ''", true);
f.checkBoolean("'a' similar to 'a'", true);
f.checkBoolean("'a' similar to 'b'", false);
f.checkBoolean("'a' similar to 'A'", false);
f.checkBoolean("'a' similar to 'a_'", false);
f.checkBoolean("'a' similar to '_a'", false);
f.checkBoolean("'a' similar to '%a'", true);
f.checkBoolean("'a' similar to '%a%'", true);
f.checkBoolean("'a' similar to 'a%'", true);
f.checkBoolean("'ab' similar to 'a_'", true);
f.checkBoolean("'abc' similar to 'a_'", false);
f.checkBoolean("'abcd' similar to 'a%'", true);
f.checkBoolean("'ab' similar to '_b'", true);
f.checkBoolean("'abcd' similar to '_d'", false);
f.checkBoolean("'abcd' similar to '%d'", true);
f.checkBoolean("'ab\ncd' similar to 'ab%'", true);
f.checkBoolean("'abc\ncd' similar to 'ab%'", true);
f.checkBoolean("'123\n\n45\n' similar to '%'", true);
f.checkBoolean("'ab\ncd\nef' similar to '%cd%'", true);
f.checkBoolean("'ab\ncd\nef' similar to '%cde%'", false);
// simple regular expressions
// ab*c+d matches acd, abcd, acccd, abcccd but not abd, aabc
f.checkBoolean("'acd' similar to 'ab*c+d'", true);
f.checkBoolean("'abcd' similar to 'ab*c+d'", true);
f.checkBoolean("'acccd' similar to 'ab*c+d'", true);
f.checkBoolean("'abcccd' similar to 'ab*c+d'", true);
f.checkBoolean("'abd' similar to 'ab*c+d'", false);
f.checkBoolean("'aabc' similar to 'ab*c+d'", false);
// compound regular expressions
// x(ab|c)*y matches xy, xccy, xababcy but not xbcy
f.checkBoolean("'xy' similar to 'x(ab|c)*y'", true);
f.checkBoolean("'xccy' similar to 'x(ab|c)*y'", true);
f.checkBoolean("'xababcy' similar to 'x(ab|c)*y'", true);
f.checkBoolean("'xbcy' similar to 'x(ab|c)*y'", false);
// x(ab|c)+y matches xccy, xababcy but not xy, xbcy
f.checkBoolean("'xy' similar to 'x(ab|c)+y'", false);
f.checkBoolean("'xccy' similar to 'x(ab|c)+y'", true);
f.checkBoolean("'xababcy' similar to 'x(ab|c)+y'", true);
f.checkBoolean("'xbcy' similar to 'x(ab|c)+y'", false);
f.checkBoolean("'ab' similar to 'a%' ", true);
f.checkBoolean("'a' similar to 'a%' ", true);
f.checkBoolean("'abcd' similar to 'a_' ", false);
f.checkBoolean("'abcd' similar to 'a%' ", true);
f.checkBoolean("'1a' similar to '_a' ", true);
f.checkBoolean("'123aXYZ' similar to '%a%'", true);
f.checkBoolean("'123aXYZ' similar to '_%_a%_' ", true);
f.checkBoolean("'xy' similar to '(xy)' ", true);
f.checkBoolean("'abd' similar to '[ab][bcde]d' ", true);
f.checkBoolean("'bdd' similar to '[ab][bcde]d' ", true);
f.checkBoolean("'abd' similar to '[ab]d' ", false);
f.checkBoolean("'cd' similar to '[a-e]d' ", true);
f.checkBoolean("'amy' similar to 'amy|fred' ", true);
f.checkBoolean("'fred' similar to 'amy|fred' ", true);
f.checkBoolean("'mike' similar to 'amy|fred' ", false);
f.checkBoolean("'acd' similar to 'ab*c+d' ", true);
f.checkBoolean("'accccd' similar to 'ab*c+d' ", true);
f.checkBoolean("'abd' similar to 'ab*c+d' ", false);
f.checkBoolean("'aabc' similar to 'ab*c+d' ", false);
f.checkBoolean("'abb' similar to 'a(b{3})' ", false);
f.checkBoolean("'abbb' similar to 'a(b{3})' ", true);
f.checkBoolean("'abbbbb' similar to 'a(b{3})' ", false);
f.checkBoolean("'abbbbb' similar to 'ab{3,6}' ", true);
f.checkBoolean("'abbbbbbbb' similar to 'ab{3,6}' ", false);
f.checkBoolean("'' similar to 'ab?' ", false);
f.checkBoolean("'a' similar to 'ab?' ", true);
f.checkBoolean("'a' similar to 'a(b?)' ", true);
f.checkBoolean("'ab' similar to 'ab?' ", true);
f.checkBoolean("'ab' similar to 'a(b?)' ", true);
f.checkBoolean("'abb' similar to 'ab?' ", false);
f.checkBoolean("'ab' similar to 'a\\_' ESCAPE '\\' ", false);
f.checkBoolean("'ab' similar to 'a\\%' ESCAPE '\\' ", false);
f.checkBoolean("'a_' similar to 'a\\_' ESCAPE '\\' ", true);
f.checkBoolean("'a%' similar to 'a\\%' ESCAPE '\\' ", true);
f.checkBoolean("'a(b{3})' similar to 'a(b{3})' ", false);
f.checkBoolean("'a(b{3})' similar to 'a\\(b\\{3\\}\\)' ESCAPE '\\' ", true);
f.checkBoolean("'yd' similar to '[a-ey]d'", true);
f.checkBoolean("'yd' similar to '[^a-ey]d'", false);
f.checkBoolean("'yd' similar to '[^a-ex-z]d'", false);
f.checkBoolean("'yd' similar to '[a-ex-z]d'", true);
f.checkBoolean("'yd' similar to '[x-za-e]d'", true);
f.checkBoolean("'yd' similar to '[^a-ey]?d'", false);
f.checkBoolean("'yyyd' similar to '[a-ey]*d'", true);
// range must be specified in []
f.checkBoolean("'yd' similar to 'x-zd'", false);
f.checkBoolean("'y' similar to 'x-z'", false);
f.checkBoolean("'cd' similar to '([a-e])d'", true);
f.checkBoolean("'xy' similar to 'x*?y'", true);
f.checkBoolean("'y' similar to 'x*?y'", true);
f.checkBoolean("'y' similar to '(x?)*y'", true);
f.checkBoolean("'y' similar to 'x+?y'", false);
f.checkBoolean("'y' similar to 'x?+y'", true);
f.checkBoolean("'y' similar to 'x*+y'", true);
// dot is a wildcard for SIMILAR TO but not LIKE
f.checkBoolean("'abc' similar to 'a.c'", true);
f.checkBoolean("'a.c' similar to 'a.c'", true);
f.checkBoolean("'abcd' similar to 'a.*d'", true);
f.checkBoolean("'abc' like 'a.c'", false);
f.checkBoolean("'a.c' like 'a.c'", true);
f.checkBoolean("'abcd' like 'a.*d'", false);
// The following two tests throws exception(They probably should).
// "Dangling meta character '*' near index 2"
if (f.brokenTestsEnabled()) {
f.checkBoolean("'y' similar to 'x+*y'", true);
f.checkBoolean("'y' similar to 'x?*y'", true);
}
// some negative tests
f.checkFails("'yd' similar to '[x-ze-a]d'",
"Illegal character range near index 6\n"
+ "\\[x-ze-a\\]d\n"
+ " \\^",
true); // illegal range
// Slightly different error message from JDK 13 onwards
final String expectedError =
TestUtil.getJavaMajorVersion() >= 13
? "Illegal repetition near index 22\n"
+ "\\[\\:LOWER\\:\\]\\{2\\}\\[\\:DIGIT\\:\\]\\{,5\\}\n"
+ " \\^"
: "Illegal repetition near index 20\n"
+ "\\[\\:LOWER\\:\\]\\{2\\}\\[\\:DIGIT\\:\\]\\{,5\\}\n"
+ " \\^";
f.checkFails("'yd3223' similar to '[:LOWER:]{2}[:DIGIT:]{,5}'",
expectedError, true);
if (Bug.CALCITE_2539_FIXED) {
f.checkFails("'cd' similar to '[(a-e)]d' ",
"Invalid regular expression: \\[\\(a-e\\)\\]d at 1",
true);
f.checkFails("'yd' similar to '[(a-e)]d' ",
"Invalid regular expression: \\[\\(a-e\\)\\]d at 1",
true);
}
// all the following tests wrong results due to missing functionality
// or defect (FRG-375, 377).
if (Bug.FRG375_FIXED) {
f.checkBoolean("'cd' similar to '[a-e^c]d' ", false); // FRG-375
}
// following tests use regular character set identifiers.
// Not implemented yet. FRG-377.
if (Bug.FRG377_FIXED) {
f.checkBoolean("'y' similar to '[:ALPHA:]*'", true);
f.checkBoolean("'yd32' similar to '[:LOWER:]{2}[:DIGIT:]*'", true);
f.checkBoolean("'yd32' similar to '[:ALNUM:]*'", true);
f.checkBoolean("'yd32' similar to '[:ALNUM:]*[:DIGIT:]?'", true);
f.checkBoolean("'yd32' similar to '[:ALNUM:]?[:DIGIT:]*'", false);
f.checkBoolean("'yd3223' similar to '([:LOWER:]{2})[:DIGIT:]{2,5}'",
true);
f.checkBoolean("'yd3223' similar to '[:LOWER:]{2}[:DIGIT:]{2,}'", true);
f.checkBoolean("'yd3223' similar to '[:LOWER:]{2}||[:DIGIT:]{4}'", true);
f.checkBoolean("'yd3223' similar to '[:LOWER:]{2}[:DIGIT:]{3}'", false);
f.checkBoolean("'yd 3223' similar to '[:UPPER:]{2} [:DIGIT:]{3}'",
false);
f.checkBoolean("'YD 3223' similar to '[:UPPER:]{2} [:DIGIT:]{3}'",
false);
f.checkBoolean("'YD 3223' similar to "
+ "'[:UPPER:]{2}||[:WHITESPACE:]*[:DIGIT:]{4}'", true);
f.checkBoolean("'YD\t3223' similar to "
+ "'[:UPPER:]{2}[:SPACE:]*[:DIGIT:]{4}'", false);
f.checkBoolean("'YD\t3223' similar to "
+ "'[:UPPER:]{2}[:WHITESPACE:]*[:DIGIT:]{4}'", true);
f.checkBoolean("'YD\t\t3223' similar to "
+ "'([:UPPER:]{2}[:WHITESPACE:]+)||[:DIGIT:]{4}'", true);
}
}
@Test void testEscapeOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.ESCAPE, VM_EXPAND);
}
@Test void testConvertFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.CONVERT, VM_FENNEL, VM_JAVA);
}
@Test void testTranslateFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.TRANSLATE, VM_FENNEL, VM_JAVA);
}
@Test void testTranslate3Func() {
final SqlOperatorFixture f = fixture()
.setFor(SqlLibraryOperators.TRANSLATE3)
.withLibrary(SqlLibrary.ORACLE);
f.checkString("translate('aabbcc', 'ab', '+-')",
"++--cc", "VARCHAR(6) NOT NULL");
f.checkString("translate('aabbcc', 'ab', 'ba')",
"bbaacc", "VARCHAR(6) NOT NULL");
f.checkString("translate('aabbcc', 'ab', '')",
"cc", "VARCHAR(6) NOT NULL");
f.checkString("translate('aabbcc', '', '+-')",
"aabbcc", "VARCHAR(6) NOT NULL");
f.checkString("translate(cast('aabbcc' as varchar(10)), 'ab', '+-')",
"++--cc", "VARCHAR(10) NOT NULL");
f.checkNull("translate(cast(null as varchar(7)), 'ab', '+-')");
f.checkNull("translate('aabbcc', cast(null as varchar(2)), '+-')");
f.checkNull("translate('aabbcc', 'ab', cast(null as varchar(2)))");
}
@Test void testOverlayFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.OVERLAY, VmName.EXPAND);
f.checkString("overlay('ABCdef' placing 'abc' from 1)",
"abcdef", "VARCHAR(9) NOT NULL");
f.checkString("overlay('ABCdef' placing 'abc' from 1 for 2)",
"abcCdef", "VARCHAR(9) NOT NULL");
if (f.brokenTestsEnabled()) {
f.checkString("overlay(cast('ABCdef' as varchar(10)) placing "
+ "cast('abc' as char(5)) from 1 for 2)",
"abc Cdef", "VARCHAR(15) NOT NULL");
}
if (f.brokenTestsEnabled()) {
f.checkString("overlay(cast('ABCdef' as char(10)) placing "
+ "cast('abc' as char(5)) from 1 for 2)",
"abc Cdef ",
"VARCHAR(15) NOT NULL");
}
f.checkNull("overlay('ABCdef' placing 'abc'"
+ " from 1 for cast(null as integer))");
f.checkNull("overlay(cast(null as varchar(1)) placing 'abc' from 1)");
f.checkString("overlay(x'ABCdef' placing x'abcd' from 1)",
"abcdef", "VARBINARY(5) NOT NULL");
f.checkString("overlay(x'ABCDEF1234' placing x'2345' from 1 for 2)",
"2345ef1234", "VARBINARY(7) NOT NULL");
if (f.brokenTestsEnabled()) {
f.checkString("overlay(cast(x'ABCdef' as varbinary(5)) placing "
+ "cast(x'abcd' as binary(3)) from 1 for 2)",
"abc Cdef", "VARBINARY(8) NOT NULL");
}
if (f.brokenTestsEnabled()) {
f.checkString("overlay(cast(x'ABCdef' as binary(5)) placing "
+ "cast(x'abcd' as binary(3)) from 1 for 2)",
"abc Cdef ", "VARBINARY(8) NOT NULL");
}
f.checkNull("overlay(x'ABCdef' placing x'abcd'"
+ " from 1 for cast(null as integer))");
f.checkNull("overlay(cast(null as varbinary(1)) placing x'abcd' from 1)");
f.checkNull("overlay(x'abcd' placing x'abcd' from cast(null as integer))");
}
@Test void testPositionFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.POSITION, VmName.EXPAND);
f.checkScalarExact("position('b' in 'abc')", 2);
f.checkScalarExact("position('' in 'abc')", 1);
f.checkScalarExact("position('b' in 'abcabc' FROM 3)", 5);
f.checkScalarExact("position('b' in 'abcabc' FROM 5)", 5);
f.checkScalarExact("position('b' in 'abcabc' FROM 6)", 0);
f.checkScalarExact("position('b' in 'abcabc' FROM -5)", 0);
f.checkScalarExact("position('' in 'abc' FROM 3)", 3);
f.checkScalarExact("position('' in 'abc' FROM 10)", 0);
f.checkScalarExact("position(x'bb' in x'aabbcc')", 2);
f.checkScalarExact("position(x'' in x'aabbcc')", 1);
f.checkScalarExact("position(x'bb' in x'aabbccaabbcc' FROM 3)", 5);
f.checkScalarExact("position(x'bb' in x'aabbccaabbcc' FROM 5)", 5);
f.checkScalarExact("position(x'bb' in x'aabbccaabbcc' FROM 6)", 0);
f.checkScalarExact("position(x'bb' in x'aabbccaabbcc' FROM -5)", 0);
f.checkScalarExact("position(x'cc' in x'aabbccdd' FROM 2)", 3);
f.checkScalarExact("position(x'' in x'aabbcc' FROM 3)", 3);
f.checkScalarExact("position(x'' in x'aabbcc' FROM 10)", 0);
// FRG-211
f.checkScalarExact("position('tra' in 'fdgjklewrtra')", 10);
f.checkNull("position(cast(null as varchar(1)) in '0010')");
f.checkNull("position('a' in cast(null as varchar(1)))");
f.checkScalar("position(cast('a' as char) in cast('bca' as varchar))",
3, "INTEGER NOT NULL");
}
@Test void testReplaceFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.REPLACE, VmName.EXPAND);
f.checkString("REPLACE('ciao', 'ciao', '')", "",
"VARCHAR(4) NOT NULL");
f.checkString("REPLACE('hello world', 'o', '')", "hell wrld",
"VARCHAR(11) NOT NULL");
f.checkNull("REPLACE(cast(null as varchar(5)), 'ciao', '')");
f.checkNull("REPLACE('ciao', cast(null as varchar(3)), 'zz')");
f.checkNull("REPLACE('ciao', 'bella', cast(null as varchar(3)))");
}
@Test void testCharLengthFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.CHAR_LENGTH, VmName.EXPAND);
f.checkScalarExact("char_length('abc')", 3);
f.checkNull("char_length(cast(null as varchar(1)))");
}
@Test void testCharacterLengthFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.CHARACTER_LENGTH, VmName.EXPAND);
f.checkScalarExact("CHARACTER_LENGTH('abc')", 3);
f.checkNull("CHARACTER_LENGTH(cast(null as varchar(1)))");
}
@Test void testOctetLengthFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.OCTET_LENGTH, VmName.EXPAND);
f.checkScalarExact("OCTET_LENGTH(x'aabbcc')", 3);
f.checkNull("OCTET_LENGTH(cast(null as varbinary(1)))");
}
@Test void testAsciiFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.ASCII, VmName.EXPAND);
f.checkScalarExact("ASCII('')", 0);
f.checkScalarExact("ASCII('a')", 97);
f.checkScalarExact("ASCII('1')", 49);
f.checkScalarExact("ASCII('abc')", 97);
f.checkScalarExact("ASCII('ABC')", 65);
f.checkScalarExact("ASCII(_UTF8'\u0082')", 130);
f.checkScalarExact("ASCII(_UTF8'\u5B57')", 23383);
f.checkScalarExact("ASCII(_UTF8'\u03a9')", 937); // omega
f.checkNull("ASCII(cast(null as varchar(1)))");
}
@Test void testToBase64() {
final SqlOperatorFixture f = fixture().withLibrary(SqlLibrary.MYSQL);
f.setFor(SqlLibraryOperators.TO_BASE64);
f.checkString("to_base64(x'546869732069732061207465737420537472696e672e')",
"VGhpcyBpcyBhIHRlc3QgU3RyaW5nLg==",
"VARCHAR NOT NULL");
f.checkString("to_base64(x'546869732069732061207465737420537472696e672e20636865"
+ "636b20726573756c7465206f7574206f66203736546869732069732061207465737420537472696e"
+ "672e546869732069732061207465737420537472696e672e54686973206973206120746573742053"
+ "7472696e672e546869732069732061207465737420537472696e672e546869732069732061207465"
+ "737420537472696e672e20546869732069732061207465737420537472696e672e20636865636b20"
+ "726573756c7465206f7574206f66203736546869732069732061207465737420537472696e672e54"
+ "6869732069732061207465737420537472696e672e54686973206973206120746573742053747269"
+ "6e672e546869732069732061207465737420537472696e672e546869732069732061207465737420"
+ "537472696e672e20546869732069732061207465737420537472696e672e20636865636b20726573"
+ "756c7465206f7574206f66203736546869732069732061207465737420537472696e672e54686973"
+ "2069732061207465737420537472696e672e546869732069732061207465737420537472696e672e"
+ "546869732069732061207465737420537472696e672e546869732069732061207465737420537472"
+ "696e672e')",
"VGhpcyBpcyBhIHRlc3QgU3RyaW5nLiBjaGVjayByZXN1bHRlIG91dCBvZiA3NlRoaXMgaXMgYSB0\n"
+ "ZXN0IFN0cmluZy5UaGlzIGlzIGEgdGVzdCBTdHJpbmcuVGhpcyBpcyBhIHRlc3QgU3RyaW5nLlRo\n"
+ "aXMgaXMgYSB0ZXN0IFN0cmluZy5UaGlzIGlzIGEgdGVzdCBTdHJpbmcuIFRoaXMgaXMgYSB0ZXN0\n"
+ "IFN0cmluZy4gY2hlY2sgcmVzdWx0ZSBvdXQgb2YgNzZUaGlzIGlzIGEgdGVzdCBTdHJpbmcuVGhp\n"
+ "cyBpcyBhIHRlc3QgU3RyaW5nLlRoaXMgaXMgYSB0ZXN0IFN0cmluZy5UaGlzIGlzIGEgdGVzdCBT\n"
+ "dHJpbmcuVGhpcyBpcyBhIHRlc3QgU3RyaW5nLiBUaGlzIGlzIGEgdGVzdCBTdHJpbmcuIGNoZWNr\n"
+ "IHJlc3VsdGUgb3V0IG9mIDc2VGhpcyBpcyBhIHRlc3QgU3RyaW5nLlRoaXMgaXMgYSB0ZXN0IFN0\n"
+ "cmluZy5UaGlzIGlzIGEgdGVzdCBTdHJpbmcuVGhpcyBpcyBhIHRlc3QgU3RyaW5nLlRoaXMgaXMg\n"
+ "YSB0ZXN0IFN0cmluZy4=",
"VARCHAR NOT NULL");
f.checkString("to_base64('This is a test String.')",
"VGhpcyBpcyBhIHRlc3QgU3RyaW5nLg==",
"VARCHAR NOT NULL");
f.checkString("to_base64('This is a test String. check resulte out of 76T"
+ "his is a test String.This is a test String.This is a test String.This is a "
+ "test String.This is a test String. This is a test String. check resulte out "
+ "of 76This is a test String.This is a test String.This is a test String.This "
+ "is a test String.This is a test String. This is a test String. check resulte "
+ "out of 76This is a test String.This is a test String.This is a test String."
+ "This is a test String.This is a test String.')",
"VGhpcyBpcyBhIHRlc3QgU3RyaW5nLiBjaGVjayByZXN1bHRlIG91dCBvZiA3NlRoaXMgaXMgYSB0\n"
+ "ZXN0IFN0cmluZy5UaGlzIGlzIGEgdGVzdCBTdHJpbmcuVGhpcyBpcyBhIHRlc3QgU3RyaW5nLlRo\n"
+ "aXMgaXMgYSB0ZXN0IFN0cmluZy5UaGlzIGlzIGEgdGVzdCBTdHJpbmcuIFRoaXMgaXMgYSB0ZXN0\n"
+ "IFN0cmluZy4gY2hlY2sgcmVzdWx0ZSBvdXQgb2YgNzZUaGlzIGlzIGEgdGVzdCBTdHJpbmcuVGhp\n"
+ "cyBpcyBhIHRlc3QgU3RyaW5nLlRoaXMgaXMgYSB0ZXN0IFN0cmluZy5UaGlzIGlzIGEgdGVzdCBT\n"
+ "dHJpbmcuVGhpcyBpcyBhIHRlc3QgU3RyaW5nLiBUaGlzIGlzIGEgdGVzdCBTdHJpbmcuIGNoZWNr\n"
+ "IHJlc3VsdGUgb3V0IG9mIDc2VGhpcyBpcyBhIHRlc3QgU3RyaW5nLlRoaXMgaXMgYSB0ZXN0IFN0\n"
+ "cmluZy5UaGlzIGlzIGEgdGVzdCBTdHJpbmcuVGhpcyBpcyBhIHRlc3QgU3RyaW5nLlRoaXMgaXMg\n"
+ "YSB0ZXN0IFN0cmluZy4=",
"VARCHAR NOT NULL");
f.checkString("to_base64('')", "", "VARCHAR NOT NULL");
f.checkString("to_base64('a')", "YQ==", "VARCHAR NOT NULL");
f.checkString("to_base64(x'61')", "YQ==", "VARCHAR NOT NULL");
}
@Test void testFromBase64() {
final SqlOperatorFixture f = fixture()
.setFor(SqlLibraryOperators.FROM_BASE64)
.withLibrary(SqlLibrary.MYSQL);
f.checkString("from_base64('VGhpcyBpcyBhIHRlc3QgU3RyaW5nLg==')",
"546869732069732061207465737420537472696e672e",
"VARBINARY NOT NULL");
f.checkString("from_base64('VGhpcyBpcyBhIHRlc\t3QgU3RyaW5nLg==')",
"546869732069732061207465737420537472696e672e",
"VARBINARY NOT NULL");
f.checkString("from_base64('VGhpcyBpcyBhIHRlc\t3QgU3\nRyaW5nLg==')",
"546869732069732061207465737420537472696e672e",
"VARBINARY NOT NULL");
f.checkString("from_base64('VGhpcyB pcyBhIHRlc3Qg\tU3Ry\naW5nLg==')",
"546869732069732061207465737420537472696e672e",
"VARBINARY NOT NULL");
f.checkNull("from_base64('-1')");
f.checkNull("from_base64('-100')");
}
@Test void testMd5() {
final SqlOperatorFixture f = fixture()
.setFor(SqlLibraryOperators.MD5)
.withLibrary(SqlLibrary.MYSQL);
f.checkString("md5(x'')",
"d41d8cd98f00b204e9800998ecf8427e",
"VARCHAR NOT NULL");
f.checkString("md5('')",
"d41d8cd98f00b204e9800998ecf8427e",
"VARCHAR NOT NULL");
f.checkString("md5('ABC')",
"902fbdd2b1df0c4f70b4a5d23525e932",
"VARCHAR NOT NULL");
f.checkString("md5(x'414243')",
"902fbdd2b1df0c4f70b4a5d23525e932",
"VARCHAR NOT NULL");
}
@Test void testSha1() {
final SqlOperatorFixture f = fixture()
.setFor(SqlLibraryOperators.SHA1)
.withLibrary(SqlLibrary.MYSQL);
f.checkString("sha1(x'')",
"da39a3ee5e6b4b0d3255bfef95601890afd80709",
"VARCHAR NOT NULL");
f.checkString("sha1('')",
"da39a3ee5e6b4b0d3255bfef95601890afd80709",
"VARCHAR NOT NULL");
f.checkString("sha1('ABC')",
"3c01bdbb26f358bab27f267924aa2c9a03fcfdb8",
"VARCHAR NOT NULL");
f.checkString("sha1(x'414243')",
"3c01bdbb26f358bab27f267924aa2c9a03fcfdb8",
"VARCHAR NOT NULL");
}
@Test void testRepeatFunc() {
final SqlOperatorFixture f = fixture()
.setFor(SqlLibraryOperators.REPEAT)
.withLibrary(SqlLibrary.MYSQL);
f.checkString("REPEAT('a', -100)", "", "VARCHAR(1) NOT NULL");
f.checkString("REPEAT('a', -1)", "", "VARCHAR(1) NOT NULL");
f.checkString("REPEAT('a', 0)", "", "VARCHAR(1) NOT NULL");
f.checkString("REPEAT('a', 2)", "aa", "VARCHAR(1) NOT NULL");
f.checkString("REPEAT('abc', 3)", "abcabcabc", "VARCHAR(3) NOT NULL");
f.checkNull("REPEAT(cast(null as varchar(1)), -1)");
f.checkNull("REPEAT(cast(null as varchar(1)), 2)");
f.checkNull("REPEAT('abc', cast(null as integer))");
f.checkNull("REPEAT(cast(null as varchar(1)), cast(null as integer))");
}
@Test void testSpaceFunc() {
final SqlOperatorFixture f = fixture()
.setFor(SqlLibraryOperators.SPACE)
.withLibrary(SqlLibrary.MYSQL);
f.checkString("SPACE(-100)", "", "VARCHAR(2000) NOT NULL");
f.checkString("SPACE(-1)", "", "VARCHAR(2000) NOT NULL");
f.checkString("SPACE(0)", "", "VARCHAR(2000) NOT NULL");
f.checkString("SPACE(2)", " ", "VARCHAR(2000) NOT NULL");
f.checkString("SPACE(5)", " ", "VARCHAR(2000) NOT NULL");
f.checkNull("SPACE(cast(null as integer))");
}
@Test void testStrcmpFunc() {
final SqlOperatorFixture f = fixture()
.setFor(SqlLibraryOperators.STRCMP)
.withLibrary(SqlLibrary.MYSQL);
f.checkString("STRCMP('mytesttext', 'mytesttext')", "0", "INTEGER NOT NULL");
f.checkString("STRCMP('mytesttext', 'mytest_text')", "-1", "INTEGER NOT NULL");
f.checkString("STRCMP('mytest_text', 'mytesttext')", "1", "INTEGER NOT NULL");
f.checkNull("STRCMP('mytesttext', cast(null as varchar(1)))");
f.checkNull("STRCMP(cast(null as varchar(1)), 'mytesttext')");
}
@Test void testSoundexFunc() {
final SqlOperatorFixture f = fixture()
.setFor(SqlLibraryOperators.SOUNDEX)
.withLibrary(SqlLibrary.ORACLE);
f.checkString("SOUNDEX('TECH ON THE NET')", "T253", "VARCHAR(4) NOT NULL");
f.checkString("SOUNDEX('Miller')", "M460", "VARCHAR(4) NOT NULL");
f.checkString("SOUNDEX('miler')", "M460", "VARCHAR(4) NOT NULL");
f.checkString("SOUNDEX('myller')", "M460", "VARCHAR(4) NOT NULL");
f.checkString("SOUNDEX('muller')", "M460", "VARCHAR(4) NOT NULL");
f.checkString("SOUNDEX('m')", "M000", "VARCHAR(4) NOT NULL");
f.checkString("SOUNDEX('mu')", "M000", "VARCHAR(4) NOT NULL");
f.checkString("SOUNDEX('mile')", "M400", "VARCHAR(4) NOT NULL");
f.checkNull("SOUNDEX(cast(null as varchar(1)))");
f.checkFails("SOUNDEX(_UTF8'\u5B57\u5B57')", "The character is not mapped.*", true);
}
@Test void testDifferenceFunc() {
final SqlOperatorFixture f = fixture()
.setFor(SqlLibraryOperators.DIFFERENCE)
.withLibrary(SqlLibrary.POSTGRESQL);
f.checkScalarExact("DIFFERENCE('Miller', 'miller')", 4);
f.checkScalarExact("DIFFERENCE('Miller', 'myller')", 4);
f.checkScalarExact("DIFFERENCE('muller', 'miller')", 4);
f.checkScalarExact("DIFFERENCE('muller', 'miller')", 4);
f.checkScalarExact("DIFFERENCE('muller', 'milk')", 2);
f.checkScalarExact("DIFFERENCE('muller', 'mile')", 2);
f.checkScalarExact("DIFFERENCE('muller', 'm')", 1);
f.checkScalarExact("DIFFERENCE('muller', 'lee')", 0);
f.checkNull("DIFFERENCE('muller', cast(null as varchar(1)))");
f.checkNull("DIFFERENCE(cast(null as varchar(1)), 'muller')");
}
@Test void testReverseFunc() {
final SqlOperatorFixture f = fixture()
.setFor(SqlLibraryOperators.REVERSE)
.withLibrary(SqlLibrary.MYSQL);
f.checkString("reverse('')", "", "VARCHAR(0) NOT NULL");
f.checkString("reverse('123')", "321", "VARCHAR(3) NOT NULL");
f.checkString("reverse('abc')", "cba", "VARCHAR(3) NOT NULL");
f.checkString("reverse('ABC')", "CBA", "VARCHAR(3) NOT NULL");
f.checkString("reverse('Hello World')", "dlroW olleH",
"VARCHAR(11) NOT NULL");
f.checkString("reverse(_UTF8'\u4F60\u597D')", "\u597D\u4F60",
"VARCHAR(2) NOT NULL");
f.checkNull("reverse(cast(null as varchar(1)))");
}
@Test void testIfFunc() {
final SqlOperatorFixture f = fixture();
checkIf(f.withLibrary(SqlLibrary.BIG_QUERY));
checkIf(f.withLibrary(SqlLibrary.HIVE));
checkIf(f.withLibrary(SqlLibrary.SPARK));
}
private void checkIf(SqlOperatorFixture f) {
f.setFor(SqlLibraryOperators.IF);
f.checkString("if(1 = 2, 1, 2)", "2", "INTEGER NOT NULL");
f.checkString("if('abc'='xyz', 'abc', 'xyz')", "xyz",
"CHAR(3) NOT NULL");
f.checkString("if(substring('abc',1,2)='ab', 'abc', 'xyz')", "abc",
"CHAR(3) NOT NULL");
f.checkString("if(substring('abc',1,2)='ab', 'abc', 'wxyz')", "abc ",
"CHAR(4) NOT NULL");
// TRUE yields first arg, FALSE and UNKNOWN yield second arg
f.checkScalar("if(nullif(true,false), 5, 10)", 5, "INTEGER NOT NULL");
f.checkScalar("if(nullif(true,true), 5, 10)", 10, "INTEGER NOT NULL");
f.checkScalar("if(nullif(true,true), 5, 10)", 10, "INTEGER NOT NULL");
}
@Test void testUpperFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.UPPER, VmName.EXPAND);
f.checkString("upper('a')", "A", "CHAR(1) NOT NULL");
f.checkString("upper('A')", "A", "CHAR(1) NOT NULL");
f.checkString("upper('1')", "1", "CHAR(1) NOT NULL");
f.checkString("upper('aa')", "AA", "CHAR(2) NOT NULL");
f.checkNull("upper(cast(null as varchar(1)))");
}
@Test void testLeftFunc() {
final SqlOperatorFixture f = fixture();
Stream.of(SqlLibrary.MYSQL, SqlLibrary.POSTGRESQL)
.map(f::withLibrary)
.forEach(t -> {
t.setFor(SqlLibraryOperators.LEFT);
t.checkString("left('abcd', 3)", "abc", "VARCHAR(4) NOT NULL");
t.checkString("left('abcd', 0)", "", "VARCHAR(4) NOT NULL");
t.checkString("left('abcd', 5)", "abcd", "VARCHAR(4) NOT NULL");
t.checkString("left('abcd', -2)", "", "VARCHAR(4) NOT NULL");
t.checkNull("left(cast(null as varchar(1)), -2)");
t.checkNull("left('abcd', cast(null as Integer))");
// test for ByteString
t.checkString("left(x'ABCdef', 1)", "ab", "VARBINARY(3) NOT NULL");
t.checkString("left(x'ABCdef', 0)", "", "VARBINARY(3) NOT NULL");
t.checkString("left(x'ABCdef', 4)", "abcdef",
"VARBINARY(3) NOT NULL");
t.checkString("left(x'ABCdef', -2)", "", "VARBINARY(3) NOT NULL");
t.checkNull("left(cast(null as binary(1)), -2)");
t.checkNull("left(x'ABCdef', cast(null as Integer))");
});
}
@Test void testRightFunc() {
final SqlOperatorFixture f = fixture();
Stream.of(SqlLibrary.MYSQL, SqlLibrary.POSTGRESQL)
.map(f::withLibrary)
.forEach(t -> {
t.setFor(SqlLibraryOperators.RIGHT);
t.checkString("right('abcd', 3)", "bcd", "VARCHAR(4) NOT NULL");
t.checkString("right('abcd', 0)", "", "VARCHAR(4) NOT NULL");
t.checkString("right('abcd', 5)", "abcd", "VARCHAR(4) NOT NULL");
t.checkString("right('abcd', -2)", "", "VARCHAR(4) NOT NULL");
t.checkNull("right(cast(null as varchar(1)), -2)");
t.checkNull("right('abcd', cast(null as Integer))");
// test for ByteString
t.checkString("right(x'ABCdef', 1)", "ef", "VARBINARY(3) NOT NULL");
t.checkString("right(x'ABCdef', 0)", "", "VARBINARY(3) NOT NULL");
t.checkString("right(x'ABCdef', 4)", "abcdef",
"VARBINARY(3) NOT NULL");
t.checkString("right(x'ABCdef', -2)", "", "VARBINARY(3) NOT NULL");
t.checkNull("right(cast(null as binary(1)), -2)");
t.checkNull("right(x'ABCdef', cast(null as Integer))");
});
}
@Test void testRegexpReplaceFunc() {
final SqlOperatorFixture f = fixture();
Stream.of(SqlLibrary.MYSQL, SqlLibrary.ORACLE)
.map(f::withLibrary)
.forEach(t -> {
t.setFor(SqlLibraryOperators.REGEXP_REPLACE);
t.checkString("regexp_replace('a b c', 'b', 'X')", "a X c",
"VARCHAR NOT NULL");
t.checkString("regexp_replace('abc def ghi', '[a-z]+', 'X')", "X X X",
"VARCHAR NOT NULL");
t.checkString("regexp_replace('100-200', '(\\d+)', 'num')", "num-num",
"VARCHAR NOT NULL");
t.checkString("regexp_replace('100-200', '(-)', '###')", "100###200",
"VARCHAR NOT NULL");
t.checkNull("regexp_replace(cast(null as varchar), '(-)', '###')");
t.checkNull("regexp_replace('100-200', cast(null as varchar), '###')");
t.checkNull("regexp_replace('100-200', '(-)', cast(null as varchar))");
t.checkString("regexp_replace('abc def ghi', '[a-z]+', 'X', 2)", "aX X X",
"VARCHAR NOT NULL");
t.checkString("regexp_replace('abc def ghi', '[a-z]+', 'X', 1, 3)", "abc def X",
"VARCHAR NOT NULL");
t.checkString("regexp_replace('abc def GHI', '[a-z]+', 'X', 1, 3, 'c')", "abc def GHI",
"VARCHAR NOT NULL");
t.checkString("regexp_replace('abc def GHI', '[a-z]+', 'X', 1, 3, 'i')", "abc def X",
"VARCHAR NOT NULL");
t.checkString("regexp_replace('abc def GHI', '[a-z]+', 'X', 1, 3, 'i')", "abc def X",
"VARCHAR NOT NULL");
t.checkString("regexp_replace('abc\t\ndef\t\nghi', '\t', '+')", "abc+\ndef+\nghi",
"VARCHAR NOT NULL");
t.checkString("regexp_replace('abc\t\ndef\t\nghi', '\t\n', '+')", "abc+def+ghi",
"VARCHAR NOT NULL");
t.checkString("regexp_replace('abc\t\ndef\t\nghi', '\\w+', '+')", "+\t\n+\t\n+",
"VARCHAR NOT NULL");
t.checkQuery("select regexp_replace('a b c', 'b', 'X')");
t.checkQuery("select regexp_replace('a b c', 'b', 'X', 1)");
t.checkQuery("select regexp_replace('a b c', 'b', 'X', 1, 3)");
t.checkQuery("select regexp_replace('a b c', 'b', 'X', 1, 3, 'i')");
});
}
@Test void testJsonExists() {
// default pathmode the default is: strict mode
final SqlOperatorFixture f = fixture();
f.checkBoolean("json_exists('{\"foo\":\"bar\"}', "
+ "'$.foo')", true);
f.checkBoolean("json_exists('{\"foo\":\"bar\"}', "
+ "'strict $.foo' false on error)", true);
f.checkBoolean("json_exists('{\"foo\":\"bar\"}', "
+ "'strict $.foo' true on error)", true);
f.checkBoolean("json_exists('{\"foo\":\"bar\"}', "
+ "'strict $.foo' unknown on error)", true);
f.checkBoolean("json_exists('{\"foo\":\"bar\"}', "
+ "'lax $.foo' false on error)", true);
f.checkBoolean("json_exists('{\"foo\":\"bar\"}', "
+ "'lax $.foo' true on error)", true);
f.checkBoolean("json_exists('{\"foo\":\"bar\"}', "
+ "'lax $.foo' unknown on error)", true);
f.checkBoolean("json_exists('{}', "
+ "'invalid $.foo' false on error)", false);
f.checkBoolean("json_exists('{}', "
+ "'invalid $.foo' true on error)", true);
f.checkBoolean("json_exists('{}', "
+ "'invalid $.foo' unknown on error)", null);
// not exists
f.checkBoolean("json_exists('{\"foo\":\"bar\"}', "
+ "'strict $.foo1' false on error)", false);
f.checkBoolean("json_exists('{\"foo\":\"bar\"}', "
+ "'strict $.foo1' true on error)", true);
f.checkBoolean("json_exists('{\"foo\":\"bar\"}', "
+ "'strict $.foo1' unknown on error)", null);
f.checkBoolean("json_exists('{\"foo\":\"bar\"}', "
+ "'lax $.foo1' true on error)", false);
f.checkBoolean("json_exists('{\"foo\":\"bar\"}', "
+ "'lax $.foo1' false on error)", false);
f.checkBoolean("json_exists('{\"foo\":\"bar\"}', "
+ "'lax $.foo1' error on error)", false);
f.checkBoolean("json_exists('{\"foo\":\"bar\"}', "
+ "'lax $.foo1' unknown on error)", false);
// nulls
f.enableTypeCoercion(false)
.checkFails("json_exists(^null^, "
+ "'lax $' unknown on error)", "(?s).*Illegal use of 'NULL'.*", false);
f.checkString("json_exists(null, 'lax $' unknown on error)",
null, "BOOLEAN");
f.checkNull("json_exists(cast(null as varchar), "
+ "'lax $.foo1' unknown on error)");
}
@Test void testJsonValue() {
final SqlOperatorFixture f = fixture();
if (false) {
f.checkFails("json_value('{\"foo\":100}', 'lax $.foo1' error on empty)",
"(?s).*Empty result of JSON_VALUE function is not allowed.*",
true);
}
// default pathmode the default is: strict mode
f.checkString("json_value('{\"foo\":100}', '$.foo')",
"100", "VARCHAR(2000)");
// type casting test
f.checkString("json_value('{\"foo\":100}', 'strict $.foo')",
"100", "VARCHAR(2000)");
f.checkScalar("json_value('{\"foo\":100}', 'strict $.foo' returning integer)",
100, "INTEGER");
f.checkFails("json_value('{\"foo\":\"100\"}', 'strict $.foo' returning boolean)",
INVALID_CHAR_MESSAGE, true);
f.checkScalar("json_value('{\"foo\":100}', 'lax $.foo1' returning integer "
+ "null on empty)", isNullValue(), "INTEGER");
f.checkScalar("json_value('{\"foo\":\"100\"}', 'strict $.foo1' returning boolean "
+ "null on error)", isNullValue(), "BOOLEAN");
// lax test
f.checkString("json_value('{\"foo\":100}', 'lax $.foo' null on empty)",
"100", "VARCHAR(2000)");
f.checkString("json_value('{\"foo\":100}', 'lax $.foo' error on empty)",
"100", "VARCHAR(2000)");
f.checkString("json_value('{\"foo\":100}', 'lax $.foo' default 'empty' on empty)",
"100", "VARCHAR(2000)");
f.checkString("json_value('{\"foo\":100}', 'lax $.foo1' null on empty)",
null, "VARCHAR(2000)");
f.checkFails("json_value('{\"foo\":100}', 'lax $.foo1' error on empty)",
"(?s).*Empty result of JSON_VALUE function is not allowed.*", true);
f.checkString("json_value('{\"foo\":100}', 'lax $.foo1' default 'empty' on empty)",
"empty", "VARCHAR(2000)");
f.checkString("json_value('{\"foo\":{}}', 'lax $.foo' null on empty)",
null, "VARCHAR(2000)");
f.checkFails("json_value('{\"foo\":{}}', 'lax $.foo' error on empty)",
"(?s).*Empty result of JSON_VALUE function is not allowed.*", true);
f.checkString("json_value('{\"foo\":{}}', 'lax $.foo' default 'empty' on empty)",
"empty", "VARCHAR(2000)");
f.checkString("json_value('{\"foo\":100}', 'lax $.foo' null on error)",
"100", "VARCHAR(2000)");
f.checkString("json_value('{\"foo\":100}', 'lax $.foo' error on error)",
"100", "VARCHAR(2000)");
f.checkString("json_value('{\"foo\":100}', 'lax $.foo' default 'empty' on error)",
"100", "VARCHAR(2000)");
// path error test
f.checkString("json_value('{\"foo\":100}', 'invalid $.foo' null on error)",
null, "VARCHAR(2000)");
f.checkFails("json_value('{\"foo\":100}', 'invalid $.foo' error on error)",
"(?s).*Illegal jsonpath spec.*", true);
f.checkString("json_value('{\"foo\":100}', "
+ "'invalid $.foo' default 'empty' on error)",
"empty", "VARCHAR(2000)");
// strict test
f.checkString("json_value('{\"foo\":100}', 'strict $.foo' null on empty)",
"100", "VARCHAR(2000)");
f.checkString("json_value('{\"foo\":100}', 'strict $.foo' error on empty)",
"100", "VARCHAR(2000)");
f.checkString("json_value('{\"foo\":100}', "
+ "'strict $.foo' default 'empty' on empty)",
"100", "VARCHAR(2000)");
f.checkString("json_value('{\"foo\":100}', 'strict $.foo1' null on error)",
null, "VARCHAR(2000)");
f.checkFails("json_value('{\"foo\":100}', 'strict $.foo1' error on error)",
"(?s).*No results for path: \\$\\['foo1'\\].*", true);
f.checkString("json_value('{\"foo\":100}', "
+ "'strict $.foo1' default 'empty' on error)",
"empty", "VARCHAR(2000)");
f.checkString("json_value('{\"foo\":{}}', 'strict $.foo' null on error)",
null, "VARCHAR(2000)");
f.checkFails("json_value('{\"foo\":{}}', 'strict $.foo' error on error)",
"(?s).*Strict jsonpath mode requires scalar value, "
+ "and the actual value is: '\\{\\}'.*", true);
f.checkString("json_value('{\"foo\":{}}', "
+ "'strict $.foo' default 'empty' on error)",
"empty", "VARCHAR(2000)");
// nulls
f.enableTypeCoercion(false)
.checkFails("json_value(^null^, 'strict $')",
"(?s).*Illegal use of 'NULL'.*", false);
f.checkString("json_value(null, 'strict $')", null, "VARCHAR(2000)");
f.checkNull("json_value(cast(null as varchar), 'strict $')");
}
@Test void testJsonQuery() {
final SqlOperatorFixture f = fixture();
// default pathmode the default is: strict mode
f.checkString("json_query('{\"foo\":100}', '$' null on empty)",
"{\"foo\":100}", "VARCHAR(2000)");
// lax test
f.checkString("json_query('{\"foo\":100}', 'lax $' null on empty)",
"{\"foo\":100}", "VARCHAR(2000)");
f.checkString("json_query('{\"foo\":100}', 'lax $' error on empty)",
"{\"foo\":100}", "VARCHAR(2000)");
f.checkString("json_query('{\"foo\":100}', 'lax $' empty array on empty)",
"{\"foo\":100}", "VARCHAR(2000)");
f.checkString("json_query('{\"foo\":100}', 'lax $' empty object on empty)",
"{\"foo\":100}", "VARCHAR(2000)");
f.checkString("json_query('{\"foo\":100}', 'lax $.foo' null on empty)",
null, "VARCHAR(2000)");
f.checkFails("json_query('{\"foo\":100}', 'lax $.foo' error on empty)",
"(?s).*Empty result of JSON_QUERY function is not allowed.*", true);
f.checkString("json_query('{\"foo\":100}', 'lax $.foo' empty array on empty)",
"[]", "VARCHAR(2000)");
f.checkString("json_query('{\"foo\":100}', 'lax $.foo' empty object on empty)",
"{}", "VARCHAR(2000)");
// path error test
f.checkString("json_query('{\"foo\":100}', 'invalid $.foo' null on error)",
null, "VARCHAR(2000)");
f.checkFails("json_query('{\"foo\":100}', 'invalid $.foo' error on error)",
"(?s).*Illegal jsonpath spec.*", true);
f.checkString("json_query('{\"foo\":100}', "
+ "'invalid $.foo' empty array on error)",
"[]", "VARCHAR(2000)");
f.checkString("json_query('{\"foo\":100}', "
+ "'invalid $.foo' empty object on error)",
"{}", "VARCHAR(2000)");
// strict test
f.checkString("json_query('{\"foo\":100}', 'strict $' null on empty)",
"{\"foo\":100}", "VARCHAR(2000)");
f.checkString("json_query('{\"foo\":100}', 'strict $' error on empty)",
"{\"foo\":100}", "VARCHAR(2000)");
f.checkString("json_query('{\"foo\":100}', 'strict $' empty array on error)",
"{\"foo\":100}", "VARCHAR(2000)");
f.checkString("json_query('{\"foo\":100}', 'strict $' empty object on error)",
"{\"foo\":100}", "VARCHAR(2000)");
f.checkString("json_query('{\"foo\":100}', 'strict $.foo1' null on error)",
null, "VARCHAR(2000)");
f.checkFails("json_query('{\"foo\":100}', 'strict $.foo1' error on error)",
"(?s).*No results for path: \\$\\['foo1'\\].*", true);
f.checkString("json_query('{\"foo\":100}', 'strict $.foo1' empty array on error)",
"[]", "VARCHAR(2000)");
f.checkString("json_query('{\"foo\":100}', 'strict $.foo1' empty object on error)",
"{}", "VARCHAR(2000)");
f.checkString("json_query('{\"foo\":100}', 'strict $.foo' null on error)",
null, "VARCHAR(2000)");
f.checkFails("json_query('{\"foo\":100}', 'strict $.foo' error on error)",
"(?s).*Strict jsonpath mode requires array or object value, "
+ "and the actual value is: '100'.*", true);
f.checkString("json_query('{\"foo\":100}', 'strict $.foo' empty array on error)",
"[]", "VARCHAR(2000)");
f.checkString("json_query('{\"foo\":100}', 'strict $.foo' empty object on error)",
"{}", "VARCHAR(2000)");
// array wrapper test
f.checkString("json_query('{\"foo\":100}', 'strict $.foo' without wrapper)",
null, "VARCHAR(2000)");
f.checkString("json_query('{\"foo\":100}', 'strict $.foo' without array wrapper)",
null, "VARCHAR(2000)");
f.checkString("json_query('{\"foo\":100}', 'strict $.foo' with wrapper)",
"[100]", "VARCHAR(2000)");
f.checkString("json_query('{\"foo\":100}', 'strict $.foo' "
+ "with unconditional wrapper)",
"[100]", "VARCHAR(2000)");
f.checkString("json_query('{\"foo\":100}', 'strict $.foo' "
+ "with conditional wrapper)",
"[100]", "VARCHAR(2000)");
f.checkString("json_query('{\"foo\":[100]}', 'strict $.foo' without wrapper)",
"[100]", "VARCHAR(2000)");
f.checkString("json_query('{\"foo\":[100]}', 'strict $.foo' without array wrapper)",
"[100]", "VARCHAR(2000)");
f.checkString("json_query('{\"foo\":[100]}', 'strict $.foo' with wrapper)",
"[[100]]", "VARCHAR(2000)");
f.checkString("json_query('{\"foo\":[100]}', 'strict $.foo' "
+ "with unconditional wrapper)",
"[[100]]", "VARCHAR(2000)");
f.checkString("json_query('{\"foo\":[100]}', 'strict $.foo' "
+ "with conditional wrapper)",
"[100]", "VARCHAR(2000)");
// nulls
f.enableTypeCoercion(false).checkFails("json_query(^null^, 'lax $')",
"(?s).*Illegal use of 'NULL'.*", false);
f.checkString("json_query(null, 'lax $')", null, "VARCHAR(2000)");
f.checkNull("json_query(cast(null as varchar), 'lax $')");
}
@Test void testJsonPretty() {
final SqlOperatorFixture f = fixture();
f.checkString("json_pretty('{\"foo\":100}')",
"{\n \"foo\" : 100\n}", "VARCHAR(2000)");
f.checkString("json_pretty('[1,2,3]')",
"[ 1, 2, 3 ]", "VARCHAR(2000)");
f.checkString("json_pretty('null')",
"null", "VARCHAR(2000)");
// nulls
f.enableTypeCoercion(false).checkFails("json_pretty(^null^)",
"(?s).*Illegal use of 'NULL'.*", false);
f.checkString("json_pretty(null)", null, "VARCHAR(2000)");
f.checkNull("json_pretty(cast(null as varchar))");
}
@Test void testJsonStorageSize() {
final SqlOperatorFixture f = fixture();
f.checkString("json_storage_size('[100, \"sakila\", [1, 3, 5], 425.05]')",
"29", "INTEGER");
f.checkString("json_storage_size('{\"a\": 1000,\"b\": \"aa\", \"c\": \"[1, 3, 5]\"}')",
"35", "INTEGER");
f.checkString("json_storage_size('{\"a\": 1000, \"b\": \"wxyz\", \"c\": \"[1, 3]\"}')",
"34", "INTEGER");
f.checkString("json_storage_size('[100, \"json\", [[10, 20, 30], 3, 5], 425.05]')",
"36", "INTEGER");
f.checkString("json_storage_size('12')",
"2", "INTEGER");
f.checkString("json_storage_size('12' format json)",
"2", "INTEGER");
f.checkString("json_storage_size('null')",
"4", "INTEGER");
// nulls
f.enableTypeCoercion(false).checkFails("json_storage_size(^null^)",
"(?s).*Illegal use of 'NULL'.*", false);
f.checkString("json_storage_size(null)", null, "INTEGER");
f.checkNull("json_storage_size(cast(null as varchar))");
}
@Test void testJsonType() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlLibraryOperators.JSON_TYPE, VmName.EXPAND);
f.checkString("json_type('\"1\"')",
"STRING", "VARCHAR(20)");
f.checkString("json_type('1')",
"INTEGER", "VARCHAR(20)");
f.checkString("json_type('11.45')",
"DOUBLE", "VARCHAR(20)");
f.checkString("json_type('true')",
"BOOLEAN", "VARCHAR(20)");
f.checkString("json_type('null')",
"NULL", "VARCHAR(20)");
f.checkNull("json_type(cast(null as varchar(1)))");
f.checkString("json_type('{\"a\": [10, true]}')",
"OBJECT", "VARCHAR(20)");
f.checkString("json_type('{}')",
"OBJECT", "VARCHAR(20)");
f.checkString("json_type('[10, true]')",
"ARRAY", "VARCHAR(20)");
f.checkString("json_type('\"2019-01-27 21:24:00\"')",
"STRING", "VARCHAR(20)");
// nulls
f.enableTypeCoercion(false).checkFails("json_type(^null^)",
"(?s).*Illegal use of 'NULL'.*", false);
f.checkString("json_type(null)", null, "VARCHAR(20)");
f.checkNull("json_type(cast(null as varchar))");
}
@Test void testJsonDepth() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlLibraryOperators.JSON_DEPTH, VmName.EXPAND);
f.checkString("json_depth('1')",
"1", "INTEGER");
f.checkString("json_depth('11.45')",
"1", "INTEGER");
f.checkString("json_depth('true')",
"1", "INTEGER");
f.checkString("json_depth('\"2019-01-27 21:24:00\"')",
"1", "INTEGER");
f.checkString("json_depth('{}')",
"1", "INTEGER");
f.checkString("json_depth('[]')",
"1", "INTEGER");
f.checkString("json_depth('null')",
null, "INTEGER");
f.checkString("json_depth(cast(null as varchar(1)))",
null, "INTEGER");
f.checkString("json_depth('[10, true]')",
"2", "INTEGER");
f.checkString("json_depth('[[], {}]')",
"2", "INTEGER");
f.checkString("json_depth('{\"a\": [10, true]}')",
"3", "INTEGER");
f.checkString("json_depth('[10, {\"a\": [[1,2]]}]')",
"5", "INTEGER");
// nulls
f.enableTypeCoercion(false).checkFails("json_depth(^null^)",
"(?s).*Illegal use of 'NULL'.*", false);
f.checkString("json_depth(null)", null, "INTEGER");
f.checkNull("json_depth(cast(null as varchar))");
}
@Test void testJsonLength() {
final SqlOperatorFixture f = fixture();
// no path context
f.checkString("json_length('{}')",
"0", "INTEGER");
f.checkString("json_length('[]')",
"0", "INTEGER");
f.checkString("json_length('{\"foo\":100}')",
"1", "INTEGER");
f.checkString("json_length('{\"a\": 1, \"b\": {\"c\": 30}}')",
"2", "INTEGER");
f.checkString("json_length('[1, 2, {\"a\": 3}]')",
"3", "INTEGER");
// default pathmode the default is: strict mode
f.checkString("json_length('{\"foo\":100}', '$')",
"1", "INTEGER");
// lax test
f.checkString("json_length('{}', 'lax $')",
"0", "INTEGER");
f.checkString("json_length('[]', 'lax $')",
"0", "INTEGER");
f.checkString("json_length('{\"foo\":100}', 'lax $')",
"1", "INTEGER");
f.checkString("json_length('{\"a\": 1, \"b\": {\"c\": 30}}', 'lax $')",
"2", "INTEGER");
f.checkString("json_length('[1, 2, {\"a\": 3}]', 'lax $')",
"3", "INTEGER");
f.checkString("json_length('{\"a\": 1, \"b\": {\"c\": 30}}', 'lax $.b')",
"1", "INTEGER");
f.checkString("json_length('{\"foo\":100}', 'lax $.foo1')",
null, "INTEGER");
// strict test
f.checkString("json_length('{}', 'strict $')",
"0", "INTEGER");
f.checkString("json_length('[]', 'strict $')",
"0", "INTEGER");
f.checkString("json_length('{\"foo\":100}', 'strict $')",
"1", "INTEGER");
f.checkString("json_length('{\"a\": 1, \"b\": {\"c\": 30}}', 'strict $')",
"2", "INTEGER");
f.checkString("json_length('[1, 2, {\"a\": 3}]', 'strict $')",
"3", "INTEGER");
f.checkString("json_length('{\"a\": 1, \"b\": {\"c\": 30}}', 'strict $.b')",
"1", "INTEGER");
// catch error test
f.checkFails("json_length('{\"foo\":100}', 'invalid $.foo')",
"(?s).*Illegal jsonpath spec.*", true);
f.checkFails("json_length('{\"foo\":100}', 'strict $.foo1')",
"(?s).*No results for path.*", true);
// nulls
f.enableTypeCoercion(false).checkFails("json_length(^null^)",
"(?s).*Illegal use of 'NULL'.*", false);
f.checkString("json_length(null)", null, "INTEGER");
f.checkNull("json_length(cast(null as varchar))");
}
@Test void testJsonKeys() {
final SqlOperatorFixture f = fixture();
// no path context
f.checkString("json_keys('{}')",
"[]", "VARCHAR(2000)");
f.checkString("json_keys('[]')",
"null", "VARCHAR(2000)");
f.checkString("json_keys('{\"foo\":100}')",
"[\"foo\"]", "VARCHAR(2000)");
f.checkString("json_keys('{\"a\": 1, \"b\": {\"c\": 30}}')",
"[\"a\",\"b\"]", "VARCHAR(2000)");
f.checkString("json_keys('[1, 2, {\"a\": 3}]')",
"null", "VARCHAR(2000)");
// lax test
f.checkString("json_keys('{}', 'lax $')",
"[]", "VARCHAR(2000)");
f.checkString("json_keys('[]', 'lax $')",
"null", "VARCHAR(2000)");
f.checkString("json_keys('{\"foo\":100}', 'lax $')",
"[\"foo\"]", "VARCHAR(2000)");
f.checkString("json_keys('{\"a\": 1, \"b\": {\"c\": 30}}', 'lax $')",
"[\"a\",\"b\"]", "VARCHAR(2000)");
f.checkString("json_keys('[1, 2, {\"a\": 3}]', 'lax $')",
"null", "VARCHAR(2000)");
f.checkString("json_keys('{\"a\": 1, \"b\": {\"c\": 30}}', 'lax $.b')",
"[\"c\"]", "VARCHAR(2000)");
f.checkString("json_keys('{\"foo\":100}', 'lax $.foo1')",
"null", "VARCHAR(2000)");
// strict test
f.checkString("json_keys('{}', 'strict $')",
"[]", "VARCHAR(2000)");
f.checkString("json_keys('[]', 'strict $')",
"null", "VARCHAR(2000)");
f.checkString("json_keys('{\"foo\":100}', 'strict $')",
"[\"foo\"]", "VARCHAR(2000)");
f.checkString("json_keys('{\"a\": 1, \"b\": {\"c\": 30}}', 'strict $')",
"[\"a\",\"b\"]", "VARCHAR(2000)");
f.checkString("json_keys('[1, 2, {\"a\": 3}]', 'strict $')",
"null", "VARCHAR(2000)");
f.checkString("json_keys('{\"a\": 1, \"b\": {\"c\": 30}}', 'strict $.b')",
"[\"c\"]", "VARCHAR(2000)");
// catch error test
f.checkFails("json_keys('{\"foo\":100}', 'invalid $.foo')",
"(?s).*Illegal jsonpath spec.*", true);
f.checkFails("json_keys('{\"foo\":100}', 'strict $.foo1')",
"(?s).*No results for path.*", true);
// nulls
f.enableTypeCoercion(false).checkFails("json_keys(^null^)",
"(?s).*Illegal use of 'NULL'.*", false);
f.checkString("json_keys(null)", null, "VARCHAR(2000)");
f.checkNull("json_keys(cast(null as varchar))");
}
@Test void testJsonRemove() {
final SqlOperatorFixture f = fixture();
f.checkString("json_remove('{\"foo\":100}', '$.foo')",
"{}", "VARCHAR(2000)");
f.checkString("json_remove('{\"foo\":100, \"foo1\":100}', '$.foo')",
"{\"foo1\":100}", "VARCHAR(2000)");
f.checkString("json_remove('[\"a\", [\"b\", \"c\"], \"d\"]', '$[1][0]')",
"[\"a\",[\"c\"],\"d\"]", "VARCHAR(2000)");
f.checkString("json_remove('[\"a\", [\"b\", \"c\"], \"d\"]', '$[1]')",
"[\"a\",\"d\"]", "VARCHAR(2000)");
f.checkString("json_remove('[\"a\", [\"b\", \"c\"], \"d\"]', '$[0]', '$[0]')",
"[\"d\"]", "VARCHAR(2000)");
f.checkFails("json_remove('[\"a\", [\"b\", \"c\"], \"d\"]', '$')",
"(?s).*Invalid input for.*", true);
// nulls
f.enableTypeCoercion(false).checkFails("json_remove(^null^, '$')",
"(?s).*Illegal use of 'NULL'.*", false);
f.checkString("json_remove(null, '$')", null, "VARCHAR(2000)");
f.checkNull("json_remove(cast(null as varchar), '$')");
}
@Test void testJsonObject() {
final SqlOperatorFixture f = fixture();
f.checkString("json_object()", "{}", "VARCHAR(2000) NOT NULL");
f.checkString("json_object('foo': 'bar')",
"{\"foo\":\"bar\"}", "VARCHAR(2000) NOT NULL");
f.checkString("json_object('foo': 'bar', 'foo2': 'bar2')",
"{\"foo\":\"bar\",\"foo2\":\"bar2\"}", "VARCHAR(2000) NOT NULL");
f.checkString("json_object('foo': null)",
"{\"foo\":null}", "VARCHAR(2000) NOT NULL");
f.checkString("json_object('foo': null null on null)",
"{\"foo\":null}", "VARCHAR(2000) NOT NULL");
f.checkString("json_object('foo': null absent on null)",
"{}", "VARCHAR(2000) NOT NULL");
f.checkString("json_object('foo': 100)",
"{\"foo\":100}", "VARCHAR(2000) NOT NULL");
f.checkString("json_object('foo': json_object('foo': 'bar'))",
"{\"foo\":\"{\\\"foo\\\":\\\"bar\\\"}\"}", "VARCHAR(2000) NOT NULL");
f.checkString("json_object('foo': json_object('foo': 'bar') format json)",
"{\"foo\":{\"foo\":\"bar\"}}", "VARCHAR(2000) NOT NULL");
}
@Test void testJsonObjectAgg() {
final SqlOperatorFixture f = fixture();
f.checkAggType("json_objectagg('foo': 'bar')", "VARCHAR(2000) NOT NULL");
f.checkAggType("json_objectagg('foo': null)", "VARCHAR(2000) NOT NULL");
f.checkAggType("json_objectagg(100: 'bar')", "VARCHAR(2000) NOT NULL");
f.enableTypeCoercion(false).checkFails("^json_objectagg(100: 'bar')^",
"(?s).*Cannot apply.*", false);
final String[][] values = {
{"'foo'", "'bar'"},
{"'foo2'", "cast(null as varchar(2000))"},
{"'foo3'", "'bar3'"}
};
f.checkAggWithMultipleArgs("json_objectagg(x: x2)",
values,
isSingle("{\"foo\":\"bar\",\"foo2\":null,\"foo3\":\"bar3\"}"));
f.checkAggWithMultipleArgs("json_objectagg(x: x2 null on null)",
values,
isSingle("{\"foo\":\"bar\",\"foo2\":null,\"foo3\":\"bar3\"}"));
f.checkAggWithMultipleArgs("json_objectagg(x: x2 absent on null)",
values,
isSingle("{\"foo\":\"bar\",\"foo3\":\"bar3\"}"));
}
@Test void testJsonValueExpressionOperator() {
final SqlOperatorFixture f = fixture();
f.checkScalar("'{}' format json", "{}", "ANY NOT NULL");
f.checkScalar("'[1, 2, 3]' format json", "[1, 2, 3]", "ANY NOT NULL");
f.checkNull("cast(null as varchar) format json");
f.checkScalar("'null' format json", "null", "ANY NOT NULL");
f.enableTypeCoercion(false)
.checkFails("^null^ format json", "(?s).*Illegal use of .NULL.*",
false);
}
@Test void testJsonArray() {
final SqlOperatorFixture f = fixture();
f.checkString("json_array()", "[]", "VARCHAR(2000) NOT NULL");
f.checkString("json_array('foo')",
"[\"foo\"]", "VARCHAR(2000) NOT NULL");
f.checkString("json_array('foo', 'bar')",
"[\"foo\",\"bar\"]", "VARCHAR(2000) NOT NULL");
f.checkString("json_array(null)",
"[]", "VARCHAR(2000) NOT NULL");
f.checkString("json_array(null null on null)",
"[null]", "VARCHAR(2000) NOT NULL");
f.checkString("json_array(null absent on null)",
"[]", "VARCHAR(2000) NOT NULL");
f.checkString("json_array(100)",
"[100]", "VARCHAR(2000) NOT NULL");
f.checkString("json_array(json_array('foo'))",
"[\"[\\\"foo\\\"]\"]", "VARCHAR(2000) NOT NULL");
f.checkString("json_array(json_array('foo') format json)",
"[[\"foo\"]]", "VARCHAR(2000) NOT NULL");
}
@Test void testJsonArrayAgg() {
final SqlOperatorFixture f = fixture();
f.checkAggType("json_arrayagg('foo')", "VARCHAR(2000) NOT NULL");
f.checkAggType("json_arrayagg(null)", "VARCHAR(2000) NOT NULL");
final String[] values = {
"'foo'",
"cast(null as varchar(2000))",
"'foo3'"
};
f.checkAgg("json_arrayagg(x)", values, isSingle("[\"foo\",\"foo3\"]"));
f.checkAgg("json_arrayagg(x null on null)", values,
isSingle("[\"foo\",null,\"foo3\"]"));
f.checkAgg("json_arrayagg(x absent on null)", values,
isSingle("[\"foo\",\"foo3\"]"));
}
@Test void testJsonPredicate() {
final SqlOperatorFixture f = fixture();
f.checkBoolean("'{}' is json value", true);
f.checkBoolean("'{]' is json value", false);
f.checkBoolean("'{}' is json object", true);
f.checkBoolean("'[]' is json object", false);
f.checkBoolean("'{}' is json array", false);
f.checkBoolean("'[]' is json array", true);
f.checkBoolean("'100' is json scalar", true);
f.checkBoolean("'[]' is json scalar", false);
f.checkBoolean("'{}' is not json value", false);
f.checkBoolean("'{]' is not json value", true);
f.checkBoolean("'{}' is not json object", false);
f.checkBoolean("'[]' is not json object", true);
f.checkBoolean("'{}' is not json array", true);
f.checkBoolean("'[]' is not json array", false);
f.checkBoolean("'100' is not json scalar", false);
f.checkBoolean("'[]' is not json scalar", true);
}
@Test void testCompress() {
SqlOperatorFixture f = fixture().withLibrary(SqlLibrary.MYSQL);
f.checkNull("COMPRESS(NULL)");
f.checkString("COMPRESS('')", "",
"VARBINARY NOT NULL");
f.checkString("COMPRESS(REPEAT('a',1000))",
"e8030000789c4b4c1c05a360140c770000f9d87af8", "VARBINARY NOT NULL");
f.checkString("COMPRESS(REPEAT('a',16))",
"10000000789c4b4c44050033980611", "VARBINARY NOT NULL");
f.checkString("COMPRESS('sample')",
"06000000789c2b4ecc2dc849050008de0283", "VARBINARY NOT NULL");
f.checkString("COMPRESS('example')",
"07000000789c4bad48cc2dc84905000bc002ed", "VARBINARY NOT NULL");
}
@Test void testExtractValue() {
SqlOperatorFixture f = fixture().withLibrary(SqlLibrary.MYSQL);
f.checkNull("ExtractValue(NULL, '//b')");
f.checkNull("ExtractValue('', NULL)");
f.checkFails("ExtractValue('', '#/a/b')",
"Invalid input for EXTRACTVALUE: xml: '.*", true);
f.checkFails("ExtractValue('', '/b')",
"Invalid input for EXTRACTVALUE: xml: '.*", true);
f.checkString("ExtractValue('c', '//a')",
"c", "VARCHAR(2000)");
f.checkString("ExtractValue('cccddd', '/a')",
"ccc", "VARCHAR(2000)");
f.checkString("ExtractValue('cccddd', '/a/b')",
"ddd", "VARCHAR(2000)");
f.checkString("ExtractValue('cccddd', '/b')",
"", "VARCHAR(2000)");
f.checkString("ExtractValue('cccdddeee', '//b')",
"ddd eee", "VARCHAR(2000)");
f.checkString("ExtractValue('', 'count(/a/b)')",
"1", "VARCHAR(2000)");
}
@Test void testXmlTransform() {
SqlOperatorFixture f = fixture().withLibrary(SqlLibrary.ORACLE);
f.checkNull("XMLTRANSFORM('', NULL)");
f.checkNull("XMLTRANSFORM(NULL,'')");
f.checkFails("XMLTRANSFORM('', '<')",
"Illegal xslt specified : '.*", true);
final String sql = "XMLTRANSFORM('<', '\n"
+ ""
+ " ')";
f.checkFails(sql,
"Invalid input for XMLTRANSFORM xml: '.*", true);
final String sql2 = "XMLTRANSFORM("
+ "'\n"
+ "\n"
+ " My Article \n"
+ " \n"
+ " Mr. Foo \n"
+ " Mr. Bar \n"
+ " \n"
+ " This is my article text.\n"
+ " '"
+ ","
+ "'\n"
+ ""
+ " "
+ " "
+ " Article - "
+ " Authors: "
+ " "
+ " "
+ " - "
+ " "
+ " ')";
f.checkString(sql2,
" Article - My Article Authors: - Mr. Foo - Mr. Bar",
"VARCHAR(2000)");
}
@Test void testExtractXml() {
SqlOperatorFixture f = fixture().withLibrary(SqlLibrary.ORACLE);
f.checkFails("\"EXTRACT\"('', '<','a')",
"Invalid input for EXTRACT xpath: '.*", true);
f.checkFails("\"EXTRACT\"('', '<')",
"Invalid input for EXTRACT xpath: '.*", true);
f.checkNull("\"EXTRACT\"('', NULL)");
f.checkNull("\"EXTRACT\"(NULL,'')");
f.checkString("\"EXTRACT\"("
+ "'"
+ "Article1 "
+ ""
+ "Foo "
+ "Bar "
+ " "
+ "article text."
+ " ', '/Article/Title')",
"Article1 ",
"VARCHAR(2000)");
f.checkString("\"EXTRACT\"('"
+ ""
+ "Article1 "
+ "Article2 "
+ "Foo Bar "
+ "article text."
+ " ', '/Article/Title')",
"Article1 Article2 ",
"VARCHAR(2000)");
f.checkString("\"EXTRACT\"(\n"
+ "'"
+ "Title "
+ "Author Name "
+ "5.50 "
+ " "
+ " ', "
+ "'/books:books/books:book', "
+ "'books=\"http://www.contoso.com/books\"')",
"Title Author "
+ "Name 5.50 ",
"VARCHAR(2000)");
}
@Test void testExistsNode() {
SqlOperatorFixture f = fixture().withLibrary(SqlLibrary.ORACLE);
f.checkFails("EXISTSNODE('', '<','a')",
"Invalid input for EXISTSNODE xpath: '.*", true);
f.checkFails("EXISTSNODE('', '<')",
"Invalid input for EXISTSNODE xpath: '.*", true);
f.checkNull("EXISTSNODE('', NULL)");
f.checkNull("EXISTSNODE(NULL,'')");
f.checkString("EXISTSNODE('"
+ "Article1 "
+ "Foo Bar "
+ "article text."
+ " ', '/Article/Title')",
"1",
"INTEGER");
f.checkString("EXISTSNODE('"
+ "Article1 "
+ "Foo Bar "
+ "article text. ', '/Article/Title/Books')",
"0",
"INTEGER");
f.checkString("EXISTSNODE('"
+ "Article1 "
+ "Article2 "
+ "Foo Bar "
+ "article text. ', '/Article/Title')",
"1",
"INTEGER");
f.checkString("EXISTSNODE(\n"
+ "'"
+ ""
+ "Title "
+ "Author Name "
+ "5.50 "
+ " "
+ " ', "
+ "'/books:books/books:book', "
+ "'books=\"http://www.contoso.com/books\"')",
"1",
"INTEGER");
f.checkString("EXISTSNODE(\n"
+ "'"
+ "Title "
+ "Author Name "
+ "5.50 ', "
+ "'/books:books/books:book/books:title2', "
+ "'books=\"http://www.contoso.com/books\"'"
+ ")",
"0",
"INTEGER");
}
@Test void testLowerFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.LOWER, VmName.EXPAND);
// SQL:2003 6.29.8 The type of lower is the type of its argument
f.checkString("lower('A')", "a", "CHAR(1) NOT NULL");
f.checkString("lower('a')", "a", "CHAR(1) NOT NULL");
f.checkString("lower('1')", "1", "CHAR(1) NOT NULL");
f.checkString("lower('AA')", "aa", "CHAR(2) NOT NULL");
f.checkNull("lower(cast(null as varchar(1)))");
}
@Test void testInitcapFunc() {
// Note: the initcap function is an Oracle defined function and is not
// defined in the SQL:2003 standard
// todo: implement in fennel
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.INITCAP, VM_FENNEL);
f.checkString("initcap('aA')", "Aa", "CHAR(2) NOT NULL");
f.checkString("initcap('Aa')", "Aa", "CHAR(2) NOT NULL");
f.checkString("initcap('1a')", "1a", "CHAR(2) NOT NULL");
f.checkString("initcap('ab cd Ef 12')",
"Ab Cd Ef 12",
"CHAR(11) NOT NULL");
f.checkNull("initcap(cast(null as varchar(1)))");
// dtbug 232
f.enableTypeCoercion(false)
.checkFails("^initcap(cast(null as date))^",
"Cannot apply 'INITCAP' to arguments of type "
+ "'INITCAP\\(\\)'\\. Supported form\\(s\\): "
+ "'INITCAP\\(\\)'",
false);
f.checkType("initcap(cast(null as date))", "VARCHAR");
}
@Test void testPowerFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.POWER, VmName.EXPAND);
f.checkScalarApprox("power(2,-2)", "DOUBLE NOT NULL", isExactly("0.25"));
f.checkNull("power(cast(null as integer),2)");
f.checkNull("power(2,cast(null as double))");
// 'pow' is an obsolete form of the 'power' function
f.checkFails("^pow(2,-2)^",
"No match found for function signature POW\\(, \\)",
false);
}
@Test void testSqrtFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.SQRT, VmName.EXPAND);
f.checkType("sqrt(2)", "DOUBLE NOT NULL");
f.checkType("sqrt(cast(2 as float))", "DOUBLE NOT NULL");
f.checkType("sqrt(case when false then 2 else null end)", "DOUBLE");
f.enableTypeCoercion(false)
.checkFails("^sqrt('abc')^",
"Cannot apply 'SQRT' to arguments of type "
+ "'SQRT\\(\\)'\\. Supported form\\(s\\): "
+ "'SQRT\\(\\)'",
false);
f.checkType("sqrt('abc')", "DOUBLE NOT NULL");
f.checkScalarApprox("sqrt(2)", "DOUBLE NOT NULL",
isWithin(1.4142d, 0.0001d));
f.checkScalarApprox("sqrt(cast(2 as decimal(2, 0)))", "DOUBLE NOT NULL",
isWithin(1.4142d, 0.0001d));
f.checkNull("sqrt(cast(null as integer))");
f.checkNull("sqrt(cast(null as double))");
}
@Test void testExpFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.EXP, VM_FENNEL);
f.checkScalarApprox("exp(2)", "DOUBLE NOT NULL",
isWithin(7.389056, 0.000001));
f.checkScalarApprox("exp(-2)", "DOUBLE NOT NULL",
isWithin(0.1353, 0.0001));
f.checkNull("exp(cast(null as integer))");
f.checkNull("exp(cast(null as double))");
}
@Test void testModFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.MOD, VmName.EXPAND);
f.checkScalarExact("mod(4,2)", 0);
f.checkScalarExact("mod(8,5)", 3);
f.checkScalarExact("mod(-12,7)", -5);
f.checkScalarExact("mod(-12,-7)", -5);
f.checkScalarExact("mod(12,-7)", 5);
f.checkScalarExact("mod(cast(12 as tinyint), cast(-7 as tinyint))",
"TINYINT NOT NULL", "5");
if (!DECIMAL) {
return;
}
f.checkScalarExact("mod(cast(9 as decimal(2, 0)), 7)",
"INTEGER NOT NULL", "2");
f.checkScalarExact("mod(7, cast(9 as decimal(2, 0)))",
"DECIMAL(2, 0) NOT NULL", "7");
f.checkScalarExact("mod(cast(-9 as decimal(2, 0)), "
+ "cast(7 as decimal(1, 0)))",
"DECIMAL(1, 0) NOT NULL", "-2");
}
@Test void testModFuncNull() {
final SqlOperatorFixture f = fixture();
f.checkNull("mod(cast(null as integer),2)");
f.checkNull("mod(4,cast(null as tinyint))");
if (!DECIMAL) {
return;
}
f.checkNull("mod(4,cast(null as decimal(12,0)))");
}
@Test void testModFuncDivByZero() {
// The extra CASE expression is to fool Janino. It does constant
// reduction and will throw the divide by zero exception while
// compiling the expression. The test frame work would then issue
// unexpected exception occurred during "validation". You cannot
// submit as non-runtime because the janino exception does not have
// error position information and the framework is unhappy with that.
final SqlOperatorFixture f = fixture();
f.checkFails("mod(3,case 'a' when 'a' then 0 end)",
DIVISION_BY_ZERO_MESSAGE, true);
}
@Test void testLnFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.LN, VmName.EXPAND);
f.checkScalarApprox("ln(2.71828)", "DOUBLE NOT NULL",
isWithin(1.0, 0.000001));
f.checkScalarApprox("ln(2.71828)", "DOUBLE NOT NULL",
isWithin(0.999999327, 0.0000001));
f.checkNull("ln(cast(null as tinyint))");
}
@Test void testLogFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.LOG10, VmName.EXPAND);
f.checkScalarApprox("log10(10)", "DOUBLE NOT NULL",
isWithin(1.0, 0.000001));
f.checkScalarApprox("log10(100.0)", "DOUBLE NOT NULL",
isWithin(2.0, 0.000001));
f.checkScalarApprox("log10(cast(10e8 as double))", "DOUBLE NOT NULL",
isWithin(9.0, 0.000001));
f.checkScalarApprox("log10(cast(10e2 as float))", "DOUBLE NOT NULL",
isWithin(3.0, 0.000001));
f.checkScalarApprox("log10(cast(10e-3 as real))", "DOUBLE NOT NULL",
isWithin(-2.0, 0.000001));
f.checkNull("log10(cast(null as real))");
}
@Test void testRandFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.RAND, VmName.EXPAND);
f.checkFails("^rand^", "Column 'RAND' not found in any table", false);
for (int i = 0; i < 100; i++) {
// Result must always be between 0 and 1, inclusive.
f.checkScalarApprox("rand()", "DOUBLE NOT NULL", isWithin(0.5, 0.5));
}
}
@Test void testRandSeedFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.RAND, VmName.EXPAND);
f.checkScalarApprox("rand(1)", "DOUBLE NOT NULL", isWithin(0.6016, 0.0001));
f.checkScalarApprox("rand(2)", "DOUBLE NOT NULL", isWithin(0.4728, 0.0001));
}
@Test void testRandIntegerFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.RAND_INTEGER, VmName.EXPAND);
for (int i = 0; i < 100; i++) {
// Result must always be between 0 and 10, inclusive.
f.checkScalarApprox("rand_integer(11)", "INTEGER NOT NULL",
isWithin(5.0, 5.0));
}
}
@Test void testRandIntegerSeedFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.RAND_INTEGER, VmName.EXPAND);
f.checkScalar("rand_integer(1, 11)", 4, "INTEGER NOT NULL");
f.checkScalar("rand_integer(2, 11)", 1, "INTEGER NOT NULL");
}
/** Tests {@code ARRAY_CONCAT} function from BigQuery. */
@Test void testArrayConcat() {
SqlOperatorFixture f = fixture()
.setFor(SqlLibraryOperators.ARRAY_CONCAT)
.withLibrary(SqlLibrary.BIG_QUERY);
f.checkFails("^array_concat()^", INVALID_ARGUMENTS_NUMBER, false);
f.checkScalar("array_concat(array[1, 2], array[2, 3])", "[1, 2, 2, 3]",
"INTEGER NOT NULL ARRAY NOT NULL");
f.checkScalar("array_concat(array[1, 2], array[2, null])", "[1, 2, 2, null]",
"INTEGER ARRAY NOT NULL");
f.checkScalar("array_concat(array['hello', 'world'], array['!'], "
+ "array[cast(null as char)])",
"[hello, world, !, null]", "CHAR(5) ARRAY NOT NULL");
f.checkNull("array_concat(cast(null as integer array), array[1])");
}
/** Tests {@code ARRAY_REVERSE} function from BigQuery. */
@Test void testArrayReverseFunc() {
SqlOperatorFixture f = fixture()
.setFor(SqlLibraryOperators.ARRAY_REVERSE)
.withLibrary(SqlLibrary.BIG_QUERY);
f.checkScalar("array_reverse(array[1])", "[1]",
"INTEGER NOT NULL ARRAY NOT NULL");
f.checkScalar("array_reverse(array[1, 2])", "[2, 1]",
"INTEGER NOT NULL ARRAY NOT NULL");
f.checkScalar("array_reverse(array[null, 1])", "[1, null]",
"INTEGER ARRAY NOT NULL");
}
/** Tests {@code ARRAY_LENGTH} function from BigQuery. */
@Test void testArrayLengthFunc() {
SqlOperatorFixture f = fixture()
.setFor(SqlLibraryOperators.ARRAY_LENGTH)
.withLibrary(SqlLibrary.BIG_QUERY);
f.checkScalar("array_length(array[1])", "1",
"INTEGER NOT NULL");
f.checkScalar("array_length(array[1, 2, null])", "3",
"INTEGER NOT NULL");
f.checkNull("array_length(null)");
}
/** Tests {@code UNIX_SECONDS} and other datetime functions from BigQuery. */
@Test void testUnixSecondsFunc() {
SqlOperatorFixture f = fixture()
.setFor(SqlLibraryOperators.UNIX_SECONDS)
.withLibrary(SqlLibrary.BIG_QUERY);
f.checkScalar("unix_seconds(timestamp '1970-01-01 00:00:00')", 0,
"BIGINT NOT NULL");
f.checkNull("unix_seconds(cast(null as timestamp))");
f.checkNull("unix_millis(cast(null as timestamp))");
f.checkNull("unix_micros(cast(null as timestamp))");
f.checkScalar("timestamp_seconds(0)", "1970-01-01 00:00:00",
"TIMESTAMP(0) NOT NULL");
f.checkNull("timestamp_seconds(cast(null as bigint))");
f.checkNull("timestamp_millis(cast(null as bigint))");
f.checkNull("timestamp_micros(cast(null as bigint))");
f.checkScalar("date_from_unix_date(0)", "1970-01-01", "DATE NOT NULL");
// Have to quote the "DATE" function because we're not using the Babel
// parser. In the regular parser, DATE is a reserved keyword.
f.checkNull("\"DATE\"(null)");
f.checkScalar("\"DATE\"('1985-12-06')", "1985-12-06", "DATE NOT NULL");
f.checkType("CURRENT_DATETIME()", "TIMESTAMP(0) NOT NULL");
f.checkType("CURRENT_DATETIME('America/Los_Angeles')", "TIMESTAMP(0) NOT NULL");
f.checkType("CURRENT_DATETIME(CAST(NULL AS VARCHAR(20)))", "TIMESTAMP(0)");
}
@Test void testAbsFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.ABS, VmName.EXPAND);
f.checkScalarExact("abs(-1)", 1);
f.checkScalarExact("abs(cast(10 as TINYINT))", "TINYINT NOT NULL", "10");
f.checkScalarExact("abs(cast(-20 as SMALLINT))", "SMALLINT NOT NULL", "20");
f.checkScalarExact("abs(cast(-100 as INT))", "INTEGER NOT NULL", "100");
f.checkScalarExact("abs(cast(1000 as BIGINT))", "BIGINT NOT NULL", "1000");
f.checkScalarExact("abs(54.4)", "DECIMAL(3, 1) NOT NULL", "54.4");
f.checkScalarExact("abs(-54.4)", "DECIMAL(3, 1) NOT NULL", "54.4");
f.checkScalarApprox("abs(-9.32E-2)", "DOUBLE NOT NULL",
isExactly("0.0932"));
f.checkScalarApprox("abs(cast(-3.5 as double))", "DOUBLE NOT NULL",
isExactly("3.5"));
f.checkScalarApprox("abs(cast(-3.5 as float))", "FLOAT NOT NULL",
isExactly("3.5"));
f.checkScalarApprox("abs(cast(3.5 as real))", "REAL NOT NULL",
isExactly("3.5"));
f.checkNull("abs(cast(null as double))");
}
@Test void testAbsFuncIntervals() {
final SqlOperatorFixture f = fixture();
f.checkScalar("abs(interval '-2' day)", "+2", "INTERVAL DAY NOT NULL");
f.checkScalar("abs(interval '-5-03' year to month)",
"+5-03", "INTERVAL YEAR TO MONTH NOT NULL");
f.checkNull("abs(cast(null as interval hour))");
}
@Test void testAcosFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.ACOS, VmName.EXPAND);
f.checkType("acos(0)", "DOUBLE NOT NULL");
f.checkType("acos(cast(1 as float))", "DOUBLE NOT NULL");
f.checkType("acos(case when false then 0.5 else null end)", "DOUBLE");
f.enableTypeCoercion(false)
.checkFails("^acos('abc')^",
"Cannot apply 'ACOS' to arguments of type "
+ "'ACOS\\(\\)'\\. Supported form\\(s\\): "
+ "'ACOS\\(\\)'",
false);
f.checkType("acos('abc')", "DOUBLE NOT NULL");
f.checkScalarApprox("acos(0.5)", "DOUBLE NOT NULL",
isWithin(1.0472d, 0.0001d));
f.checkScalarApprox("acos(cast(0.5 as decimal(1, 1)))", "DOUBLE NOT NULL",
isWithin(1.0472d, 0.0001d));
f.checkNull("acos(cast(null as integer))");
f.checkNull("acos(cast(null as double))");
}
@Test void testAsinFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.ASIN, VmName.EXPAND);
f.checkType("asin(0)", "DOUBLE NOT NULL");
f.checkType("asin(cast(1 as float))", "DOUBLE NOT NULL");
f.checkType("asin(case when false then 0.5 else null end)", "DOUBLE");
f.enableTypeCoercion(false)
.checkFails("^asin('abc')^",
"Cannot apply 'ASIN' to arguments of type "
+ "'ASIN\\(\\)'\\. Supported form\\(s\\): "
+ "'ASIN\\(\\)'",
false);
f.checkType("asin('abc')", "DOUBLE NOT NULL");
f.checkScalarApprox("asin(0.5)", "DOUBLE NOT NULL",
isWithin(0.5236d, 0.0001d));
f.checkScalarApprox("asin(cast(0.5 as decimal(1, 1)))", "DOUBLE NOT NULL",
isWithin(0.5236d, 0.0001d));
f.checkNull("asin(cast(null as integer))");
f.checkNull("asin(cast(null as double))");
}
@Test void testAtanFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.ATAN, VmName.EXPAND);
f.checkType("atan(2)", "DOUBLE NOT NULL");
f.checkType("atan(cast(2 as float))", "DOUBLE NOT NULL");
f.checkType("atan(case when false then 2 else null end)", "DOUBLE");
f.enableTypeCoercion(false)
.checkFails("^atan('abc')^",
"Cannot apply 'ATAN' to arguments of type "
+ "'ATAN\\(\\)'\\. Supported form\\(s\\): "
+ "'ATAN\\(\\)'",
false);
f.checkType("atan('abc')", "DOUBLE NOT NULL");
f.checkScalarApprox("atan(2)", "DOUBLE NOT NULL",
isWithin(1.1071d, 0.0001d));
f.checkScalarApprox("atan(cast(2 as decimal(1, 0)))", "DOUBLE NOT NULL",
isWithin(1.1071d, 0.0001d));
f.checkNull("atan(cast(null as integer))");
f.checkNull("atan(cast(null as double))");
}
@Test void testAtan2Func() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.ATAN2, VmName.EXPAND);
f.checkType("atan2(2, -2)", "DOUBLE NOT NULL");
f.checkScalarApprox("atan2(cast(1 as float), -1)", "DOUBLE NOT NULL",
isWithin(2.3562d, 0.0001d));
f.checkType("atan2(case when false then 0.5 else null end, -1)",
"DOUBLE");
f.enableTypeCoercion(false)
.checkFails("^atan2('abc', 'def')^",
"Cannot apply 'ATAN2' to arguments of type "
+ "'ATAN2\\(, \\)'\\. "
+ "Supported form\\(s\\): 'ATAN2\\(, \\)'",
false);
f.checkType("atan2('abc', 'def')", "DOUBLE NOT NULL");
f.checkScalarApprox("atan2(0.5, -0.5)", "DOUBLE NOT NULL",
isWithin(2.3562d, 0.0001d));
f.checkScalarApprox("atan2(cast(0.5 as decimal(1, 1)),"
+ " cast(-0.5 as decimal(1, 1)))", "DOUBLE NOT NULL",
isWithin(2.3562d, 0.0001d));
f.checkNull("atan2(cast(null as integer), -1)");
f.checkNull("atan2(1, cast(null as double))");
}
@Test void testCbrtFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.CBRT, VmName.EXPAND);
f.checkType("cbrt(1)", "DOUBLE NOT NULL");
f.checkType("cbrt(cast(1 as float))", "DOUBLE NOT NULL");
f.checkType("cbrt(case when false then 1 else null end)", "DOUBLE");
f.enableTypeCoercion(false)
.checkFails("^cbrt('abc')^",
"Cannot apply 'CBRT' to arguments of type "
+ "'CBRT\\(\\)'\\. Supported form\\(s\\): "
+ "'CBRT\\(\\)'",
false);
f.checkType("cbrt('abc')", "DOUBLE NOT NULL");
f.checkScalar("cbrt(8)", "2.0", "DOUBLE NOT NULL");
f.checkScalar("cbrt(-8)", "-2.0", "DOUBLE NOT NULL");
f.checkScalar("cbrt(cast(1 as decimal(1, 0)))", "1.0",
"DOUBLE NOT NULL");
f.checkNull("cbrt(cast(null as integer))");
f.checkNull("cbrt(cast(null as double))");
}
@Test void testCosFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.COS, VmName.EXPAND);
f.checkType("cos(1)", "DOUBLE NOT NULL");
f.checkType("cos(cast(1 as float))", "DOUBLE NOT NULL");
f.checkType("cos(case when false then 1 else null end)", "DOUBLE");
f.enableTypeCoercion(false)
.checkFails("^cos('abc')^",
"Cannot apply 'COS' to arguments of type "
+ "'COS\\(\\)'\\. Supported form\\(s\\): "
+ "'COS\\(\\)'",
false);
f.checkType("cos('abc')", "DOUBLE NOT NULL");
f.checkScalarApprox("cos(1)", "DOUBLE NOT NULL",
isWithin(0.5403d, 0.0001d));
f.checkScalarApprox("cos(cast(1 as decimal(1, 0)))", "DOUBLE NOT NULL",
isWithin(0.5403d, 0.0001d));
f.checkNull("cos(cast(null as integer))");
f.checkNull("cos(cast(null as double))");
}
@Test void testCoshFunc() {
final SqlOperatorFixture f0 = fixture();
final SqlOperatorFixture f = f0.withLibrary(SqlLibrary.ORACLE);
f.checkType("cosh(1)", "DOUBLE NOT NULL");
f.checkType("cosh(cast(1 as float))", "DOUBLE NOT NULL");
f.checkType("cosh(case when false then 1 else null end)", "DOUBLE");
f0.enableTypeCoercion(false)
.checkFails("^cosh('abc')^",
"No match found for function signature COSH\\(\\)",
false);
f.checkType("cosh('abc')", "DOUBLE NOT NULL");
f.checkScalarApprox("cosh(1)", "DOUBLE NOT NULL",
isWithin(1.5430d, 0.0001d));
f.checkScalarApprox("cosh(cast(1 as decimal(1, 0)))", "DOUBLE NOT NULL",
isWithin(1.5430d, 0.0001d));
f.checkNull("cosh(cast(null as integer))");
f.checkNull("cosh(cast(null as double))");
}
@Test void testCotFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.COT, VmName.EXPAND);
f.checkType("cot(1)", "DOUBLE NOT NULL");
f.checkType("cot(cast(1 as float))", "DOUBLE NOT NULL");
f.checkType("cot(case when false then 1 else null end)", "DOUBLE");
f.enableTypeCoercion(false).checkFails("^cot('abc')^",
"Cannot apply 'COT' to arguments of type "
+ "'COT\\(\\)'\\. Supported form\\(s\\): "
+ "'COT\\(\\)'",
false);
f.checkType("cot('abc')", "DOUBLE NOT NULL");
f.checkScalarApprox("cot(1)", "DOUBLE NOT NULL",
isWithin(0.6421d, 0.0001d));
f.checkScalarApprox("cot(cast(1 as decimal(1, 0)))", "DOUBLE NOT NULL",
isWithin(0.6421d, 0.0001d));
f.checkNull("cot(cast(null as integer))");
f.checkNull("cot(cast(null as double))");
}
@Test void testDegreesFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.DEGREES, VmName.EXPAND);
f.checkType("degrees(1)", "DOUBLE NOT NULL");
f.checkType("degrees(cast(1 as float))", "DOUBLE NOT NULL");
f.checkType("degrees(case when false then 1 else null end)", "DOUBLE");
f.enableTypeCoercion(false)
.checkFails("^degrees('abc')^",
"Cannot apply 'DEGREES' to arguments of type "
+ "'DEGREES\\(\\)'\\. Supported form\\(s\\): "
+ "'DEGREES\\(\\)'",
false);
f.checkType("degrees('abc')", "DOUBLE NOT NULL");
f.checkScalarApprox("degrees(1)", "DOUBLE NOT NULL",
isWithin(57.2958d, 0.0001d));
f.checkScalarApprox("degrees(cast(1 as decimal(1, 0)))", "DOUBLE NOT NULL",
isWithin(57.2958d, 0.0001d));
f.checkNull("degrees(cast(null as integer))");
f.checkNull("degrees(cast(null as double))");
}
@Test void testPiFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.PI, VmName.EXPAND);
f.checkScalarApprox("PI", "DOUBLE NOT NULL", isWithin(3.1415d, 0.0001d));
f.checkFails("^PI()^",
"No match found for function signature PI\\(\\)", false);
// assert that PI function is not dynamic [CALCITE-2750]
assertThat("PI operator should not be identified as dynamic function",
PI.isDynamicFunction(), is(false));
}
@Test void testRadiansFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.RADIANS, VmName.EXPAND);
f.checkType("radians(42)", "DOUBLE NOT NULL");
f.checkType("radians(cast(42 as float))", "DOUBLE NOT NULL");
f.checkType("radians(case when false then 42 else null end)", "DOUBLE");
f.enableTypeCoercion(false)
.checkFails("^radians('abc')^",
"Cannot apply 'RADIANS' to arguments of type "
+ "'RADIANS\\(\\)'\\. Supported form\\(s\\): "
+ "'RADIANS\\(\\)'",
false);
f.checkType("radians('abc')", "DOUBLE NOT NULL");
f.checkScalarApprox("radians(42)", "DOUBLE NOT NULL",
isWithin(0.7330d, 0.0001d));
f.checkScalarApprox("radians(cast(42 as decimal(2, 0)))", "DOUBLE NOT NULL",
isWithin(0.7330d, 0.0001d));
f.checkNull("radians(cast(null as integer))");
f.checkNull("radians(cast(null as double))");
}
@Test void testRoundFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.ROUND, VmName.EXPAND);
f.checkType("round(42, -1)", "INTEGER NOT NULL");
f.checkType("round(cast(42 as float), 1)", "FLOAT NOT NULL");
f.checkType("round(case when false then 42 else null end, -1)",
"INTEGER");
f.enableTypeCoercion(false)
.checkFails("^round('abc', 'def')^",
"Cannot apply 'ROUND' to arguments of type "
+ "'ROUND\\(, \\)'\\. Supported "
+ "form\\(s\\): 'ROUND\\(, \\)'",
false);
f.checkType("round('abc', 'def')", "DECIMAL(19, 9) NOT NULL");
f.checkScalar("round(42, -1)", 40, "INTEGER NOT NULL");
f.checkScalar("round(cast(42.346 as decimal(2, 3)), 2)",
BigDecimal.valueOf(4235, 2), "DECIMAL(2, 3) NOT NULL");
f.checkScalar("round(cast(-42.346 as decimal(2, 3)), 2)",
BigDecimal.valueOf(-4235, 2), "DECIMAL(2, 3) NOT NULL");
f.checkNull("round(cast(null as integer), 1)");
f.checkNull("round(cast(null as double), 1)");
f.checkNull("round(43.21, cast(null as integer))");
f.checkNull("round(cast(null as double))");
f.checkScalar("round(42)", 42, "INTEGER NOT NULL");
f.checkScalar("round(cast(42.346 as decimal(2, 3)))",
BigDecimal.valueOf(42, 0), "DECIMAL(2, 3) NOT NULL");
f.checkScalar("round(42.324)",
BigDecimal.valueOf(42, 0), "DECIMAL(5, 3) NOT NULL");
f.checkScalar("round(42.724)",
BigDecimal.valueOf(43, 0), "DECIMAL(5, 3) NOT NULL");
}
@Test void testSignFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.SIGN, VmName.EXPAND);
f.checkType("sign(1)", "INTEGER NOT NULL");
f.checkType("sign(cast(1 as float))", "FLOAT NOT NULL");
f.checkType("sign(case when false then 1 else null end)", "INTEGER");
f.enableTypeCoercion(false)
.checkFails("^sign('abc')^",
"Cannot apply 'SIGN' to arguments of type "
+ "'SIGN\\(\\)'\\. Supported form\\(s\\): "
+ "'SIGN\\(\\)'",
false);
f.checkType("sign('abc')", "DECIMAL(19, 9) NOT NULL");
f.checkScalar("sign(1)", 1, "INTEGER NOT NULL");
f.checkScalar("sign(cast(-1 as decimal(1, 0)))",
BigDecimal.valueOf(-1), "DECIMAL(1, 0) NOT NULL");
f.checkScalar("sign(cast(0 as float))", 0d, "FLOAT NOT NULL");
f.checkNull("sign(cast(null as integer))");
f.checkNull("sign(cast(null as double))");
}
@Test void testSinFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.SIN, VmName.EXPAND);
f.checkType("sin(1)", "DOUBLE NOT NULL");
f.checkType("sin(cast(1 as float))", "DOUBLE NOT NULL");
f.checkType("sin(case when false then 1 else null end)", "DOUBLE");
f.enableTypeCoercion(false)
.checkFails("^sin('abc')^",
"Cannot apply 'SIN' to arguments of type "
+ "'SIN\\(\\)'\\. Supported form\\(s\\): "
+ "'SIN\\(\\)'",
false);
f.checkType("sin('abc')", "DOUBLE NOT NULL");
f.checkScalarApprox("sin(1)", "DOUBLE NOT NULL",
isWithin(0.8415d, 0.0001d));
f.checkScalarApprox("sin(cast(1 as decimal(1, 0)))", "DOUBLE NOT NULL",
isWithin(0.8415d, 0.0001d));
f.checkNull("sin(cast(null as integer))");
f.checkNull("sin(cast(null as double))");
}
@Test void testSinhFunc() {
final SqlOperatorFixture f0 = fixture();
final SqlOperatorFixture f = f0.withLibrary(SqlLibrary.ORACLE);
f.checkType("sinh(1)", "DOUBLE NOT NULL");
f.checkType("sinh(cast(1 as float))", "DOUBLE NOT NULL");
f.checkType("sinh(case when false then 1 else null end)", "DOUBLE");
f0.enableTypeCoercion(false)
.checkFails("^sinh('abc')^",
"No match found for function signature SINH\\(\\)",
false);
f.checkType("sinh('abc')", "DOUBLE NOT NULL");
f.checkScalarApprox("sinh(1)", "DOUBLE NOT NULL",
isWithin(1.1752d, 0.0001d));
f.checkScalarApprox("sinh(cast(1 as decimal(1, 0)))", "DOUBLE NOT NULL",
isWithin(1.1752d, 0.0001d));
f.checkNull("sinh(cast(null as integer))");
f.checkNull("sinh(cast(null as double))");
}
@Test void testTanFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.TAN, VmName.EXPAND);
f.checkType("tan(1)", "DOUBLE NOT NULL");
f.checkType("tan(cast(1 as float))", "DOUBLE NOT NULL");
f.checkType("tan(case when false then 1 else null end)", "DOUBLE");
f.enableTypeCoercion(false)
.checkFails("^tan('abc')^",
"Cannot apply 'TAN' to arguments of type "
+ "'TAN\\(\\)'\\. Supported form\\(s\\): "
+ "'TAN\\(\\)'",
false);
f.checkType("tan('abc')", "DOUBLE NOT NULL");
f.checkScalarApprox("tan(1)", "DOUBLE NOT NULL",
isWithin(1.5574d, 0.0001d));
f.checkScalarApprox("tan(cast(1 as decimal(1, 0)))", "DOUBLE NOT NULL",
isWithin(1.5574d, 0.0001d));
f.checkNull("tan(cast(null as integer))");
f.checkNull("tan(cast(null as double))");
}
@Test void testTanhFunc() {
SqlOperatorFixture f0 = fixture();
final SqlOperatorFixture f = f0.withLibrary(SqlLibrary.ORACLE);
f.checkType("tanh(1)", "DOUBLE NOT NULL");
f.checkType("tanh(cast(1 as float))", "DOUBLE NOT NULL");
f.checkType("tanh(case when false then 1 else null end)", "DOUBLE");
f0.enableTypeCoercion(false)
.checkFails("^tanh('abc')^",
"No match found for function signature TANH\\(\\)",
false);
f.checkType("tanh('abc')", "DOUBLE NOT NULL");
f.checkScalarApprox("tanh(1)", "DOUBLE NOT NULL",
isWithin(0.7615d, 0.0001d));
f.checkScalarApprox("tanh(cast(1 as decimal(1, 0)))", "DOUBLE NOT NULL",
isWithin(0.7615d, 0.0001d));
f.checkNull("tanh(cast(null as integer))");
f.checkNull("tanh(cast(null as double))");
}
@Test void testTruncateFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.TRUNCATE, VmName.EXPAND);
f.checkType("truncate(42, -1)", "INTEGER NOT NULL");
f.checkType("truncate(cast(42 as float), 1)", "FLOAT NOT NULL");
f.checkType("truncate(case when false then 42 else null end, -1)",
"INTEGER");
f.enableTypeCoercion(false)
.checkFails("^truncate('abc', 'def')^",
"Cannot apply 'TRUNCATE' to arguments of type "
+ "'TRUNCATE\\(, \\)'\\. Supported "
+ "form\\(s\\): 'TRUNCATE\\(, \\)'",
false);
f.checkType("truncate('abc', 'def')", "DECIMAL(19, 9) NOT NULL");
f.checkScalar("truncate(42, -1)", 40, "INTEGER NOT NULL");
f.checkScalar("truncate(cast(42.345 as decimal(2, 3)), 2)",
BigDecimal.valueOf(4234, 2), "DECIMAL(2, 3) NOT NULL");
f.checkScalar("truncate(cast(-42.345 as decimal(2, 3)), 2)",
BigDecimal.valueOf(-4234, 2), "DECIMAL(2, 3) NOT NULL");
f.checkNull("truncate(cast(null as integer), 1)");
f.checkNull("truncate(cast(null as double), 1)");
f.checkNull("truncate(43.21, cast(null as integer))");
f.checkScalar("truncate(42)", 42, "INTEGER NOT NULL");
f.checkScalar("truncate(42.324)",
BigDecimal.valueOf(42, 0), "DECIMAL(5, 3) NOT NULL");
f.checkScalar("truncate(cast(42.324 as float))", 42F,
"FLOAT NOT NULL");
f.checkScalar("truncate(cast(42.345 as decimal(2, 3)))",
BigDecimal.valueOf(42, 0), "DECIMAL(2, 3) NOT NULL");
f.checkNull("truncate(cast(null as integer))");
f.checkNull("truncate(cast(null as double))");
}
@Test void testNullifFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.NULLIF, VM_EXPAND);
f.checkNull("nullif(1,1)");
f.checkScalarExact("nullif(1.5, 13.56)", "DECIMAL(2, 1)", "1.5");
f.checkScalarExact("nullif(13.56, 1.5)", "DECIMAL(4, 2)", "13.56");
f.checkScalarExact("nullif(1.5, 3)", "DECIMAL(2, 1)", "1.5");
f.checkScalarExact("nullif(3, 1.5)", "INTEGER", "3");
f.checkScalarApprox("nullif(1.5e0, 3e0)", "DOUBLE", isExactly("1.5"));
f.checkScalarApprox("nullif(1.5, cast(3e0 as REAL))", "DECIMAL(2, 1)",
isExactly("1.5"));
f.checkScalarExact("nullif(3, 1.5e0)", "INTEGER", "3");
f.checkScalarExact("nullif(3, cast(1.5e0 as REAL))", "INTEGER", "3");
f.checkScalarApprox("nullif(1.5e0, 3.4)", "DOUBLE", isExactly("1.5"));
f.checkScalarExact("nullif(3.4, 1.5e0)", "DECIMAL(2, 1)", "3.4");
f.checkString("nullif('a','bc')", "a", "CHAR(1)");
f.checkString("nullif('a',cast(null as varchar(1)))", "a", "CHAR(1)");
f.checkNull("nullif(cast(null as varchar(1)),'a')");
f.checkNull("nullif(cast(null as numeric(4,3)), 4.3)");
// Error message reflects the fact that Nullif is expanded before it is
// validated (like a C macro). Not perfect, but good enough.
f.checkFails("1 + ^nullif(1, date '2005-8-4')^ + 2",
"(?s)Cannot apply '=' to arguments of type ' = '\\..*",
false);
f.checkFails("1 + ^nullif(1, 2, 3)^ + 2",
"Invalid number of arguments to function 'NULLIF'\\. "
+ "Was expecting 2 arguments",
false);
}
@Test void testNullIfOperatorIntervals() {
final SqlOperatorFixture f = fixture();
f.checkScalar("nullif(interval '2' month, interval '3' year)", "+2",
"INTERVAL MONTH");
f.checkScalar("nullif(interval '2 5' day to hour,"
+ " interval '5' second)",
"+2 05", "INTERVAL DAY TO HOUR");
f.checkNull("nullif(interval '3' day, interval '3' day)");
}
@Test void testCoalesceFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.COALESCE, VM_EXPAND);
f.checkString("coalesce('a','b')", "a", "CHAR(1) NOT NULL");
f.checkScalarExact("coalesce(null,null,3)", 3);
f.enableTypeCoercion(false)
.checkFails("1 + ^coalesce('a', 'b', 1, null)^ + 2",
"Illegal mixing of types in CASE or COALESCE statement",
false);
f.checkType("1 + coalesce('a', 'b', 1, null) + 2",
"INTEGER");
}
@Test void testUserFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.USER, VM_FENNEL);
f.checkString("USER", "sa", "VARCHAR(2000) NOT NULL");
}
@Test void testCurrentUserFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.CURRENT_USER, VM_FENNEL);
f.checkString("CURRENT_USER", "sa", "VARCHAR(2000) NOT NULL");
}
@Test void testSessionUserFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.SESSION_USER, VM_FENNEL);
f.checkString("SESSION_USER", "sa", "VARCHAR(2000) NOT NULL");
}
@Test void testSystemUserFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.SYSTEM_USER, VM_FENNEL);
String user = System.getProperty("user.name"); // e.g. "jhyde"
f.checkString("SYSTEM_USER", user, "VARCHAR(2000) NOT NULL");
}
@Test void testCurrentPathFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.CURRENT_PATH, VM_FENNEL);
f.checkString("CURRENT_PATH", "", "VARCHAR(2000) NOT NULL");
}
@Test void testCurrentRoleFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.CURRENT_ROLE, VM_FENNEL);
// By default, the CURRENT_ROLE function returns
// the empty string because a role has to be set explicitly.
f.checkString("CURRENT_ROLE", "", "VARCHAR(2000) NOT NULL");
}
@Test void testCurrentCatalogFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.CURRENT_CATALOG, VM_FENNEL);
// By default, the CURRENT_CATALOG function returns
// the empty string because a catalog has to be set explicitly.
f.checkString("CURRENT_CATALOG", "", "VARCHAR(2000) NOT NULL");
}
@Tag("slow")
@Test void testLocalTimeFuncWithCurrentTime() {
testLocalTimeFunc(currentTimeString(LOCAL_TZ));
}
@Test void testLocalTimeFuncWithFixedTime() {
testLocalTimeFunc(fixedTimeString(LOCAL_TZ));
}
private void testLocalTimeFunc(Pair pair) {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.LOCALTIME, VmName.EXPAND);
f.checkScalar("LOCALTIME", TIME_PATTERN, "TIME(0) NOT NULL");
f.checkFails("^LOCALTIME()^",
"No match found for function signature LOCALTIME\\(\\)",
false);
f.checkScalar("LOCALTIME(1)", TIME_PATTERN, "TIME(1) NOT NULL");
f.checkScalar("CAST(LOCALTIME AS VARCHAR(30))",
Pattern.compile(pair.left.substring(11) + "[0-9][0-9]:[0-9][0-9]"),
"VARCHAR(30) NOT NULL");
f.checkScalar("LOCALTIME",
Pattern.compile(pair.left.substring(11) + "[0-9][0-9]:[0-9][0-9]"),
"TIME(0) NOT NULL");
pair.right.close();
}
@Tag("slow")
@Test void testLocalTimestampFuncWithCurrentTime() {
testLocalTimestampFunc(currentTimeString(LOCAL_TZ));
}
@Test void testLocalTimestampFuncWithFixedTime() {
testLocalTimestampFunc(fixedTimeString(LOCAL_TZ));
}
private void testLocalTimestampFunc(Pair pair) {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.LOCALTIMESTAMP, VmName.EXPAND);
f.checkScalar("LOCALTIMESTAMP", TIMESTAMP_PATTERN,
"TIMESTAMP(0) NOT NULL");
f.checkFails("^LOCALTIMESTAMP()^",
"No match found for function signature LOCALTIMESTAMP\\(\\)",
false);
f.checkFails("^LOCALTIMESTAMP(4000000000)^",
LITERAL_OUT_OF_RANGE_MESSAGE, false);
f.checkFails("^LOCALTIMESTAMP(9223372036854775807)^",
LITERAL_OUT_OF_RANGE_MESSAGE, false);
f.checkScalar("LOCALTIMESTAMP(1)", TIMESTAMP_PATTERN,
"TIMESTAMP(1) NOT NULL");
// Check that timestamp is being generated in the right timezone by
// generating a specific timestamp.
f.checkScalar("CAST(LOCALTIMESTAMP AS VARCHAR(30))",
Pattern.compile(pair.left + "[0-9][0-9]:[0-9][0-9]"),
"VARCHAR(30) NOT NULL");
f.checkScalar("LOCALTIMESTAMP",
Pattern.compile(pair.left + "[0-9][0-9]:[0-9][0-9]"),
"TIMESTAMP(0) NOT NULL");
pair.right.close();
}
@Tag("slow")
@Test void testCurrentTimeFuncWithCurrentTime() {
testCurrentTimeFunc(currentTimeString(CURRENT_TZ));
}
@Test void testCurrentTimeFuncWithFixedTime() {
testCurrentTimeFunc(fixedTimeString(CURRENT_TZ));
}
private void testCurrentTimeFunc(Pair pair) {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.CURRENT_TIME, VmName.EXPAND);
f.checkScalar("CURRENT_TIME", TIME_PATTERN, "TIME(0) NOT NULL");
f.checkFails("^CURRENT_TIME()^",
"No match found for function signature CURRENT_TIME\\(\\)",
false);
f.checkScalar("CURRENT_TIME(1)", TIME_PATTERN, "TIME(1) NOT NULL");
f.checkScalar("CAST(CURRENT_TIME AS VARCHAR(30))",
Pattern.compile(pair.left.substring(11) + "[0-9][0-9]:[0-9][0-9]"),
"VARCHAR(30) NOT NULL");
f.checkScalar("CURRENT_TIME",
Pattern.compile(pair.left.substring(11) + "[0-9][0-9]:[0-9][0-9]"),
"TIME(0) NOT NULL");
pair.right.close();
}
@Tag("slow")
@Test void testCurrentTimestampFuncWithCurrentTime() {
testCurrentTimestampFunc(currentTimeString(CURRENT_TZ));
}
@Test void testCurrentTimestampFuncWithFixedTime() {
testCurrentTimestampFunc(fixedTimeString(CURRENT_TZ));
}
private void testCurrentTimestampFunc(Pair pair) {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.CURRENT_TIMESTAMP,
VmName.EXPAND);
f.checkScalar("CURRENT_TIMESTAMP", TIMESTAMP_PATTERN,
"TIMESTAMP(0) NOT NULL");
f.checkFails("^CURRENT_TIMESTAMP()^",
"No match found for function signature CURRENT_TIMESTAMP\\(\\)",
false);
f.checkFails("^CURRENT_TIMESTAMP(4000000000)^",
LITERAL_OUT_OF_RANGE_MESSAGE, false);
f.checkScalar("CURRENT_TIMESTAMP(1)", TIMESTAMP_PATTERN,
"TIMESTAMP(1) NOT NULL");
f.checkScalar("CAST(CURRENT_TIMESTAMP AS VARCHAR(30))",
Pattern.compile(pair.left + "[0-9][0-9]:[0-9][0-9]"),
"VARCHAR(30) NOT NULL");
f.checkScalar("CURRENT_TIMESTAMP",
Pattern.compile(pair.left + "[0-9][0-9]:[0-9][0-9]"),
"TIMESTAMP(0) NOT NULL");
pair.right.close();
}
/**
* Returns a time string, in GMT, that will be valid for at least 2 minutes.
*
* For example, at "2005-01-01 12:34:56 PST", returns "2005-01-01 20:".
* At "2005-01-01 12:34:59 PST", waits a minute, then returns "2005-01-01
* 21:".
*
* @param tz Time zone
* @return Time string
*/
protected static Pair currentTimeString(TimeZone tz) {
final Calendar calendar = getCalendarNotTooNear(Calendar.HOUR_OF_DAY);
final Hook.Closeable closeable = () -> { };
return Pair.of(toTimeString(tz, calendar), closeable);
}
private static Pair fixedTimeString(TimeZone tz) {
final Calendar calendar = getFixedCalendar();
final long timeInMillis = calendar.getTimeInMillis();
final Hook.Closeable closeable = Hook.CURRENT_TIME.addThread(
(Consumer>) o -> o.set(timeInMillis));
return Pair.of(toTimeString(tz, calendar), closeable);
}
private static String toTimeString(TimeZone tz, Calendar cal) {
SimpleDateFormat sdf = getDateFormatter("yyyy-MM-dd HH:", tz);
return sdf.format(cal.getTime());
}
@Tag("slow")
@Test void testCurrentDateFuncWithCurrentTime() {
testCurrentDateFunc(currentTimeString(LOCAL_TZ));
}
@Test void testCurrentDateFuncWithFixedTime() {
testCurrentDateFunc(fixedTimeString(LOCAL_TZ));
}
private void testCurrentDateFunc(Pair pair) {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.CURRENT_DATE, VM_FENNEL);
// A tester with a lenient conformance that allows parentheses.
final SqlOperatorFixture f1 = f.withConformance(SqlConformanceEnum.LENIENT);
f.checkScalar("CURRENT_DATE", DATE_PATTERN, "DATE NOT NULL");
f.checkScalar(
"(CURRENT_DATE - CURRENT_DATE) DAY",
"+0",
"INTERVAL DAY NOT NULL");
f.checkBoolean("CURRENT_DATE IS NULL", false);
f.checkBoolean("CURRENT_DATE IS NOT NULL", true);
f.checkBoolean("NOT (CURRENT_DATE IS NULL)", true);
f.checkFails("^CURRENT_DATE()^",
"No match found for function signature CURRENT_DATE\\(\\)",
false);
f1.checkBoolean("CURRENT_DATE() IS NULL", false);
f1.checkBoolean("CURRENT_DATE IS NOT NULL", true);
f1.checkBoolean("NOT (CURRENT_DATE() IS NULL)", true);
f1.checkType("CURRENT_DATE", "DATE NOT NULL");
f1.checkType("CURRENT_DATE()", "DATE NOT NULL");
f1.checkType("CURRENT_TIMESTAMP()", "TIMESTAMP(0) NOT NULL");
f1.checkType("CURRENT_TIME()", "TIME(0) NOT NULL");
// Check the actual value.
final String dateString = pair.left;
try (Hook.Closeable ignore = pair.right) {
f.checkScalar("CAST(CURRENT_DATE AS VARCHAR(30))",
dateString.substring(0, 10),
"VARCHAR(30) NOT NULL");
f.checkScalar("CURRENT_DATE",
dateString.substring(0, 10),
"DATE NOT NULL");
f1.checkScalar("CAST(CURRENT_DATE AS VARCHAR(30))",
dateString.substring(0, 10),
"VARCHAR(30) NOT NULL");
f1.checkScalar("CAST(CURRENT_DATE() AS VARCHAR(30))",
dateString.substring(0, 10),
"VARCHAR(30) NOT NULL");
f1.checkScalar("CURRENT_DATE",
dateString.substring(0, 10),
"DATE NOT NULL");
f1.checkScalar("CURRENT_DATE()",
dateString.substring(0, 10),
"DATE NOT NULL");
}
}
@Test void testLastDayFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.LAST_DAY, VmName.EXPAND);
f.checkScalar("last_day(DATE '2019-02-10')",
"2019-02-28", "DATE NOT NULL");
f.checkScalar("last_day(DATE '2019-06-10')",
"2019-06-30", "DATE NOT NULL");
f.checkScalar("last_day(DATE '2019-07-10')",
"2019-07-31", "DATE NOT NULL");
f.checkScalar("last_day(DATE '2019-09-10')",
"2019-09-30", "DATE NOT NULL");
f.checkScalar("last_day(DATE '2019-12-10')",
"2019-12-31", "DATE NOT NULL");
f.checkScalar("last_day(DATE '9999-12-10')",
"9999-12-31", "DATE NOT NULL");
// Edge tests
f.checkScalar("last_day(DATE '1900-01-01')",
"1900-01-31", "DATE NOT NULL");
f.checkScalar("last_day(DATE '1935-02-01')",
"1935-02-28", "DATE NOT NULL");
f.checkScalar("last_day(DATE '1965-09-01')",
"1965-09-30", "DATE NOT NULL");
f.checkScalar("last_day(DATE '1970-01-01')",
"1970-01-31", "DATE NOT NULL");
f.checkScalar("last_day(DATE '2019-02-28')",
"2019-02-28", "DATE NOT NULL");
f.checkScalar("last_day(DATE '2019-12-31')",
"2019-12-31", "DATE NOT NULL");
f.checkScalar("last_day(DATE '2019-01-01')",
"2019-01-31", "DATE NOT NULL");
f.checkScalar("last_day(DATE '2019-06-30')",
"2019-06-30", "DATE NOT NULL");
f.checkScalar("last_day(DATE '2020-02-20')",
"2020-02-29", "DATE NOT NULL");
f.checkScalar("last_day(DATE '2020-02-29')",
"2020-02-29", "DATE NOT NULL");
f.checkScalar("last_day(DATE '9999-12-31')",
"9999-12-31", "DATE NOT NULL");
f.checkNull("last_day(cast(null as date))");
f.checkScalar("last_day(TIMESTAMP '2019-02-10 02:10:12')",
"2019-02-28", "DATE NOT NULL");
f.checkScalar("last_day(TIMESTAMP '2019-06-10 06:10:16')",
"2019-06-30", "DATE NOT NULL");
f.checkScalar("last_day(TIMESTAMP '2019-07-10 07:10:17')",
"2019-07-31", "DATE NOT NULL");
f.checkScalar("last_day(TIMESTAMP '2019-09-10 09:10:19')",
"2019-09-30", "DATE NOT NULL");
f.checkScalar("last_day(TIMESTAMP '2019-12-10 12:10:22')",
"2019-12-31", "DATE NOT NULL");
// Edge tests
f.checkScalar("last_day(TIMESTAMP '1900-01-01 01:01:02')",
"1900-01-31", "DATE NOT NULL");
f.checkScalar("last_day(TIMESTAMP '1935-02-01 02:01:03')",
"1935-02-28", "DATE NOT NULL");
f.checkScalar("last_day(TIMESTAMP '1970-01-01 01:01:02')",
"1970-01-31", "DATE NOT NULL");
f.checkScalar("last_day(TIMESTAMP '2019-02-28 02:28:30')",
"2019-02-28", "DATE NOT NULL");
f.checkScalar("last_day(TIMESTAMP '2019-12-31 12:31:43')",
"2019-12-31", "DATE NOT NULL");
f.checkScalar("last_day(TIMESTAMP '2019-01-01 01:01:02')",
"2019-01-31", "DATE NOT NULL");
f.checkScalar("last_day(TIMESTAMP '2019-06-30 06:30:36')",
"2019-06-30", "DATE NOT NULL");
f.checkScalar("last_day(TIMESTAMP '2020-02-20 02:20:33')",
"2020-02-29", "DATE NOT NULL");
f.checkScalar("last_day(TIMESTAMP '2020-02-29 02:29:31')",
"2020-02-29", "DATE NOT NULL");
f.checkScalar("last_day(TIMESTAMP '9999-12-31 12:31:43')",
"9999-12-31", "DATE NOT NULL");
f.checkNull("last_day(cast(null as timestamp))");
}
/** Tests the {@code SUBSTRING} operator. Many test cases that used to be
* have been moved to {@link SubFunChecker#assertSubFunReturns}, and are
* called for both {@code SUBSTRING} and {@code SUBSTR}. */
@Test void testSubstringFunction() {
final SqlOperatorFixture f = fixture();
checkSubstringFunction(f);
checkSubstringFunction(f.withConformance(SqlConformanceEnum.BIG_QUERY));
}
void checkSubstringFunction(SqlOperatorFixture f) {
f.setFor(SqlStdOperatorTable.SUBSTRING);
f.checkString("substring('abc' from 1 for 2)",
"ab", "VARCHAR(3) NOT NULL");
f.checkString("substring(x'aabbcc' from 1 for 2)",
"aabb", "VARBINARY(3) NOT NULL");
switch (f.conformance().semantics()) {
case BIG_QUERY:
f.checkString("substring('abc' from 1 for -1)", "",
"VARCHAR(3) NOT NULL");
f.checkString("substring(x'aabbcc' from 1 for -1)", "",
"VARBINARY(3) NOT NULL");
break;
default:
f.checkFails("substring('abc' from 1 for -1)",
"Substring error: negative substring length not allowed",
true);
f.checkFails("substring(x'aabbcc' from 1 for -1)",
"Substring error: negative substring length not allowed",
true);
}
if (Bug.FRG296_FIXED) {
// substring regexp not supported yet
f.checkString("substring('foobar' from '%#\"o_b#\"%' for'#')",
"oob", "xx");
}
f.checkNull("substring(cast(null as varchar(1)),1,2)");
f.checkNull("substring(cast(null as varchar(1)) FROM 1 FOR 2)");
f.checkNull("substring('abc' FROM cast(null as integer) FOR 2)");
f.checkNull("substring('abc' FROM cast(null as integer))");
f.checkNull("substring('abc' FROM 2 FOR cast(null as integer))");
}
/** Tests the non-standard SUBSTR function, that has syntax
* "SUBSTR(value, start [, length ])", as used in BigQuery. */
@Test void testBigQuerySubstrFunction() {
substrChecker(SqlLibrary.BIG_QUERY, SqlLibraryOperators.SUBSTR_BIG_QUERY)
.check();
}
/** Tests the non-standard SUBSTR function, that has syntax
* "SUBSTR(value, start [, length ])", as used in Oracle. */
@Test void testMysqlSubstrFunction() {
substrChecker(SqlLibrary.MYSQL, SqlLibraryOperators.SUBSTR_MYSQL)
.check();
}
/** Tests the non-standard SUBSTR function, that has syntax
* "SUBSTR(value, start [, length ])", as used in Oracle. */
@Test void testOracleSubstrFunction() {
substrChecker(SqlLibrary.ORACLE, SqlLibraryOperators.SUBSTR_ORACLE)
.check();
}
/** Tests the non-standard SUBSTR function, that has syntax
* "SUBSTR(value, start [, length ])", as used in PostgreSQL. */
@Test void testPostgresqlSubstrFunction() {
substrChecker(SqlLibrary.POSTGRESQL, SqlLibraryOperators.SUBSTR_POSTGRESQL)
.check();
}
/** Tests the standard {@code SUBSTRING} function in the mode that has
* BigQuery's non-standard semantics. */
@Test void testBigQuerySubstringFunction() {
substringChecker(SqlConformanceEnum.BIG_QUERY, SqlLibrary.BIG_QUERY)
.check();
}
/** Tests the standard {@code SUBSTRING} function in ISO standard
* semantics. */
@Test void testStandardSubstringFunction() {
substringChecker(SqlConformanceEnum.STRICT_2003, SqlLibrary.POSTGRESQL)
.check();
}
SubFunChecker substringChecker(SqlConformanceEnum conformance,
SqlLibrary library) {
final SqlOperatorFixture f = fixture();
return new SubFunChecker(
f.withConnectionFactory(cf ->
cf.with(ConnectionFactories.add(CalciteAssert.SchemaSpec.HR))
.with(CalciteConnectionProperty.CONFORMANCE, conformance)),
library,
SqlStdOperatorTable.SUBSTRING);
}
SubFunChecker substrChecker(SqlLibrary library, SqlFunction function) {
return new SubFunChecker(fixture().withLibrary(library), library, function);
}
/** Tests various configurations of {@code SUBSTR} and {@code SUBSTRING}
* functions. */
static class SubFunChecker {
final SqlOperatorFixture f;
final SqlLibrary library;
final SqlFunction function;
SubFunChecker(SqlOperatorFixture f, SqlLibrary library,
SqlFunction function) {
this.f = f;
f.setFor(function);
this.library = library;
this.function = function;
}
void check() {
// The following tests have been checked on Oracle 11g R2, PostgreSQL 9.6,
// MySQL 5.6, Google BigQuery.
//
// PostgreSQL and MySQL have a standard SUBSTRING(x FROM s [FOR l])
// operator, and its behavior is identical to their SUBSTRING(x, s [, l]).
// Oracle and BigQuery do not have SUBSTRING.
assertReturns("abc", 1, "abc");
assertReturns("abc", 2, "bc");
assertReturns("abc", 3, "c");
assertReturns("abc", 4, "");
assertReturns("abc", 5, "");
switch (library) {
case BIG_QUERY:
case ORACLE:
assertReturns("abc", 0, "abc");
assertReturns("abc", 0, 5, "abc");
assertReturns("abc", 0, 4, "abc");
assertReturns("abc", 0, 3, "abc");
assertReturns("abc", 0, 2, "ab");
break;
case POSTGRESQL:
assertReturns("abc", 0, "abc");
assertReturns("abc", 0, 5, "abc");
assertReturns("abc", 0, 4, "abc");
assertReturns("abc", 0, 3, "ab");
assertReturns("abc", 0, 2, "a");
break;
case MYSQL:
assertReturns("abc", 0, "");
assertReturns("abc", 0, 5, "");
assertReturns("abc", 0, 4, "");
assertReturns("abc", 0, 3, "");
assertReturns("abc", 0, 2, "");
break;
default:
throw new AssertionError(library);
}
assertReturns("abc", 0, 0, "");
assertReturns("abc", 2, 8, "bc");
assertReturns("abc", 1, 0, "");
assertReturns("abc", 1, 2, "ab");
assertReturns("abc", 1, 3, "abc");
assertReturns("abc", 4, 3, "");
assertReturns("abc", 4, 4, "");
assertReturns("abc", 8, 2, "");
switch (library) {
case POSTGRESQL:
assertReturns("abc", 1, -1, null);
assertReturns("abc", 4, -1, null);
break;
default:
assertReturns("abc", 1, -1, "");
assertReturns("abc", 4, -1, "");
break;
}
// For negative start, BigQuery matches Oracle.
switch (library) {
case BIG_QUERY:
case MYSQL:
case ORACLE:
assertReturns("abc", -2, "bc");
assertReturns("abc", -1, "c");
assertReturns("abc", -2, 1, "b");
assertReturns("abc", -2, 2, "bc");
assertReturns("abc", -2, 3, "bc");
assertReturns("abc", -2, 4, "bc");
assertReturns("abc", -2, 5, "bc");
assertReturns("abc", -2, 6, "bc");
assertReturns("abc", -2, 7, "bc");
assertReturns("abcde", -3, 2, "cd");
assertReturns("abc", -3, 3, "abc");
assertReturns("abc", -3, 8, "abc");
assertReturns("abc", -1, 4, "c");
break;
case POSTGRESQL:
assertReturns("abc", -2, "abc");
assertReturns("abc", -1, "abc");
assertReturns("abc", -2, 1, "");
assertReturns("abc", -2, 2, "");
assertReturns("abc", -2, 3, "");
assertReturns("abc", -2, 4, "a");
assertReturns("abc", -2, 5, "ab");
assertReturns("abc", -2, 6, "abc");
assertReturns("abc", -2, 7, "abc");
assertReturns("abcde", -3, 2, "");
assertReturns("abc", -3, 3, "");
assertReturns("abc", -3, 8, "abc");
assertReturns("abc", -1, 4, "ab");
break;
default:
throw new AssertionError(library);
}
// For negative start and start + length between 0 and actual-length,
// confusion reigns.
switch (library) {
case BIG_QUERY:
assertReturns("abc", -4, 6, "abc");
break;
case MYSQL:
case ORACLE:
assertReturns("abc", -4, 6, "");
break;
case POSTGRESQL:
assertReturns("abc", -4, 6, "a");
break;
default:
throw new AssertionError(library);
}
// For very negative start, BigQuery differs from Oracle and PostgreSQL.
switch (library) {
case BIG_QUERY:
assertReturns("abc", -4, 3, "abc");
assertReturns("abc", -5, 1, "abc");
assertReturns("abc", -10, 2, "abc");
assertReturns("abc", -500, 1, "abc");
break;
case MYSQL:
case ORACLE:
case POSTGRESQL:
assertReturns("abc", -4, 3, "");
assertReturns("abc", -5, 1, "");
assertReturns("abc", -10, 2, "");
assertReturns("abc", -500, 1, "");
break;
default:
throw new AssertionError(library);
}
}
void assertReturns(String s, int start, String expected) {
assertSubFunReturns(false, s, start, null, expected);
assertSubFunReturns(true, s, start, null, expected);
}
void assertReturns(String s, int start, @Nullable Integer end,
@Nullable String expected) {
assertSubFunReturns(false, s, start, end, expected);
assertSubFunReturns(true, s, start, end, expected);
}
void assertSubFunReturns(boolean binary, String s, int start,
@Nullable Integer end, @Nullable String expected) {
final String v = binary
? "x'" + DOUBLER.apply(s) + "'"
: "'" + s + "'";
final String type =
(binary ? "VARBINARY" : "VARCHAR") + "(" + s.length() + ")";
final String value = "CAST(" + v + " AS " + type + ")";
final String expression;
if (function == SqlStdOperatorTable.SUBSTRING) {
expression = "substring(" + value + " FROM " + start
+ (end == null ? "" : (" FOR " + end)) + ")";
} else {
expression = "substr(" + value + ", " + start
+ (end == null ? "" : (", " + end)) + ")";
}
if (expected == null) {
f.checkFails(expression,
"Substring error: negative substring length not allowed", true);
} else {
if (binary) {
expected = DOUBLER.apply(expected);
}
f.checkString(expression, expected, type + NON_NULLABLE_SUFFIX);
}
}
}
@Test void testTrimFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.TRIM, VmName.EXPAND);
// SQL:2003 6.29.11 Trimming a CHAR yields a VARCHAR
f.checkString("trim('a' from 'aAa')", "A", "VARCHAR(3) NOT NULL");
f.checkString("trim(both 'a' from 'aAa')", "A", "VARCHAR(3) NOT NULL");
f.checkString("trim(leading 'a' from 'aAa')", "Aa", "VARCHAR(3) NOT NULL");
f.checkString("trim(trailing 'a' from 'aAa')", "aA", "VARCHAR(3) NOT NULL");
f.checkNull("trim(cast(null as varchar(1)) from 'a')");
f.checkNull("trim('a' from cast(null as varchar(1)))");
// SQL:2003 6.29.9: trim string must have length=1. Failure occurs
// at runtime.
//
// TODO: Change message to "Invalid argument\(s\) for
// 'TRIM' function".
// The message should come from a resource file, and should still
// have the SQL error code 22027.
f.checkFails("trim('xy' from 'abcde')",
"Trim error: trim character must be exactly 1 character",
true);
f.checkFails("trim('' from 'abcde')",
"Trim error: trim character must be exactly 1 character",
true);
final SqlOperatorFixture f1 = f.withConformance(SqlConformanceEnum.MYSQL_5);
f1.checkString("trim(leading 'eh' from 'hehe__hehe')", "__hehe",
"VARCHAR(10) NOT NULL");
f1.checkString("trim(trailing 'eh' from 'hehe__hehe')", "hehe__",
"VARCHAR(10) NOT NULL");
f1.checkString("trim('eh' from 'hehe__hehe')", "__", "VARCHAR(10) NOT NULL");
}
@Test void testRtrimFunc() {
final SqlOperatorFixture f = fixture()
.setFor(SqlLibraryOperators.RTRIM, VmName.EXPAND)
.withLibrary(SqlLibrary.ORACLE);
f.checkString("rtrim(' aAa ')", " aAa", "VARCHAR(6) NOT NULL");
f.checkNull("rtrim(CAST(NULL AS VARCHAR(6)))");
}
@Test void testLtrimFunc() {
final SqlOperatorFixture f = fixture()
.setFor(SqlLibraryOperators.LTRIM, VmName.EXPAND)
.withLibrary(SqlLibrary.ORACLE);
f.checkString("ltrim(' aAa ')", "aAa ", "VARCHAR(6) NOT NULL");
f.checkNull("ltrim(CAST(NULL AS VARCHAR(6)))");
}
@Test void testGreatestFunc() {
final SqlOperatorFixture f = fixture()
.setFor(SqlLibraryOperators.GREATEST, VmName.EXPAND)
.withLibrary(SqlLibrary.ORACLE);
f.checkString("greatest('on', 'earth')", "on ", "CHAR(5) NOT NULL");
f.checkString("greatest('show', 'on', 'earth')", "show ",
"CHAR(5) NOT NULL");
f.checkScalar("greatest(12, CAST(NULL AS INTEGER), 3)", isNullValue(),
"INTEGER");
f.checkScalar("greatest(false, true)", true, "BOOLEAN NOT NULL");
final SqlOperatorFixture f12 = f.forOracle(SqlConformanceEnum.ORACLE_12);
f12.checkString("greatest('on', 'earth')", "on", "VARCHAR(5) NOT NULL");
f12.checkString("greatest('show', 'on', 'earth')", "show",
"VARCHAR(5) NOT NULL");
}
@Test void testLeastFunc() {
final SqlOperatorFixture f = fixture()
.setFor(SqlLibraryOperators.LEAST, VmName.EXPAND)
.withLibrary(SqlLibrary.ORACLE);
f.checkString("least('on', 'earth')", "earth", "CHAR(5) NOT NULL");
f.checkString("least('show', 'on', 'earth')", "earth",
"CHAR(5) NOT NULL");
f.checkScalar("least(12, CAST(NULL AS INTEGER), 3)", isNullValue(),
"INTEGER");
f.checkScalar("least(false, true)", false, "BOOLEAN NOT NULL");
final SqlOperatorFixture f12 = f.forOracle(SqlConformanceEnum.ORACLE_12);
f12.checkString("least('on', 'earth')", "earth", "VARCHAR(5) NOT NULL");
f12.checkString("least('show', 'on', 'earth')", "earth",
"VARCHAR(5) NOT NULL");
}
@Test void testNvlFunc() {
final SqlOperatorFixture f = fixture()
.setFor(SqlLibraryOperators.NVL, VmName.EXPAND)
.withLibrary(SqlLibrary.ORACLE);
f.checkScalar("nvl(1, 2)", "1", "INTEGER NOT NULL");
f.checkFails("^nvl(1, true)^", "Parameters must be of the same type",
false);
f.checkScalar("nvl(true, false)", true, "BOOLEAN NOT NULL");
f.checkScalar("nvl(false, true)", false, "BOOLEAN NOT NULL");
f.checkString("nvl('abc', 'de')", "abc", "CHAR(3) NOT NULL");
f.checkString("nvl('abc', 'defg')", "abc ", "CHAR(4) NOT NULL");
f.checkString("nvl('abc', CAST(NULL AS VARCHAR(20)))", "abc",
"VARCHAR(20) NOT NULL");
f.checkString("nvl(CAST(NULL AS VARCHAR(20)), 'abc')", "abc",
"VARCHAR(20) NOT NULL");
f.checkNull("nvl(CAST(NULL AS VARCHAR(6)), cast(NULL AS VARCHAR(4)))");
final SqlOperatorFixture f12 = f.forOracle(SqlConformanceEnum.ORACLE_12);
f12.checkString("nvl('abc', 'de')", "abc", "VARCHAR(3) NOT NULL");
f12.checkString("nvl('abc', 'defg')", "abc", "VARCHAR(4) NOT NULL");
f12.checkString("nvl('abc', CAST(NULL AS VARCHAR(20)))", "abc",
"VARCHAR(20) NOT NULL");
f12.checkString("nvl(CAST(NULL AS VARCHAR(20)), 'abc')", "abc",
"VARCHAR(20) NOT NULL");
f12.checkNull("nvl(CAST(NULL AS VARCHAR(6)), cast(NULL AS VARCHAR(4)))");
}
@Test void testDecodeFunc() {
checkDecodeFunc(fixture().withLibrary(SqlLibrary.ORACLE));
}
void checkDecodeFunc(SqlOperatorFixture f) {
f.setFor(SqlLibraryOperators.DECODE, VmName.EXPAND);
f.checkScalar("decode(0, 0, 'a', 1, 'b', 2, 'c')", "a", "CHAR(1)");
f.checkScalar("decode(1, 0, 'a', 1, 'b', 2, 'c')", "b", "CHAR(1)");
// if there are duplicates, take the first match
f.checkScalar("decode(1, 0, 'a', 1, 'b', 1, 'z', 2, 'c')", "b",
"CHAR(1)");
// if there's no match, and no "else", return null
f.checkScalar("decode(3, 0, 'a', 1, 'b', 2, 'c')", isNullValue(),
"CHAR(1)");
// if there's no match, return the "else" value
f.checkScalar("decode(3, 0, 'a', 1, 'b', 2, 'c', 'd')", "d",
"CHAR(1) NOT NULL");
f.checkScalar("decode(1, 0, 'a', 1, 'b', 2, 'c', 'd')", "b",
"CHAR(1) NOT NULL");
// nulls match
f.checkScalar("decode(cast(null as integer), 0, 'a',\n"
+ " cast(null as integer), 'b', 2, 'c', 'd')", "b",
"CHAR(1) NOT NULL");
}
@Test void testWindow() {
final SqlOperatorFixture f = fixture();
f.check("select sum(1) over (order by x)\n"
+ "from (select 1 as x, 2 as y\n"
+ " from (values (true)))",
SqlTests.INTEGER_TYPE_CHECKER, 1);
}
@Test void testElementFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.ELEMENT, VM_FENNEL, VM_JAVA);
f.checkString("element(multiset['abc'])", "abc", "CHAR(3) NOT NULL");
f.checkNull("element(multiset[cast(null as integer)])");
}
@Test void testCardinalityFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.CARDINALITY, VM_FENNEL, VM_JAVA);
f.checkScalarExact("cardinality(multiset[cast(null as integer),2])", 2);
if (!f.brokenTestsEnabled()) {
return;
}
// applied to array
f.checkScalarExact("cardinality(array['foo', 'bar'])", 2);
// applied to map
f.checkScalarExact("cardinality(map['foo', 1, 'bar', 2])", 2);
}
@Test void testMemberOfOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.MEMBER_OF, VM_FENNEL, VM_JAVA);
f.checkBoolean("1 member of multiset[1]", true);
f.checkBoolean("'2' member of multiset['1']", false);
f.checkBoolean("cast(null as double) member of"
+ " multiset[cast(null as double)]", true);
f.checkBoolean("cast(null as double) member of multiset[1.1]", false);
f.checkBoolean("1.1 member of multiset[cast(null as double)]", false);
}
@Test void testMultisetUnionOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.MULTISET_UNION_DISTINCT,
VM_FENNEL, VM_JAVA);
f.checkBoolean("multiset[1,2] submultiset of "
+ "(multiset[2] multiset union multiset[1])", true);
f.checkScalar("cardinality(multiset[1, 2, 3, 4, 2] "
+ "multiset union distinct multiset[1, 4, 5, 7, 8])",
"7",
"INTEGER NOT NULL");
f.checkScalar("cardinality(multiset[1, 2, 3, 4, 2] "
+ "multiset union distinct multiset[1, 4, 5, 7, 8])",
"7",
"INTEGER NOT NULL");
f.checkBoolean("(multiset[1, 2, 3, 4, 2] "
+ "multiset union distinct multiset[1, 4, 5, 7, 8]) "
+ "submultiset of multiset[1, 2, 3, 4, 5, 7, 8]",
true);
f.checkBoolean("(multiset[1, 2, 3, 4, 2] "
+ "multiset union distinct multiset[1, 4, 5, 7, 8]) "
+ "submultiset of multiset[1, 2, 3, 4, 5, 7, 8]",
true);
f.checkScalar("cardinality(multiset['a', 'b', 'c'] "
+ "multiset union distinct multiset['c', 'd', 'e'])",
"5",
"INTEGER NOT NULL");
f.checkScalar("cardinality(multiset['a', 'b', 'c'] "
+ "multiset union distinct multiset['c', 'd', 'e'])",
"5",
"INTEGER NOT NULL");
f.checkBoolean("(multiset['a', 'b', 'c'] "
+ "multiset union distinct multiset['c', 'd', 'e'])"
+ " submultiset of multiset['a', 'b', 'c', 'd', 'e']",
true);
f.checkBoolean("(multiset['a', 'b', 'c'] "
+ "multiset union distinct multiset['c', 'd', 'e'])"
+ " submultiset of multiset['a', 'b', 'c', 'd', 'e']",
true);
f.checkScalar("multiset[cast(null as double)] "
+ "multiset union multiset[cast(null as double)]",
"[null, null]",
"DOUBLE MULTISET NOT NULL");
f.checkScalar("multiset[cast(null as boolean)] "
+ "multiset union multiset[cast(null as boolean)]",
"[null, null]",
"BOOLEAN MULTISET NOT NULL");
}
@Test void testMultisetUnionAllOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.MULTISET_UNION, VM_FENNEL, VM_JAVA);
f.checkScalar("cardinality(multiset[1, 2, 3, 4, 2] "
+ "multiset union all multiset[1, 4, 5, 7, 8])",
"10",
"INTEGER NOT NULL");
f.checkBoolean("(multiset[1, 2, 3, 4, 2] "
+ "multiset union all multiset[1, 4, 5, 7, 8]) "
+ "submultiset of multiset[1, 2, 3, 4, 5, 7, 8]",
false);
f.checkBoolean("(multiset[1, 2, 3, 4, 2] "
+ "multiset union all multiset[1, 4, 5, 7, 8]) "
+ "submultiset of multiset[1, 1, 2, 2, 3, 4, 4, 5, 7, 8]",
true);
f.checkScalar("cardinality(multiset['a', 'b', 'c'] "
+ "multiset union all multiset['c', 'd', 'e'])",
"6",
"INTEGER NOT NULL");
f.checkBoolean("(multiset['a', 'b', 'c'] "
+ "multiset union all multiset['c', 'd', 'e']) "
+ "submultiset of multiset['a', 'b', 'c', 'd', 'e']",
false);
f.checkBoolean("(multiset['a', 'b', 'c'] "
+ "multiset union distinct multiset['c', 'd', 'e']) "
+ "submultiset of multiset['a', 'b', 'c', 'd', 'e', 'c']",
true);
f.checkScalar("multiset[cast(null as double)] "
+ "multiset union all multiset[cast(null as double)]",
"[null, null]",
"DOUBLE MULTISET NOT NULL");
f.checkScalar("multiset[cast(null as boolean)] "
+ "multiset union all multiset[cast(null as boolean)]",
"[null, null]",
"BOOLEAN MULTISET NOT NULL");
}
@Test void testSubMultisetOfOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.SUBMULTISET_OF, VM_FENNEL, VM_JAVA);
f.checkBoolean("multiset[2] submultiset of multiset[1]", false);
f.checkBoolean("multiset[1] submultiset of multiset[1]", true);
f.checkBoolean("multiset[1, 2] submultiset of multiset[1]", false);
f.checkBoolean("multiset[1] submultiset of multiset[1, 2]", true);
f.checkBoolean("multiset[1, 2] submultiset of multiset[1, 2]", true);
f.checkBoolean("multiset['a', 'b'] submultiset of "
+ "multiset['c', 'd', 's', 'a']", false);
f.checkBoolean("multiset['a', 'd'] submultiset of "
+ "multiset['c', 's', 'a', 'w', 'd']", true);
f.checkBoolean("multiset['q', 'a'] submultiset of multiset['a', 'q']",
true);
}
@Test void testNotSubMultisetOfOperator() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.NOT_SUBMULTISET_OF, VM_FENNEL, VM_JAVA);
f.checkBoolean("multiset[2] not submultiset of multiset[1]", true);
f.checkBoolean("multiset[1] not submultiset of multiset[1]", false);
f.checkBoolean("multiset[1, 2] not submultiset of multiset[1]", true);
f.checkBoolean("multiset[1] not submultiset of multiset[1, 2]", false);
f.checkBoolean("multiset[1, 2] not submultiset of multiset[1, 2]", false);
f.checkBoolean("multiset['a', 'b'] not submultiset of "
+ "multiset['c', 'd', 's', 'a']", true);
f.checkBoolean("multiset['a', 'd'] not submultiset of "
+ "multiset['c', 's', 'a', 'w', 'd']", false);
f.checkBoolean("multiset['q', 'a'] not submultiset of "
+ "multiset['a', 'q']", false);
}
@Test void testCollectFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.COLLECT, VM_FENNEL, VM_JAVA);
f.checkFails("collect(^*^)", "Unknown identifier '\\*'", false);
f.checkAggType("collect(1)", "INTEGER NOT NULL MULTISET NOT NULL");
f.checkAggType("collect(1.2)", "DECIMAL(2, 1) NOT NULL MULTISET NOT NULL");
f.checkAggType("collect(DISTINCT 1.5)", "DECIMAL(2, 1) NOT NULL MULTISET NOT NULL");
f.checkFails("^collect()^",
"Invalid number of arguments to function 'COLLECT'. Was expecting 1 arguments",
false);
f.checkFails("^collect(1, 2)^",
"Invalid number of arguments to function 'COLLECT'. Was expecting 1 arguments",
false);
final String[] values = {"0", "CAST(null AS INTEGER)", "2", "2"};
f.checkAgg("collect(x)", values, isSet("[0, 2, 2]"));
f.checkAgg("collect(x) within group(order by x desc)", values,
isSet("[2, 2, 0]"));
if (!f.brokenTestsEnabled()) {
return;
}
f.checkAgg("collect(CASE x WHEN 0 THEN NULL ELSE -1 END)", values,
isSingle(-3));
f.checkAgg("collect(DISTINCT CASE x WHEN 0 THEN NULL ELSE -1 END)",
values, isSingle(-1));
f.checkAgg("collect(DISTINCT x)", values, isSingle(2));
}
@Test void testListAggFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.LISTAGG, VM_FENNEL, VM_JAVA);
f.checkFails("listagg(^*^)", "Unknown identifier '\\*'", false);
f.checkAggType("listagg(12)", "VARCHAR NOT NULL");
f.enableTypeCoercion(false)
.checkFails("^listagg(12)^",
"Cannot apply 'LISTAGG' to arguments of type .*'\n.*'", false);
f.checkAggType("listagg(cast(12 as double))", "VARCHAR NOT NULL");
f.enableTypeCoercion(false)
.checkFails("^listagg(cast(12 as double))^",
"Cannot apply 'LISTAGG' to arguments of type .*'\n.*'", false);
f.checkFails("^listagg()^",
"Invalid number of arguments to function 'LISTAGG'. Was expecting 1 arguments",
false);
f.checkFails("^listagg('1', '2', '3')^",
"Invalid number of arguments to function 'LISTAGG'. Was expecting 1 arguments",
false);
f.checkAggType("listagg('test')", "CHAR(4) NOT NULL");
f.checkAggType("listagg('test', ', ')", "CHAR(4) NOT NULL");
final String[] values1 = {"'hello'", "CAST(null AS CHAR)", "'world'", "'!'"};
f.checkAgg("listagg(x)", values1, isSingle("hello,world,!"));
final String[] values2 = {"0", "1", "2", "3"};
f.checkAgg("listagg(cast(x as CHAR))", values2, isSingle("0,1,2,3"));
}
@Test void testStringAggFunc() {
final SqlOperatorFixture f = fixture();
checkStringAggFunc(f.withLibrary(SqlLibrary.POSTGRESQL));
checkStringAggFunc(f.withLibrary(SqlLibrary.BIG_QUERY));
checkStringAggFuncFails(f.withLibrary(SqlLibrary.MYSQL));
}
private void checkStringAggFunc(SqlOperatorFixture f) {
final String[] values = {"'x'", "null", "'yz'"};
f.checkAgg("string_agg(x)", values, isSingle("x,yz"));
f.checkAgg("string_agg(x,':')", values, isSingle("x:yz"));
f.checkAgg("string_agg(x,':' order by x)", values, isSingle("x:yz"));
f.checkAgg("string_agg(x order by char_length(x) desc)", values, isSingle("yz,x"));
f.checkAggFails("^string_agg(x respect nulls order by x desc)^", values,
"Cannot specify IGNORE NULLS or RESPECT NULLS following 'STRING_AGG'",
false);
f.checkAggFails("^string_agg(x order by x desc)^ respect nulls", values,
"Cannot specify IGNORE NULLS or RESPECT NULLS following 'STRING_AGG'",
false);
}
private void checkStringAggFuncFails(SqlOperatorFixture f) {
final String[] values = {"'x'", "'y'"};
f.checkAggFails("^string_agg(x)^", values,
"No match found for function signature STRING_AGG\\(\\)",
false);
f.checkAggFails("^string_agg(x, ',')^", values,
"No match found for function signature STRING_AGG\\(, "
+ "\\)",
false);
f.checkAggFails("^string_agg(x, ',' order by x desc)^", values,
"No match found for function signature STRING_AGG\\(, "
+ "\\)",
false);
}
@Test void testGroupConcatFunc() {
final SqlOperatorFixture f = fixture();
checkGroupConcatFunc(f.withLibrary(SqlLibrary.MYSQL));
checkGroupConcatFuncFails(f.withLibrary(SqlLibrary.BIG_QUERY));
checkGroupConcatFuncFails(f.withLibrary(SqlLibrary.POSTGRESQL));
}
private void checkGroupConcatFunc(SqlOperatorFixture f) {
final String[] values = {"'x'", "null", "'yz'"};
f.checkAgg("group_concat(x)", values, isSingle("x,yz"));
f.checkAgg("group_concat(x,':')", values, isSingle("x:yz"));
f.checkAgg("group_concat(x,':' order by x)", values, isSingle("x:yz"));
f.checkAgg("group_concat(x order by x separator '|')", values,
isSingle("x|yz"));
f.checkAgg("group_concat(x order by char_length(x) desc)", values,
isSingle("yz,x"));
f.checkAggFails("^group_concat(x respect nulls order by x desc)^", values,
"Cannot specify IGNORE NULLS or RESPECT NULLS following 'GROUP_CONCAT'",
false);
f.checkAggFails("^group_concat(x order by x desc)^ respect nulls", values,
"Cannot specify IGNORE NULLS or RESPECT NULLS following 'GROUP_CONCAT'",
false);
}
private void checkGroupConcatFuncFails(SqlOperatorFixture t) {
final String[] values = {"'x'", "'y'"};
t.checkAggFails("^group_concat(x)^", values,
"No match found for function signature GROUP_CONCAT\\(\\)",
false);
t.checkAggFails("^group_concat(x, ',')^", values,
"No match found for function signature GROUP_CONCAT\\(, "
+ "\\)",
false);
t.checkAggFails("^group_concat(x, ',' order by x desc)^", values,
"No match found for function signature GROUP_CONCAT\\(, "
+ "\\)",
false);
}
@Test void testArrayAggFunc() {
final SqlOperatorFixture f = fixture();
checkArrayAggFunc(f.withLibrary(SqlLibrary.POSTGRESQL));
checkArrayAggFunc(f.withLibrary(SqlLibrary.BIG_QUERY));
checkArrayAggFuncFails(f.withLibrary(SqlLibrary.MYSQL));
}
private void checkArrayAggFunc(SqlOperatorFixture f) {
f.setFor(SqlLibraryOperators.ARRAY_CONCAT_AGG, VM_FENNEL, VM_JAVA);
final String[] values = {"'x'", "null", "'yz'"};
f.checkAgg("array_agg(x)", values, isSingle("[x, yz]"));
f.checkAgg("array_agg(x ignore nulls)", values, isSingle("[x, yz]"));
f.checkAgg("array_agg(x respect nulls)", values, isSingle("[x, yz]"));
final String expectedError = "Invalid number of arguments "
+ "to function 'ARRAY_AGG'. Was expecting 1 arguments";
f.checkAggFails("^array_agg(x,':')^", values, expectedError, false);
f.checkAggFails("^array_agg(x,':' order by x)^", values, expectedError,
false);
f.checkAgg("array_agg(x order by char_length(x) desc)", values,
isSingle("[yz, x]"));
}
private void checkArrayAggFuncFails(SqlOperatorFixture t) {
t.setFor(SqlLibraryOperators.ARRAY_CONCAT_AGG, VM_FENNEL, VM_JAVA);
final String[] values = {"'x'", "'y'"};
final String expectedError = "No match found for function signature "
+ "ARRAY_AGG\\(\\)";
final String expectedError2 = "No match found for function signature "
+ "ARRAY_AGG\\(, \\)";
t.checkAggFails("^array_agg(x)^", values, expectedError, false);
t.checkAggFails("^array_agg(x, ',')^", values, expectedError2, false);
t.checkAggFails("^array_agg(x, ',' order by x desc)^", values,
expectedError2, false);
}
@Test void testArrayConcatAggFunc() {
final SqlOperatorFixture f = fixture();
checkArrayConcatAggFunc(f.withLibrary(SqlLibrary.POSTGRESQL));
checkArrayConcatAggFunc(f.withLibrary(SqlLibrary.BIG_QUERY));
checkArrayConcatAggFuncFails(f.withLibrary(SqlLibrary.MYSQL));
}
void checkArrayConcatAggFunc(SqlOperatorFixture t) {
t.setFor(SqlLibraryOperators.ARRAY_CONCAT_AGG, VM_FENNEL, VM_JAVA);
t.checkFails("array_concat_agg(^*^)",
"(?s)Encountered \"\\*\" at .*", false);
t.checkAggType("array_concat_agg(ARRAY[1,2,3])",
"INTEGER NOT NULL ARRAY NOT NULL");
final String expectedError = "Cannot apply 'ARRAY_CONCAT_AGG' to arguments "
+ "of type 'ARRAY_CONCAT_AGG\\(\\)'. Supported "
+ "form\\(s\\): 'ARRAY_CONCAT_AGG\\(\\)'";
t.checkFails("^array_concat_agg(multiset[1,2])^", expectedError, false);
final String expectedError1 = "Cannot apply 'ARRAY_CONCAT_AGG' to "
+ "arguments of type 'ARRAY_CONCAT_AGG\\(\\)'\\. Supported "
+ "form\\(s\\): 'ARRAY_CONCAT_AGG\\(\\)'";
t.checkFails("^array_concat_agg(12)^", expectedError1, false);
final String[] values1 = {"ARRAY[0]", "ARRAY[1]", "ARRAY[2]", "ARRAY[3]"};
t.checkAgg("array_concat_agg(x)", values1, isSingle("[0, 1, 2, 3]"));
final String[] values2 = {"ARRAY[0,1]", "ARRAY[1, 2]"};
t.checkAgg("array_concat_agg(x)", values2, isSingle("[0, 1, 1, 2]"));
}
void checkArrayConcatAggFuncFails(SqlOperatorFixture t) {
t.setFor(SqlLibraryOperators.ARRAY_CONCAT_AGG, VM_FENNEL, VM_JAVA);
final String[] values = {"'x'", "'y'"};
final String expectedError = "No match found for function signature "
+ "ARRAY_CONCAT_AGG\\(\\)";
final String expectedError2 = "No match found for function signature "
+ "ARRAY_CONCAT_AGG\\(, \\)";
t.checkAggFails("^array_concat_agg(x)^", values, expectedError, false);
t.checkAggFails("^array_concat_agg(x, ',')^", values, expectedError2, false);
t.checkAggFails("^array_concat_agg(x, ',' order by x desc)^", values,
expectedError2, false);
}
@Test void testFusionFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.FUSION, VM_FENNEL, VM_JAVA);
f.checkFails("fusion(^*^)", "Unknown identifier '\\*'", false);
f.checkAggType("fusion(MULTISET[1,2,3])", "INTEGER NOT NULL MULTISET NOT NULL");
f.enableTypeCoercion(false).checkFails("^fusion(12)^",
"Cannot apply 'FUSION' to arguments of type .*", false);
final String[] values1 = {"MULTISET[0]", "MULTISET[1]", "MULTISET[2]", "MULTISET[3]"};
f.checkAgg("fusion(x)", values1, isSingle("[0, 1, 2, 3]"));
final String[] values2 = {"MULTISET[0,1]", "MULTISET[1, 2]"};
f.checkAgg("fusion(x)", values2, isSingle("[0, 1, 1, 2]"));
}
@Test void testIntersectionFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.INTERSECTION, VM_FENNEL, VM_JAVA);
f.checkFails("intersection(^*^)", "Unknown identifier '\\*'", false);
f.checkAggType("intersection(MULTISET[1,2,3])",
"INTEGER NOT NULL MULTISET NOT NULL");
f.enableTypeCoercion(false).checkFails("^intersection(12)^",
"Cannot apply 'INTERSECTION' to arguments of type .*", false);
final String[] values1 = {"MULTISET[0]", "MULTISET[1]", "MULTISET[2]",
"MULTISET[3]"};
f.checkAgg("intersection(x)", values1, isSingle("[]"));
final String[] values2 = {"MULTISET[0, 1]", "MULTISET[1, 2]"};
f.checkAgg("intersection(x)", values2, isSingle("[1]"));
final String[] values3 = {"MULTISET[0, 1, 1]", "MULTISET[0, 1, 2]"};
f.checkAgg("intersection(x)", values3, isSingle("[0, 1, 1]"));
}
@Test void testModeFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.MODE, VM_EXPAND);
f.checkFails("mode(^*^)", "Unknown identifier '\\*'", false);
f.enableTypeCoercion(false)
.checkFails("^mode()^",
"Invalid number of arguments to function 'MODE'. "
+ "Was expecting 1 arguments",
false);
f.enableTypeCoercion(false)
.checkFails("^mode(1,2)^",
"Invalid number of arguments to function 'MODE'. "
+ "Was expecting 1 arguments",
false);
f.enableTypeCoercion(false)
.checkFails("mode(^null^)", "Illegal use of 'NULL'", false);
f.checkType("mode('name')", "CHAR(4)");
f.checkAggType("mode(1)", "INTEGER NOT NULL");
f.checkAggType("mode(1.2)", "DECIMAL(2, 1) NOT NULL");
f.checkAggType("mode(DISTINCT 1.5)", "DECIMAL(2, 1) NOT NULL");
f.checkType("mode(cast(null as varchar(2)))", "VARCHAR(2)");
final String[] values = {"0", "CAST(null AS INTEGER)", "2", "2", "3", "3", "3" };
f.checkAgg("mode(x)", values, isSingle("3"));
final String[] values2 = {"0", null, null, null, "2", "2"};
f.checkAgg("mode(x)", values2, isSingle("2"));
final String[] values3 = {};
f.checkAgg("mode(x)", values3, isNullValue());
f.checkAgg("mode(CASE x WHEN 0 THEN NULL ELSE -1 END)",
values, isSingle(-1));
f.checkAgg("mode(DISTINCT CASE x WHEN 0 THEN NULL ELSE -1 END)",
values, isSingle(-1));
f.checkAgg("mode(DISTINCT x)", values, isSingle(0));
}
@Test void testYear() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.YEAR, VM_FENNEL, VM_JAVA);
f.checkScalar("year(date '2008-1-23')", "2008", "BIGINT NOT NULL");
f.checkNull("year(cast(null as date))");
}
@Test void testQuarter() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.QUARTER, VM_FENNEL, VM_JAVA);
f.checkScalar("quarter(date '2008-1-23')", "1", "BIGINT NOT NULL");
f.checkScalar("quarter(date '2008-2-23')", "1", "BIGINT NOT NULL");
f.checkScalar("quarter(date '2008-3-23')", "1", "BIGINT NOT NULL");
f.checkScalar("quarter(date '2008-4-23')", "2", "BIGINT NOT NULL");
f.checkScalar("quarter(date '2008-5-23')", "2", "BIGINT NOT NULL");
f.checkScalar("quarter(date '2008-6-23')", "2", "BIGINT NOT NULL");
f.checkScalar("quarter(date '2008-7-23')", "3", "BIGINT NOT NULL");
f.checkScalar("quarter(date '2008-8-23')", "3", "BIGINT NOT NULL");
f.checkScalar("quarter(date '2008-9-23')", "3", "BIGINT NOT NULL");
f.checkScalar("quarter(date '2008-10-23')", "4", "BIGINT NOT NULL");
f.checkScalar("quarter(date '2008-11-23')", "4", "BIGINT NOT NULL");
f.checkScalar("quarter(date '2008-12-23')", "4", "BIGINT NOT NULL");
f.checkNull("quarter(cast(null as date))");
}
@Test void testMonth() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.MONTH, VM_FENNEL, VM_JAVA);
f.checkScalar("month(date '2008-1-23')", "1", "BIGINT NOT NULL");
f.checkNull("month(cast(null as date))");
}
@Test void testWeek() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.WEEK, VM_FENNEL, VM_JAVA);
if (Bug.CALCITE_2539_FIXED) {
// TODO: Not implemented in operator test execution code
f.checkFails("week(date '2008-1-23')",
"cannot translate call EXTRACT.*",
true);
f.checkFails("week(cast(null as date))",
"cannot translate call EXTRACT.*",
true);
}
}
@Test void testDayOfYear() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.DAYOFYEAR, VM_FENNEL, VM_JAVA);
if (Bug.CALCITE_2539_FIXED) {
// TODO: Not implemented in operator test execution code
f.checkFails("dayofyear(date '2008-1-23')",
"cannot translate call EXTRACT.*",
true);
f.checkFails("dayofyear(cast(null as date))",
"cannot translate call EXTRACT.*",
true);
}
}
@Test void testDayOfMonth() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.DAYOFMONTH, VM_FENNEL, VM_JAVA);
f.checkScalar("dayofmonth(date '2008-1-23')", "23",
"BIGINT NOT NULL");
f.checkNull("dayofmonth(cast(null as date))");
}
@Test void testDayOfWeek() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.DAYOFWEEK, VM_FENNEL, VM_JAVA);
if (Bug.CALCITE_2539_FIXED) {
// TODO: Not implemented in operator test execution code
f.checkFails("dayofweek(date '2008-1-23')",
"cannot translate call EXTRACT.*",
true);
f.checkFails("dayofweek(cast(null as date))",
"cannot translate call EXTRACT.*",
true);
}
}
@Test void testHour() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.HOUR, VM_FENNEL, VM_JAVA);
f.checkScalar("hour(timestamp '2008-1-23 12:34:56')", "12",
"BIGINT NOT NULL");
f.checkNull("hour(cast(null as timestamp))");
}
@Test void testMinute() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.MINUTE, VM_FENNEL, VM_JAVA);
f.checkScalar("minute(timestamp '2008-1-23 12:34:56')", "34",
"BIGINT NOT NULL");
f.checkNull("minute(cast(null as timestamp))");
}
@Test void testSecond() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.SECOND, VM_FENNEL, VM_JAVA);
f.checkScalar("second(timestamp '2008-1-23 12:34:56')", "56",
"BIGINT NOT NULL");
f.checkNull("second(cast(null as timestamp))");
}
@Test void testExtractIntervalYearMonth() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.EXTRACT, VM_FENNEL, VM_JAVA);
if (TODO) {
// Not supported, fails in type validation because the extract
// unit is not YearMonth interval type.
f.checkScalar("extract(epoch from interval '4-2' year to month)",
// number of seconds elapsed since timestamp
// '1970-01-01 00:00:00' + input interval
"131328000", "BIGINT NOT NULL");
f.checkScalar("extract(second from interval '4-2' year to month)",
"0", "BIGINT NOT NULL");
f.checkScalar("extract(millisecond from "
+ "interval '4-2' year to month)", "0", "BIGINT NOT NULL");
f.checkScalar("extract(microsecond "
+ "from interval '4-2' year to month)", "0", "BIGINT NOT NULL");
f.checkScalar("extract(nanosecond from "
+ "interval '4-2' year to month)", "0", "BIGINT NOT NULL");
f.checkScalar("extract(minute from interval '4-2' year to month)",
"0", "BIGINT NOT NULL");
f.checkScalar("extract(hour from interval '4-2' year to month)",
"0", "BIGINT NOT NULL");
f.checkScalar("extract(day from interval '4-2' year to month)",
"0", "BIGINT NOT NULL");
}
// Postgres doesn't support DOW, ISODOW, DOY and WEEK on INTERVAL YEAR MONTH type.
// SQL standard doesn't have extract units for DOW, ISODOW, DOY and WEEK.
f.checkFails("^extract(doy from interval '4-2' year to month)^",
INVALID_EXTRACT_UNIT_VALIDATION_ERROR, false);
f.checkFails("^extract(dow from interval '4-2' year to month)^",
INVALID_EXTRACT_UNIT_VALIDATION_ERROR, false);
f.checkFails("^extract(week from interval '4-2' year to month)^",
INVALID_EXTRACT_UNIT_VALIDATION_ERROR, false);
f.checkFails("^extract(isodow from interval '4-2' year to month)^",
INVALID_EXTRACT_UNIT_VALIDATION_ERROR, false);
f.checkScalar("extract(month from interval '4-2' year to month)",
"2", "BIGINT NOT NULL");
f.checkScalar("extract(quarter from interval '4-2' year to month)",
"1", "BIGINT NOT NULL");
f.checkScalar("extract(year from interval '4-2' year to month)",
"4", "BIGINT NOT NULL");
f.checkScalar("extract(decade from "
+ "interval '426-3' year(3) to month)", "42", "BIGINT NOT NULL");
f.checkScalar("extract(century from "
+ "interval '426-3' year(3) to month)", "4", "BIGINT NOT NULL");
f.checkScalar("extract(millennium from "
+ "interval '2005-3' year(4) to month)", "2", "BIGINT NOT NULL");
}
@Test void testExtractIntervalDayTime() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.EXTRACT, VM_FENNEL, VM_JAVA);
if (TODO) {
// Not implemented in operator test
f.checkScalar("extract(epoch from "
+ "interval '2 3:4:5.678' day to second)",
// number of seconds elapsed since timestamp
// '1970-01-01 00:00:00' + input interval
"183845.678",
"BIGINT NOT NULL");
}
f.checkScalar("extract(millisecond from "
+ "interval '2 3:4:5.678' day to second)",
"5678",
"BIGINT NOT NULL");
f.checkScalar("extract(microsecond from "
+ "interval '2 3:4:5.678' day to second)",
"5678000",
"BIGINT NOT NULL");
f.checkScalar("extract(nanosecond from "
+ "interval '2 3:4:5.678' day to second)",
"5678000000",
"BIGINT NOT NULL");
f.checkScalar(
"extract(second from interval '2 3:4:5.678' day to second)",
"5",
"BIGINT NOT NULL");
f.checkScalar(
"extract(minute from interval '2 3:4:5.678' day to second)",
"4",
"BIGINT NOT NULL");
f.checkScalar(
"extract(hour from interval '2 3:4:5.678' day to second)",
"3",
"BIGINT NOT NULL");
f.checkScalar(
"extract(day from interval '2 3:4:5.678' day to second)",
"2",
"BIGINT NOT NULL");
// Postgres doesn't support DOW, ISODOW, DOY and WEEK on INTERVAL DAY TIME type.
// SQL standard doesn't have extract units for DOW, ISODOW, DOY and WEEK.
if (Bug.CALCITE_2539_FIXED) {
f.checkFails("extract(doy from interval '2 3:4:5.678' day to second)",
INVALID_EXTRACT_UNIT_CONVERTLET_ERROR, true);
f.checkFails("extract(dow from interval '2 3:4:5.678' day to second)",
INVALID_EXTRACT_UNIT_CONVERTLET_ERROR, true);
f.checkFails("extract(week from interval '2 3:4:5.678' day to second)",
INVALID_EXTRACT_UNIT_CONVERTLET_ERROR, true);
f.checkFails("extract(isodow from interval '2 3:4:5.678' day to second)",
INVALID_EXTRACT_UNIT_CONVERTLET_ERROR, true);
}
f.checkFails("^extract(month from interval '2 3:4:5.678' day to second)^",
"(?s)Cannot apply 'EXTRACT' to arguments of type 'EXTRACT\\( FROM \\)'\\. Supported "
+ "form\\(s\\):.*",
false);
f.checkFails("^extract(quarter from interval '2 3:4:5.678' day to second)^",
"(?s)Cannot apply 'EXTRACT' to arguments of type 'EXTRACT\\( FROM \\)'\\. Supported "
+ "form\\(s\\):.*",
false);
f.checkFails("^extract(year from interval '2 3:4:5.678' day to second)^",
"(?s)Cannot apply 'EXTRACT' to arguments of type 'EXTRACT\\( FROM \\)'\\. Supported "
+ "form\\(s\\):.*",
false);
f.checkFails("^extract(isoyear from interval '2 3:4:5.678' day to second)^",
"(?s)Cannot apply 'EXTRACT' to arguments of type 'EXTRACT\\( FROM \\)'\\. Supported "
+ "form\\(s\\):.*",
false);
f.checkFails("^extract(century from interval '2 3:4:5.678' day to second)^",
"(?s)Cannot apply 'EXTRACT' to arguments of type 'EXTRACT\\( FROM \\)'\\. Supported "
+ "form\\(s\\):.*",
false);
}
@Test void testExtractDate() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.EXTRACT, VM_FENNEL, VM_JAVA);
f.checkScalar("extract(epoch from date '2008-2-23')",
"1203724800", // number of seconds elapsed since timestamp
// '1970-01-01 00:00:00' for given date
"BIGINT NOT NULL");
f.checkScalar("extract(second from date '2008-2-23')",
"0", "BIGINT NOT NULL");
f.checkScalar("extract(millisecond from date '2008-2-23')",
"0", "BIGINT NOT NULL");
f.checkScalar("extract(microsecond from date '2008-2-23')",
"0", "BIGINT NOT NULL");
f.checkScalar("extract(nanosecond from date '2008-2-23')",
"0", "BIGINT NOT NULL");
f.checkScalar("extract(minute from date '9999-2-23')",
"0", "BIGINT NOT NULL");
f.checkScalar("extract(minute from date '0001-1-1')",
"0", "BIGINT NOT NULL");
f.checkScalar("extract(minute from date '2008-2-23')",
"0", "BIGINT NOT NULL");
f.checkScalar("extract(hour from date '2008-2-23')",
"0", "BIGINT NOT NULL");
f.checkScalar("extract(day from date '2008-2-23')",
"23", "BIGINT NOT NULL");
f.checkScalar("extract(month from date '2008-2-23')",
"2", "BIGINT NOT NULL");
f.checkScalar("extract(quarter from date '2008-4-23')",
"2", "BIGINT NOT NULL");
f.checkScalar("extract(year from date '2008-2-23')",
"2008", "BIGINT NOT NULL");
f.checkScalar("extract(isoyear from date '2008-2-23')",
"2008", "BIGINT NOT NULL");
f.checkScalar("extract(doy from date '2008-2-23')",
"54", "BIGINT NOT NULL");
f.checkScalar("extract(dow from date '2008-2-23')",
"7", "BIGINT NOT NULL");
f.checkScalar("extract(dow from date '2008-2-24')",
"1", "BIGINT NOT NULL");
f.checkScalar("extract(isodow from date '2008-2-23')",
"6", "BIGINT NOT NULL");
f.checkScalar("extract(isodow from date '2008-2-24')",
"7", "BIGINT NOT NULL");
f.checkScalar("extract(week from date '2008-2-23')",
"8", "BIGINT NOT NULL");
f.checkScalar("extract(week from timestamp '2008-2-23 01:23:45')",
"8", "BIGINT NOT NULL");
f.checkScalar("extract(week from cast(null as date))",
isNullValue(), "BIGINT");
f.checkScalar("extract(decade from date '2008-2-23')",
"200", "BIGINT NOT NULL");
f.checkScalar("extract(century from date '2008-2-23')",
"21", "BIGINT NOT NULL");
f.checkScalar("extract(century from date '2001-01-01')",
"21", "BIGINT NOT NULL");
f.checkScalar("extract(century from date '2000-12-31')",
"20", "BIGINT NOT NULL");
f.checkScalar("extract(century from date '1852-06-07')",
"19", "BIGINT NOT NULL");
f.checkScalar("extract(century from date '0001-02-01')",
"1", "BIGINT NOT NULL");
f.checkScalar("extract(millennium from date '2000-2-23')",
"2", "BIGINT NOT NULL");
f.checkScalar("extract(millennium from date '1969-2-23')",
"2", "BIGINT NOT NULL");
f.checkScalar("extract(millennium from date '2000-12-31')",
"2", "BIGINT NOT NULL");
f.checkScalar("extract(millennium from date '2001-01-01')",
"3", "BIGINT NOT NULL");
}
@Test void testExtractTimestamp() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.EXTRACT, VM_FENNEL, VM_JAVA);
f.checkScalar("extract(epoch from timestamp '2008-2-23 12:34:56')",
"1203770096", // number of seconds elapsed since timestamp
// '1970-01-01 00:00:00' for given date
"BIGINT NOT NULL");
f.checkScalar("extract(second from timestamp '2008-2-23 12:34:56')",
"56", "BIGINT NOT NULL");
f.checkScalar("extract(millisecond from timestamp '2008-2-23 12:34:56')",
"56000", "BIGINT NOT NULL");
f.checkScalar("extract(microsecond from timestamp '2008-2-23 12:34:56')",
"56000000", "BIGINT NOT NULL");
f.checkScalar("extract(nanosecond from timestamp '2008-2-23 12:34:56')",
"56000000000", "BIGINT NOT NULL");
f.checkScalar("extract(minute from timestamp '2008-2-23 12:34:56')",
"34", "BIGINT NOT NULL");
f.checkScalar("extract(hour from timestamp '2008-2-23 12:34:56')",
"12", "BIGINT NOT NULL");
f.checkScalar("extract(day from timestamp '2008-2-23 12:34:56')",
"23", "BIGINT NOT NULL");
f.checkScalar("extract(month from timestamp '2008-2-23 12:34:56')",
"2", "BIGINT NOT NULL");
f.checkScalar("extract(quarter from timestamp '2008-7-23 12:34:56')",
"3", "BIGINT NOT NULL");
f.checkScalar("extract(year from timestamp '2008-2-23 12:34:56')",
"2008", "BIGINT NOT NULL");
f.checkScalar("extract(isoyear from timestamp '2008-2-23 12:34:56')",
"2008", "BIGINT NOT NULL");
if (Bug.CALCITE_2539_FIXED) {
// TODO: Not implemented in operator test execution code
f.checkFails("extract(doy from timestamp '2008-2-23 12:34:56')",
"cannot translate call EXTRACT.*", true);
// TODO: Not implemented in operator test execution code
f.checkFails("extract(dow from timestamp '2008-2-23 12:34:56')",
"cannot translate call EXTRACT.*", true);
// TODO: Not implemented in operator test execution code
f.checkFails("extract(week from timestamp '2008-2-23 12:34:56')",
"cannot translate call EXTRACT.*", true);
}
f.checkScalar("extract(decade from timestamp '2008-2-23 12:34:56')",
"200", "BIGINT NOT NULL");
f.checkScalar("extract(century from timestamp '2008-2-23 12:34:56')",
"21", "BIGINT NOT NULL");
f.checkScalar("extract(century from timestamp '2001-01-01 12:34:56')",
"21", "BIGINT NOT NULL");
f.checkScalar("extract(century from timestamp '2000-12-31 12:34:56')",
"20", "BIGINT NOT NULL");
f.checkScalar("extract(millennium from timestamp '2008-2-23 12:34:56')",
"3", "BIGINT NOT NULL");
f.checkScalar("extract(millennium from timestamp '2000-2-23 12:34:56')",
"2", "BIGINT NOT NULL");
}
@Test void testExtractFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.EXTRACT, VM_FENNEL, VM_JAVA);
f.checkScalar("extract(day from interval '2 3:4:5.678' day to second)",
"2", "BIGINT NOT NULL");
f.checkScalar("extract(day from interval '23456 3:4:5.678' day(5) to second)",
"23456", "BIGINT NOT NULL");
f.checkScalar("extract(hour from interval '2 3:4:5.678' day to second)",
"3", "BIGINT NOT NULL");
f.checkScalar("extract(minute from interval '2 3:4:5.678' day to second)",
"4", "BIGINT NOT NULL");
// TODO: Seconds should include precision
f.checkScalar("extract(second from interval '2 3:4:5.678' day to second)",
"5", "BIGINT NOT NULL");
f.checkScalar("extract(millisecond from"
+ " interval '2 3:4:5.678' day to second)",
"5678", "BIGINT NOT NULL");
f.checkScalar("extract(microsecond from"
+ " interval '2 3:4:5.678' day to second)",
"5678000", "BIGINT NOT NULL");
f.checkScalar("extract(nanosecond from"
+ " interval '2 3:4:5.678' day to second)",
"5678000000", "BIGINT NOT NULL");
f.checkNull("extract(month from cast(null as interval year))");
}
@Test void testExtractFuncFromDateTime() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.EXTRACT, VM_FENNEL, VM_JAVA);
f.checkScalar("extract(year from date '2008-2-23')",
"2008", "BIGINT NOT NULL");
f.checkScalar("extract(isoyear from date '2008-2-23')",
"2008", "BIGINT NOT NULL");
f.checkScalar("extract(month from date '2008-2-23')",
"2", "BIGINT NOT NULL");
f.checkScalar("extract(month from timestamp '2008-2-23 12:34:56')",
"2", "BIGINT NOT NULL");
f.checkScalar("extract(minute from timestamp '2008-2-23 12:34:56')",
"34", "BIGINT NOT NULL");
f.checkScalar("extract(minute from time '12:23:34')",
"23", "BIGINT NOT NULL");
f.checkNull("extract(month from cast(null as timestamp))");
f.checkNull("extract(month from cast(null as date))");
f.checkNull("extract(second from cast(null as time))");
f.checkNull("extract(millisecond from cast(null as time))");
f.checkNull("extract(microsecond from cast(null as time))");
f.checkNull("extract(nanosecond from cast(null as time))");
}
@Test void testExtractWithDatesBeforeUnixEpoch() {
final SqlOperatorFixture f = fixture();
f.checkScalar("extract(millisecond from"
+ " TIMESTAMP '1969-12-31 21:13:17.357')",
"17357", "BIGINT NOT NULL");
f.checkScalar("extract(year from TIMESTAMP '1970-01-01 00:00:00')",
"1970", "BIGINT NOT NULL");
f.checkScalar("extract(year from TIMESTAMP '1969-12-31 10:13:17')",
"1969", "BIGINT NOT NULL");
f.checkScalar("extract(quarter from TIMESTAMP '1969-12-31 08:13:17')",
"4", "BIGINT NOT NULL");
f.checkScalar("extract(quarter from TIMESTAMP '1969-5-31 21:13:17')",
"2", "BIGINT NOT NULL");
f.checkScalar("extract(month from TIMESTAMP '1969-12-31 00:13:17')",
"12", "BIGINT NOT NULL");
f.checkScalar("extract(day from TIMESTAMP '1969-12-31 12:13:17')",
"31", "BIGINT NOT NULL");
f.checkScalar("extract(week from TIMESTAMP '1969-2-23 01:23:45')",
"8", "BIGINT NOT NULL");
f.checkScalar("extract(doy from TIMESTAMP '1969-12-31 21:13:17.357')",
"365", "BIGINT NOT NULL");
f.checkScalar("extract(dow from TIMESTAMP '1969-12-31 01:13:17.357')",
"4", "BIGINT NOT NULL");
f.checkScalar("extract(decade from TIMESTAMP '1969-12-31 21:13:17.357')",
"196", "BIGINT NOT NULL");
f.checkScalar("extract(century from TIMESTAMP '1969-12-31 21:13:17.357')",
"20", "BIGINT NOT NULL");
f.checkScalar("extract(hour from TIMESTAMP '1969-12-31 21:13:17.357')",
"21", "BIGINT NOT NULL");
f.checkScalar("extract(minute from TIMESTAMP '1969-12-31 21:13:17.357')",
"13", "BIGINT NOT NULL");
f.checkScalar("extract(second from TIMESTAMP '1969-12-31 21:13:17.357')",
"17", "BIGINT NOT NULL");
f.checkScalar("extract(millisecond from"
+ " TIMESTAMP '1969-12-31 21:13:17.357')",
"17357", "BIGINT NOT NULL");
f.checkScalar("extract(microsecond from"
+ " TIMESTAMP '1969-12-31 21:13:17.357')",
"17357000", "BIGINT NOT NULL");
}
@Test void testArrayValueConstructor() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.ARRAY_VALUE_CONSTRUCTOR, VmName.EXPAND);
f.checkScalar("Array['foo', 'bar']",
"[foo, bar]", "CHAR(3) NOT NULL ARRAY NOT NULL");
// empty array is illegal per SQL spec. presumably because one can't
// infer type
f.checkFails("^Array[]^", "Require at least 1 argument", false);
}
@Test void testItemOp() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.ITEM, VmName.EXPAND);
f.checkScalar("ARRAY ['foo', 'bar'][1]", "foo", "CHAR(3)");
f.checkScalar("ARRAY ['foo', 'bar'][0]", isNullValue(), "CHAR(3)");
f.checkScalar("ARRAY ['foo', 'bar'][2]", "bar", "CHAR(3)");
f.checkScalar("ARRAY ['foo', 'bar'][3]", isNullValue(), "CHAR(3)");
f.checkNull("ARRAY ['foo', 'bar'][1 + CAST(NULL AS INTEGER)]");
f.checkFails("^ARRAY ['foo', 'bar']['baz']^",
"Cannot apply 'ITEM' to arguments of type 'ITEM\\(, "
+ "\\)'\\. Supported form\\(s\\): \\[\\]\n"
+ "