blob: 9db5f8ed076b2a281b24ab63466f0ee8f1a30efe [file] [log] [blame]
/*
* 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.rex;
import org.apache.calcite.avatica.util.ByteString;
import org.apache.calcite.avatica.util.DateTimeUtils;
import org.apache.calcite.avatica.util.Spaces;
import org.apache.calcite.avatica.util.TimeUnit;
import org.apache.calcite.rel.core.CorrelationId;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.rel.type.RelDataTypeField;
import org.apache.calcite.rel.type.RelDataTypeFieldImpl;
import org.apache.calcite.rel.type.RelDataTypeImpl;
import org.apache.calcite.rel.type.RelDataTypeSystem;
import org.apache.calcite.rel.type.RelDataTypeSystemImpl;
import org.apache.calcite.sql.SqlCollation;
import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.sql.fun.SqlLibraryOperators;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
import org.apache.calcite.sql.type.BasicSqlType;
import org.apache.calcite.sql.type.SqlTypeFactoryImpl;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.test.RexImplicationCheckerFixtures;
import org.apache.calcite.util.DateString;
import org.apache.calcite.util.Litmus;
import org.apache.calcite.util.NlsString;
import org.apache.calcite.util.TimeString;
import org.apache.calcite.util.TimeWithTimeZoneString;
import org.apache.calcite.util.TimestampString;
import org.apache.calcite.util.TimestampWithTimeZoneString;
import org.apache.calcite.util.Util;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Calendar;
import java.util.TimeZone;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Stream;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasToString;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import static org.junit.jupiter.params.provider.Arguments.of;
/**
* Test for {@link RexBuilder}.
*/
class RexBuilderTest {
private static final int PRECISION = 256;
/**
* MySqlTypeFactoryImpl provides a specific implementation of
* {@link SqlTypeFactoryImpl} which sets precision to 256 for VARCHAR.
*/
private static class MySqlTypeFactoryImpl extends SqlTypeFactoryImpl {
MySqlTypeFactoryImpl(RelDataTypeSystem typeSystem) {
super(typeSystem);
}
@Override public RelDataType createTypeWithNullability(
final RelDataType type,
final boolean nullable) {
if (type.getSqlTypeName() == SqlTypeName.VARCHAR) {
return new BasicSqlType(this.typeSystem, type.getSqlTypeName(),
PRECISION);
}
return super.createTypeWithNullability(type, nullable);
}
}
/**
* Test RexBuilder.ensureType()
*/
@Test void testEnsureTypeWithAny() {
final RelDataTypeFactory typeFactory = new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
RexBuilder builder = new RexBuilder(typeFactory);
RexNode node =
new RexLiteral(Boolean.TRUE,
typeFactory.createSqlType(SqlTypeName.BOOLEAN), SqlTypeName.BOOLEAN);
RexNode ensuredNode =
builder.ensureType(typeFactory.createSqlType(SqlTypeName.ANY), node,
true);
assertEquals(node, ensuredNode);
}
/**
* Test RexBuilder.ensureType()
*/
@Test void testEnsureTypeWithItself() {
final RelDataTypeFactory typeFactory = new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
RexBuilder builder = new RexBuilder(typeFactory);
RexNode node =
new RexLiteral(Boolean.TRUE,
typeFactory.createSqlType(SqlTypeName.BOOLEAN), SqlTypeName.BOOLEAN);
RexNode ensuredNode =
builder.ensureType(typeFactory.createSqlType(SqlTypeName.BOOLEAN), node,
true);
assertEquals(node, ensuredNode);
}
/**
* Test RexBuilder.ensureType()
*/
@Test void testEnsureTypeWithDifference() {
final RelDataTypeFactory typeFactory = new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
RexBuilder builder = new RexBuilder(typeFactory);
RexNode node =
new RexLiteral(Boolean.TRUE,
typeFactory.createSqlType(SqlTypeName.BOOLEAN), SqlTypeName.BOOLEAN);
RexNode ensuredNode =
builder.ensureType(typeFactory.createSqlType(SqlTypeName.INTEGER), node,
true);
assertNotEquals(node, ensuredNode);
assertEquals(ensuredNode.getType(), typeFactory.createSqlType(SqlTypeName.INTEGER));
}
private static final long MOON = -14159025000L;
private static final int MOON_DAY = -164;
private static final int MOON_TIME = 10575000;
/** Tests {@link RexBuilder#makeTimestampLiteral(TimestampString, int)}. */
@Test void testTimestampLiteral() {
final RelDataTypeFactory typeFactory =
new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
final RelDataType timestampType =
typeFactory.createSqlType(SqlTypeName.TIMESTAMP);
final RelDataType timestampType3 =
typeFactory.createSqlType(SqlTypeName.TIMESTAMP, 3);
final RelDataType timestampType9 =
typeFactory.createSqlType(SqlTypeName.TIMESTAMP, 9);
final RelDataType timestampType18 =
typeFactory.createSqlType(SqlTypeName.TIMESTAMP, 18);
final RexBuilder builder = new RexBuilder(typeFactory);
// Old way: provide a Calendar
final Calendar calendar = Util.calendar();
calendar.set(1969, Calendar.JULY, 21, 2, 56, 15); // one small step
calendar.set(Calendar.MILLISECOND, 0);
checkTimestamp(builder.makeLiteral(calendar, timestampType));
// Old way #2: Provide a Long
checkTimestamp(builder.makeLiteral(MOON, timestampType));
// The new way
final TimestampString ts = new TimestampString(1969, 7, 21, 2, 56, 15);
checkTimestamp(builder.makeLiteral(ts, timestampType));
// Now with milliseconds
final TimestampString ts2 = ts.withMillis(56);
assertThat(ts2, hasToString("1969-07-21 02:56:15.056"));
final RexLiteral literal2 = builder.makeLiteral(ts2, timestampType3);
assertThat(literal2.getValueAs(TimestampString.class),
hasToString("1969-07-21 02:56:15.056"));
// Now with nanoseconds
final TimestampString ts3 = ts.withNanos(56);
final RexLiteral literal3 = builder.makeLiteral(ts3, timestampType9);
assertThat(literal3.getValueAs(TimestampString.class),
hasToString("1969-07-21 02:56:15"));
final TimestampString ts3b = ts.withNanos(2345678);
final RexLiteral literal3b = builder.makeLiteral(ts3b, timestampType9);
assertThat(literal3b.getValueAs(TimestampString.class),
hasToString("1969-07-21 02:56:15.002"));
// Now with a very long fraction
final TimestampString ts4 = ts.withFraction("102030405060708090102");
final RexLiteral literal4 = builder.makeLiteral(ts4, timestampType18);
assertThat(literal4.getValueAs(TimestampString.class),
hasToString("1969-07-21 02:56:15.102"));
}
@Test void testTimestampString() {
final TimestampString ts = new TimestampString(1969, 7, 21, 2, 56, 15);
assertThat(ts, hasToString("1969-07-21 02:56:15"));
assertThat(ts.round(1), is(ts));
// Now with milliseconds
final TimestampString ts2 = ts.withMillis(56);
assertThat(ts2, hasToString("1969-07-21 02:56:15.056"));
// toString
assertThat(ts2.round(1), hasToString("1969-07-21 02:56:15"));
assertThat(ts2.round(2), hasToString("1969-07-21 02:56:15.05"));
assertThat(ts2.round(3), hasToString("1969-07-21 02:56:15.056"));
assertThat(ts2.round(4), hasToString("1969-07-21 02:56:15.056"));
assertThat(ts2.toString(6), is("1969-07-21 02:56:15.056000"));
assertThat(ts2.toString(1), is("1969-07-21 02:56:15.0"));
assertThat(ts2.toString(0), is("1969-07-21 02:56:15"));
assertThat(ts2.round(0), hasToString("1969-07-21 02:56:15"));
assertThat(ts2.round(0).toString(0), is("1969-07-21 02:56:15"));
assertThat(ts2.round(0).toString(1), is("1969-07-21 02:56:15.0"));
assertThat(ts2.round(0).toString(2), is("1969-07-21 02:56:15.00"));
// Now with milliseconds ending in zero (3 equivalent strings).
final TimestampString ts3 = ts.withMillis(10);
assertThat(ts3, hasToString("1969-07-21 02:56:15.01"));
final TimestampString ts3b = new TimestampString("1969-07-21 02:56:15.01");
assertThat(ts3b, hasToString("1969-07-21 02:56:15.01"));
assertThat(ts3b, is(ts3));
final TimestampString ts3c = new TimestampString("1969-07-21 02:56:15.010");
assertThat(ts3c, hasToString("1969-07-21 02:56:15.01"));
assertThat(ts3c, is(ts3));
// Now with nanoseconds
final TimestampString ts4 = ts.withNanos(56);
assertThat(ts4, hasToString("1969-07-21 02:56:15.000000056"));
// Check rounding; uses RoundingMode.DOWN
final TimestampString ts5 = ts.withNanos(2345670);
assertThat(ts5, hasToString("1969-07-21 02:56:15.00234567"));
assertThat(ts5.round(0), hasToString("1969-07-21 02:56:15"));
assertThat(ts5.round(1), hasToString("1969-07-21 02:56:15"));
assertThat(ts5.round(2), hasToString("1969-07-21 02:56:15"));
assertThat(ts5.round(3), hasToString("1969-07-21 02:56:15.002"));
assertThat(ts5.round(4), hasToString("1969-07-21 02:56:15.0023"));
assertThat(ts5.round(5), hasToString("1969-07-21 02:56:15.00234"));
assertThat(ts5.round(6), hasToString("1969-07-21 02:56:15.002345"));
assertThat(ts5.round(600), hasToString("1969-07-21 02:56:15.00234567"));
// Now with a very long fraction
final TimestampString ts6 = ts.withFraction("102030405060708090102");
assertThat(ts6, hasToString("1969-07-21 02:56:15.102030405060708090102"));
// From milliseconds
final TimestampString ts7 =
TimestampString.fromMillisSinceEpoch(1456513560123L);
assertThat(ts7, hasToString("2016-02-26 19:06:00.123"));
final TimestampString ts8 =
TimestampString.fromMillisSinceEpoch(1456513560120L);
assertThat(ts8, hasToString("2016-02-26 19:06:00.12"));
final TimestampString ts9 = ts8.withFraction("9876543210");
assertThat(ts9, hasToString("2016-02-26 19:06:00.987654321"));
// TimestampString.toCalendar
final Calendar c = ts9.toCalendar();
assertThat(c.get(Calendar.ERA), is(1)); // CE
assertThat(c.get(Calendar.YEAR), is(2016));
assertThat(c.get(Calendar.MONTH), is(1)); // February
assertThat(c.get(Calendar.DATE), is(26));
assertThat(c.get(Calendar.HOUR_OF_DAY), is(19));
assertThat(c.get(Calendar.MINUTE), is(6));
assertThat(c.get(Calendar.SECOND), is(0));
assertThat(c.get(Calendar.MILLISECOND), is(987)); // RoundingMode.DOWN
assertThat(ts9.getMillisSinceEpoch(), is(c.getTimeInMillis()));
// TimestampString.fromCalendarFields
c.set(Calendar.YEAR, 1969);
final TimestampString ts10 = TimestampString.fromCalendarFields(c);
assertThat(ts10, hasToString("1969-02-26 19:06:00.987"));
assertThat(ts10.getMillisSinceEpoch(), is(c.getTimeInMillis()));
}
@Test void testTimeString() {
final TimeString t = new TimeString(2, 56, 15);
assertThat(t, hasToString("02:56:15"));
assertThat(t.round(1), is(t));
// Now with milliseconds
final TimeString t2 = t.withMillis(56);
assertThat(t2, hasToString("02:56:15.056"));
// toString
assertThat(t2.round(1), hasToString("02:56:15"));
assertThat(t2.round(2), hasToString("02:56:15.05"));
assertThat(t2.round(3), hasToString("02:56:15.056"));
assertThat(t2.round(4), hasToString("02:56:15.056"));
assertThat(t2.toString(6), is("02:56:15.056000"));
assertThat(t2.toString(1), is("02:56:15.0"));
assertThat(t2.toString(0), is("02:56:15"));
assertThat(t2.round(0), hasToString("02:56:15"));
assertThat(t2.round(0).toString(0), is("02:56:15"));
assertThat(t2.round(0).toString(1), is("02:56:15.0"));
assertThat(t2.round(0).toString(2), is("02:56:15.00"));
// Now with milliseconds ending in zero (3 equivalent strings).
final TimeString t3 = t.withMillis(10);
assertThat(t3, hasToString("02:56:15.01"));
final TimeString t3b = new TimeString("02:56:15.01");
assertThat(t3b, hasToString("02:56:15.01"));
assertThat(t3b, is(t3));
final TimeString t3c = new TimeString("02:56:15.010");
assertThat(t3c, hasToString("02:56:15.01"));
assertThat(t3c, is(t3));
// Now with nanoseconds
final TimeString t4 = t.withNanos(56);
assertThat(t4, hasToString("02:56:15.000000056"));
// Check rounding; uses RoundingMode.DOWN
final TimeString t5 = t.withNanos(2345670);
assertThat(t5, hasToString("02:56:15.00234567"));
assertThat(t5.round(0), hasToString("02:56:15"));
assertThat(t5.round(1), hasToString("02:56:15"));
assertThat(t5.round(2), hasToString("02:56:15"));
assertThat(t5.round(3), hasToString("02:56:15.002"));
assertThat(t5.round(4), hasToString("02:56:15.0023"));
assertThat(t5.round(5), hasToString("02:56:15.00234"));
assertThat(t5.round(6), hasToString("02:56:15.002345"));
assertThat(t5.round(600), hasToString("02:56:15.00234567"));
// Now with a very long fraction
final TimeString t6 = t.withFraction("102030405060708090102");
assertThat(t6, hasToString("02:56:15.102030405060708090102"));
}
private void checkTimestamp(RexLiteral literal) {
assertThat(literal, hasToString("1969-07-21 02:56:15"));
assertThat(literal.getValue() instanceof Calendar, is(true));
assertThat(literal.getValue2() instanceof Long, is(true));
assertThat(literal.getValue3() instanceof Long, is(true));
assertThat((Long) literal.getValue2(), is(MOON));
assertThat(literal.getValueAs(Calendar.class), notNullValue());
assertThat(literal.getValueAs(TimestampString.class), notNullValue());
}
/** Tests
* {@link RexBuilder#makeTimestampWithLocalTimeZoneLiteral(TimestampString, int)}. */
@Test void testTimestampWithLocalTimeZoneLiteral() {
final RelDataTypeFactory typeFactory =
new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
final RelDataType timestampType =
typeFactory.createSqlType(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE);
final RelDataType timestampType3 =
typeFactory.createSqlType(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE, 3);
final RelDataType timestampType9 =
typeFactory.createSqlType(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE, 9);
final RelDataType timestampType18 =
typeFactory.createSqlType(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE, 18);
final RexBuilder builder = new RexBuilder(typeFactory);
// The new way
final TimestampWithTimeZoneString ts =
new TimestampWithTimeZoneString(1969, 7, 21, 2, 56, 15,
TimeZone.getTimeZone("PST").getID());
checkTimestampWithLocalTimeZone(
builder.makeLiteral(ts.getLocalTimestampString(), timestampType));
// Now with milliseconds
final TimestampWithTimeZoneString ts2 = ts.withMillis(56);
assertThat(ts2, hasToString("1969-07-21 02:56:15.056 PST"));
final RexLiteral literal2 =
builder.makeLiteral(ts2.getLocalTimestampString(), timestampType3);
assertThat(literal2.getValue(), hasToString("1969-07-21 02:56:15.056"));
// Now with nanoseconds
final TimestampWithTimeZoneString ts3 = ts.withNanos(56);
final RexLiteral literal3 =
builder.makeLiteral(ts3.getLocalTimestampString(), timestampType9);
assertThat(literal3.getValueAs(TimestampString.class),
hasToString("1969-07-21 02:56:15"));
final TimestampWithTimeZoneString ts3b = ts.withNanos(2345678);
final RexLiteral literal3b =
builder.makeLiteral(ts3b.getLocalTimestampString(), timestampType9);
assertThat(literal3b.getValueAs(TimestampString.class),
hasToString("1969-07-21 02:56:15.002"));
// Now with a very long fraction
final TimestampWithTimeZoneString ts4 = ts.withFraction("102030405060708090102");
final RexLiteral literal4 =
builder.makeLiteral(ts4.getLocalTimestampString(), timestampType18);
assertThat(literal4.getValueAs(TimestampString.class),
hasToString("1969-07-21 02:56:15.102"));
// toString
assertThat(ts2.round(1), hasToString("1969-07-21 02:56:15 PST"));
assertThat(ts2.round(2), hasToString("1969-07-21 02:56:15.05 PST"));
assertThat(ts2.round(3), hasToString("1969-07-21 02:56:15.056 PST"));
assertThat(ts2.round(4), hasToString("1969-07-21 02:56:15.056 PST"));
assertThat(ts2.toString(6), is("1969-07-21 02:56:15.056000 PST"));
assertThat(ts2.toString(1), is("1969-07-21 02:56:15.0 PST"));
assertThat(ts2.toString(0), is("1969-07-21 02:56:15 PST"));
assertThat(ts2.round(0), hasToString("1969-07-21 02:56:15 PST"));
assertThat(ts2.round(0).toString(0), is("1969-07-21 02:56:15 PST"));
assertThat(ts2.round(0).toString(1), is("1969-07-21 02:56:15.0 PST"));
assertThat(ts2.round(0).toString(2), is("1969-07-21 02:56:15.00 PST"));
}
private void checkTimestampWithLocalTimeZone(RexLiteral literal) {
assertThat(literal,
hasToString("1969-07-21 02:56:15:TIMESTAMP_WITH_LOCAL_TIME_ZONE(0)"));
assertThat(literal.getValue() instanceof TimestampString, is(true));
assertThat(literal.getValue2() instanceof Long, is(true));
assertThat(literal.getValue3() instanceof Long, is(true));
}
/** Tests
* {@link RexBuilder#makeTimestampTzLiteral(TimestampWithTimeZoneString, int)}. */
@Test void testTimestampTzLiterals() {
final RelDataTypeFactory typeFactory =
new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
final RelDataType timestampType =
typeFactory.createSqlType(SqlTypeName.TIMESTAMP_TZ);
final RelDataType timestampType3 =
typeFactory.createSqlType(SqlTypeName.TIMESTAMP_TZ, 3);
final RelDataType timestampType9 =
typeFactory.createSqlType(SqlTypeName.TIMESTAMP_TZ, 9);
final RelDataType timestampType18 =
typeFactory.createSqlType(SqlTypeName.TIMESTAMP_TZ, 18);
final RexBuilder builder = new RexBuilder(typeFactory);
// The new way
final TimestampWithTimeZoneString ts =
new TimestampWithTimeZoneString(1969, 7, 21, 2, 56, 15,
TimeZone.getTimeZone("PST").getID());
checkTimestampTz(builder.makeLiteral(ts, timestampType));
// Now with milliseconds
final TimestampWithTimeZoneString ts2 = ts.withMillis(56);
assertThat(ts2, hasToString("1969-07-21 02:56:15.056 PST"));
final RexLiteral literal2 =
builder.makeLiteral(ts2, timestampType3);
assertThat(literal2.getValue(), hasToString("1969-07-21 02:56:15.056 PST"));
// Now with nanoseconds
final TimestampWithTimeZoneString ts3 = ts.withNanos(56);
final RexLiteral literal3 =
builder.makeLiteral(ts3, timestampType9);
assertThat(literal3.getValueAs(TimestampWithTimeZoneString.class),
hasToString("1969-07-21 02:56:15 PST"));
final TimestampWithTimeZoneString ts3b = ts.withNanos(2345678);
final RexLiteral literal3b =
builder.makeLiteral(ts3b, timestampType9);
assertThat(literal3b.getValueAs(TimestampWithTimeZoneString.class),
hasToString("1969-07-21 02:56:15.002 PST"));
// Now with a very long fraction
final TimestampWithTimeZoneString ts4 = ts.withFraction("102030405060708090102");
final RexLiteral literal4 =
builder.makeLiteral(ts4, timestampType18);
assertThat(literal4.getValueAs(TimestampWithTimeZoneString.class),
hasToString("1969-07-21 02:56:15.102 PST"));
// toString
assertThat(ts2.round(1), hasToString("1969-07-21 02:56:15 PST"));
assertThat(ts2.round(2), hasToString("1969-07-21 02:56:15.05 PST"));
assertThat(ts2.round(3), hasToString("1969-07-21 02:56:15.056 PST"));
assertThat(ts2.round(4), hasToString("1969-07-21 02:56:15.056 PST"));
assertThat(ts2.toString(6), is("1969-07-21 02:56:15.056000 PST"));
assertThat(ts2.toString(1), is("1969-07-21 02:56:15.0 PST"));
assertThat(ts2.toString(0), is("1969-07-21 02:56:15 PST"));
assertThat(ts2.round(0), hasToString("1969-07-21 02:56:15 PST"));
assertThat(ts2.round(0).toString(0), is("1969-07-21 02:56:15 PST"));
assertThat(ts2.round(0).toString(1), is("1969-07-21 02:56:15.0 PST"));
assertThat(ts2.round(0).toString(2), is("1969-07-21 02:56:15.00 PST"));
}
private void checkTimestampTz(RexLiteral literal) {
assertThat(literal,
hasToString("1969-07-21 02:56:15 PST:TIMESTAMP_TZ(0)"));
assertThat(literal.getValue() instanceof TimestampWithTimeZoneString, is(true));
assertThat(literal.getValue2() instanceof Long, is(true));
assertThat(literal.getValue3() instanceof Long, is(true));
}
/** Tests {@link RexBuilder#makeTimeLiteral(TimeString, int)}. */
@Test void testTimeLiteral() {
final RelDataTypeFactory typeFactory =
new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
RelDataType timeType = typeFactory.createSqlType(SqlTypeName.TIME);
final RelDataType timeType3 =
typeFactory.createSqlType(SqlTypeName.TIME, 3);
final RelDataType timeType9 =
typeFactory.createSqlType(SqlTypeName.TIME, 9);
final RelDataType timeType18 =
typeFactory.createSqlType(SqlTypeName.TIME, 18);
final RexBuilder builder = new RexBuilder(typeFactory);
// Old way: provide a Calendar
final Calendar calendar = Util.calendar();
calendar.set(1969, Calendar.JULY, 21, 2, 56, 15); // one small step
calendar.set(Calendar.MILLISECOND, 0);
checkTime(builder.makeLiteral(calendar, timeType));
// Old way #2: Provide a Long
checkTime(builder.makeLiteral(MOON_TIME, timeType));
// The new way
final TimeString t = new TimeString(2, 56, 15);
assertThat(t.getMillisOfDay(), is(10575000));
checkTime(builder.makeLiteral(t, timeType));
// Now with milliseconds
final TimeString t2 = t.withMillis(56);
assertThat(t2.getMillisOfDay(), is(10575056));
assertThat(t2, hasToString("02:56:15.056"));
final RexLiteral literal2 = builder.makeLiteral(t2, timeType3);
assertThat(literal2.getValueAs(TimeString.class),
hasToString("02:56:15.056"));
// Now with nanoseconds
final TimeString t3 = t.withNanos(2345678);
assertThat(t3.getMillisOfDay(), is(10575002));
final RexLiteral literal3 = builder.makeLiteral(t3, timeType9);
assertThat(literal3.getValueAs(TimeString.class),
hasToString("02:56:15.002"));
// Now with a very long fraction
final TimeString t4 = t.withFraction("102030405060708090102");
assertThat(t4.getMillisOfDay(), is(10575102));
final RexLiteral literal4 = builder.makeLiteral(t4, timeType18);
assertThat(literal4.getValueAs(TimeString.class),
hasToString("02:56:15.102"));
// toString
assertThat(t2.round(1), hasToString("02:56:15"));
assertThat(t2.round(2), hasToString("02:56:15.05"));
assertThat(t2.round(3), hasToString("02:56:15.056"));
assertThat(t2.round(4), hasToString("02:56:15.056"));
assertThat(t2.toString(6), is("02:56:15.056000"));
assertThat(t2.toString(1), is("02:56:15.0"));
assertThat(t2.toString(0), is("02:56:15"));
assertThat(t2.round(0), hasToString("02:56:15"));
assertThat(t2.round(0).toString(0), is("02:56:15"));
assertThat(t2.round(0).toString(1), is("02:56:15.0"));
assertThat(t2.round(0).toString(2), is("02:56:15.00"));
assertThat(TimeString.fromMillisOfDay(53560123),
hasToString("14:52:40.123"));
}
private void checkTime(RexLiteral literal) {
assertThat(literal, hasToString("02:56:15"));
assertThat(literal.getValue() instanceof Calendar, is(true));
assertThat(literal.getValue2() instanceof Integer, is(true));
assertThat(literal.getValue3() instanceof Integer, is(true));
assertThat((Integer) literal.getValue2(), is(MOON_TIME));
assertThat(literal.getValueAs(Calendar.class), notNullValue());
assertThat(literal.getValueAs(TimeString.class), notNullValue());
}
/** Tests {@link RexBuilder#makeDateLiteral(DateString)}. */
@Test void testDateLiteral() {
final RelDataTypeFactory typeFactory =
new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
RelDataType dateType = typeFactory.createSqlType(SqlTypeName.DATE);
final RexBuilder builder = new RexBuilder(typeFactory);
// Old way: provide a Calendar
final Calendar calendar = Util.calendar();
calendar.set(1969, Calendar.JULY, 21); // one small step
calendar.set(Calendar.MILLISECOND, 0);
checkDate(builder.makeLiteral(calendar, dateType));
// Old way #2: Provide in Integer
checkDate(builder.makeLiteral(MOON_DAY, dateType));
// The new way
final DateString d = new DateString(1969, 7, 21);
checkDate(builder.makeLiteral(d, dateType));
}
private void checkDate(RexLiteral literal) {
assertThat(literal, hasToString("1969-07-21"));
assertThat(literal.getValue() instanceof Calendar, is(true));
assertThat(literal.getValue2() instanceof Integer, is(true));
assertThat(literal.getValue3() instanceof Integer, is(true));
assertThat((Integer) literal.getValue2(), is(MOON_DAY));
assertThat(literal.getValueAs(Calendar.class), notNullValue());
assertThat(literal.getValueAs(DateString.class), notNullValue());
}
/** Test case for
* <a href="https://issues.apache.org/jira/browse/CALCITE-2306">[CALCITE-2306]
* AssertionError in {@link RexLiteral#getValue3} with null literal of type
* DECIMAL</a>. */
@Test void testDecimalLiteral() {
final RelDataTypeFactory typeFactory =
new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
final RelDataType type = typeFactory.createSqlType(SqlTypeName.DECIMAL);
final RexBuilder builder = new RexBuilder(typeFactory);
final RexLiteral literal = builder.makeExactLiteral(null, type);
assertThat(literal.getValue3(), nullValue());
}
/** Test case for
* <a href="https://issues.apache.org/jira/browse/CALCITE-3587">[CALCITE-3587]
* RexBuilder may lose decimal fraction for creating literal with DECIMAL type</a>.
*/
@Test void testDecimal() {
final RelDataTypeFactory typeFactory =
new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
final RelDataType type = typeFactory.createSqlType(SqlTypeName.DECIMAL, 4, 2);
final RexBuilder builder = new RexBuilder(typeFactory);
try {
builder.makeLiteral(12.3, type);
fail();
} catch (AssertionError e) {
assertThat(e.getMessage(),
is("java.lang.Double is not compatible with DECIMAL, try to use makeExactLiteral"));
}
}
/** Tests {@link DateString} year range. */
@Test void testDateStringYearError() {
try {
final DateString dateString = new DateString(11969, 7, 21);
fail("expected exception, got " + dateString);
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("Year out of range: [11969]"));
}
try {
final DateString dateString = new DateString("12345-01-23");
fail("expected exception, got " + dateString);
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(),
containsString("Invalid date format: [12345-01-23]"));
}
}
/** Tests {@link DateString} month range. */
@Test void testDateStringMonthError() {
try {
final DateString dateString = new DateString(1969, 27, 21);
fail("expected exception, got " + dateString);
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("Month out of range: [27]"));
}
try {
final DateString dateString = new DateString("1234-13-02");
fail("expected exception, got " + dateString);
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("Month out of range: [13]"));
}
}
/** Tests {@link DateString} day range. */
@Test void testDateStringDayError() {
try {
final DateString dateString = new DateString(1969, 7, 41);
fail("expected exception, got " + dateString);
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("Day out of range: [41]"));
}
try {
final DateString dateString = new DateString("1234-01-32");
fail("expected exception, got " + dateString);
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("Day out of range: [32]"));
}
// We don't worry about the number of days in a month. 30 is in range.
final DateString dateString = new DateString("1234-02-30");
assertThat(dateString, notNullValue());
}
/** Tests {@link TimeString} hour range. */
@Test void testTimeStringHourError() {
try {
final TimeString timeString = new TimeString(111, 34, 56);
fail("expected exception, got " + timeString);
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("Hour out of range: [111]"));
}
try {
final TimeString timeString = new TimeString("24:00:00");
fail("expected exception, got " + timeString);
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("Hour out of range: [24]"));
}
try {
final TimeString timeString = new TimeString("24:00");
fail("expected exception, got " + timeString);
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(),
containsString("Invalid time format: [24:00]"));
}
}
/** Tests {@link TimeString} minute range. */
@Test void testTimeStringMinuteError() {
try {
final TimeString timeString = new TimeString(12, 334, 56);
fail("expected exception, got " + timeString);
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("Minute out of range: [334]"));
}
try {
final TimeString timeString = new TimeString("12:60:23");
fail("expected exception, got " + timeString);
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("Minute out of range: [60]"));
}
}
/** Tests {@link TimeString} second range. */
@Test void testTimeStringSecondError() {
try {
final TimeString timeString = new TimeString(12, 34, 567);
fail("expected exception, got " + timeString);
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("Second out of range: [567]"));
}
try {
final TimeString timeString = new TimeString(12, 34, -4);
fail("expected exception, got " + timeString);
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("Second out of range: [-4]"));
}
try {
final TimeString timeString = new TimeString("12:34:60");
fail("expected exception, got " + timeString);
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("Second out of range: [60]"));
}
}
/**
* Test string literal encoding.
*/
@Test void testStringLiteral() {
final RelDataTypeFactory typeFactory =
new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
final RelDataType varchar =
typeFactory.createSqlType(SqlTypeName.VARCHAR);
final RexBuilder builder = new RexBuilder(typeFactory);
final NlsString latin1 = new NlsString("foobar", "LATIN1", SqlCollation.IMPLICIT);
final NlsString utf8 = new NlsString("foobar", "UTF8", SqlCollation.IMPLICIT);
RexLiteral literal = builder.makePreciseStringLiteral("foobar");
assertEquals("'foobar'", literal.toString());
literal =
builder.makePreciseStringLiteral(
new ByteString(new byte[] { 'f', 'o', 'o', 'b', 'a', 'r'}),
"UTF8", SqlCollation.IMPLICIT);
assertEquals("_UTF8'foobar'", literal.toString());
assertEquals("_UTF8'foobar':CHAR(6) CHARACTER SET \"UTF-8\"",
literal.computeDigest(RexDigestIncludeType.ALWAYS));
literal =
builder.makePreciseStringLiteral(
new ByteString("\u82f1\u56fd".getBytes(StandardCharsets.UTF_8)),
"UTF8", SqlCollation.IMPLICIT);
assertEquals("_UTF8'\u82f1\u56fd'", literal.toString());
// Test again to check decode cache.
literal =
builder.makePreciseStringLiteral(
new ByteString("\u82f1".getBytes(StandardCharsets.UTF_8)),
"UTF8", SqlCollation.IMPLICIT);
assertEquals("_UTF8'\u82f1'", literal.toString());
try {
literal =
builder.makePreciseStringLiteral(
new ByteString("\u82f1\u56fd".getBytes(StandardCharsets.UTF_8)),
"GB2312", SqlCollation.IMPLICIT);
fail("expected exception, got " + literal);
} catch (RuntimeException e) {
assertThat(e.getMessage(), containsString("Failed to encode"));
}
literal = builder.makeLiteral(latin1, varchar);
assertEquals("_LATIN1'foobar'", literal.toString());
literal = builder.makeLiteral(utf8, varchar);
assertEquals("_UTF8'foobar'", literal.toString());
}
/** Tests {@link RexBuilder#makeExactLiteral(java.math.BigDecimal)}. */
@Test void testBigDecimalLiteral() {
final RelDataTypeFactory typeFactory = new SqlTypeFactoryImpl(new RelDataTypeSystemImpl() {
@Override public int getMaxPrecision(SqlTypeName typeName) {
return 38;
}
});
final RexBuilder builder = new RexBuilder(typeFactory);
checkBigDecimalLiteral(builder, "25");
checkBigDecimalLiteral(builder, "9.9");
checkBigDecimalLiteral(builder, "0");
checkBigDecimalLiteral(builder, "-75.5");
checkBigDecimalLiteral(builder, "10000000");
checkBigDecimalLiteral(builder, "100000.111111111111111111");
checkBigDecimalLiteral(builder, "-100000.111111111111111111");
checkBigDecimalLiteral(builder, "73786976294838206464"); // 2^66
checkBigDecimalLiteral(builder, "-73786976294838206464");
}
@Test void testMakeIn() {
final RelDataTypeFactory typeFactory =
new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
final RexBuilder rexBuilder = new RexBuilder(typeFactory);
final RelDataType floatType = typeFactory.createSqlType(SqlTypeName.REAL);
RexNode left = rexBuilder.makeInputRef(floatType, 0);
final RexNode literal1 = rexBuilder.makeLiteral(1.0f, floatType);
final RexNode literal2 = rexBuilder.makeLiteral(2.0f, floatType);
RexNode inCall = rexBuilder.makeIn(left, ImmutableList.of(literal1, literal2));
assertThat(inCall.getKind(), is(SqlKind.SEARCH));
}
/** Test case for
* <a href="https://issues.apache.org/jira/browse/CALCITE-4555">[CALCITE-4555]
* Invalid zero literal value is used for
* TIMESTAMP WITH LOCAL TIME ZONE type in RexBuilder</a>. */
@ParameterizedTest
@MethodSource("testData4testMakeZeroLiteral")
void testMakeZeroLiteral(RelDataType type, RexLiteral expected) {
final RelDataTypeFactory typeFactory = new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
final RexBuilder rexBuilder = new RexBuilder(typeFactory);
assertThat(rexBuilder.makeZeroLiteral(type), is(equalTo(expected)));
}
private static Stream<Arguments> testData4testMakeZeroLiteral() {
final RelDataTypeFactory typeFactory =
new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
final RexBuilder rexBuilder = new RexBuilder(typeFactory);
BiFunction<RelDataType, Function<RelDataType, Comparable>, Arguments> type2rexLiteral =
(relDataType, relDataTypeComparableFunction) ->
of(relDataType,
rexBuilder.makeLiteral(
relDataTypeComparableFunction.apply(relDataType), relDataType));
return Stream.of(
type2rexLiteral.apply(typeFactory.createSqlType(SqlTypeName.CHAR),
relDataType -> new NlsString(Spaces.of(relDataType.getPrecision()), null, null)),
type2rexLiteral.apply(typeFactory.createSqlType(SqlTypeName.VARCHAR),
relDataType -> new NlsString("", null, null)),
type2rexLiteral.apply(typeFactory.createSqlType(SqlTypeName.BINARY),
relDataType -> new ByteString(new byte[relDataType.getPrecision()])),
type2rexLiteral.apply(typeFactory.createSqlType(SqlTypeName.VARBINARY),
relDataType -> ByteString.EMPTY),
type2rexLiteral.apply(typeFactory.createSqlType(SqlTypeName.TINYINT),
relDataType -> BigDecimal.ZERO),
type2rexLiteral.apply(typeFactory.createSqlType(SqlTypeName.SMALLINT),
relDataType -> BigDecimal.ZERO),
type2rexLiteral.apply(typeFactory.createSqlType(SqlTypeName.INTEGER),
relDataType -> BigDecimal.ZERO),
type2rexLiteral.apply(typeFactory.createSqlType(SqlTypeName.BIGINT),
relDataType -> BigDecimal.ZERO),
type2rexLiteral.apply(typeFactory.createSqlType(SqlTypeName.DECIMAL),
relDataType -> BigDecimal.ZERO),
type2rexLiteral.apply(typeFactory.createSqlType(SqlTypeName.FLOAT),
relDataType -> BigDecimal.ZERO),
type2rexLiteral.apply(typeFactory.createSqlType(SqlTypeName.REAL),
relDataType -> BigDecimal.ZERO),
type2rexLiteral.apply(typeFactory.createSqlType(SqlTypeName.DOUBLE),
relDataType -> BigDecimal.ZERO),
type2rexLiteral.apply(typeFactory.createSqlType(SqlTypeName.BOOLEAN),
relDataType -> false),
type2rexLiteral.apply(typeFactory.createSqlType(SqlTypeName.TIME),
relDataType -> DateTimeUtils.ZERO_CALENDAR),
type2rexLiteral.apply(typeFactory.createSqlType(SqlTypeName.DATE),
relDataType -> DateTimeUtils.ZERO_CALENDAR),
type2rexLiteral.apply(typeFactory.createSqlType(SqlTypeName.TIMESTAMP),
relDataType -> DateTimeUtils.ZERO_CALENDAR),
type2rexLiteral.apply(typeFactory.createSqlType(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE),
relDataType -> new TimeString(0, 0, 0)),
type2rexLiteral.apply(typeFactory.createSqlType(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE),
relDataType -> new TimestampString(0, 1, 1, 0, 0, 0)),
type2rexLiteral.apply(typeFactory.createSqlType(SqlTypeName.TIME_TZ),
relDataType -> new TimeWithTimeZoneString(0, 0, 0, "GMT+00:00")),
type2rexLiteral.apply(typeFactory.createSqlType(SqlTypeName.TIMESTAMP_TZ),
relDataType -> new TimestampWithTimeZoneString(0, 1, 1, 0, 0, 0, "GMT+00:00")));
}
/** Test case for
* <a href="https://issues.apache.org/jira/browse/CALCITE-4632">[CALCITE-4632]
* Find the least restrictive datatype for SARG</a>. */
@Test void testLeastRestrictiveTypeForSargMakeIn() {
final RelDataTypeFactory typeFactory = new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
final RexBuilder rexBuilder = new RexBuilder(typeFactory);
final RelDataType decimalType = typeFactory.createSqlType(SqlTypeName.DECIMAL);
RexNode left = rexBuilder.makeInputRef(decimalType, 0);
final RexNode literal1 = rexBuilder.makeExactLiteral(new BigDecimal("1.0"));
final RexNode literal2 = rexBuilder.makeExactLiteral(new BigDecimal("20000.0"));
RexNode inCall = rexBuilder.makeIn(left, ImmutableList.of(literal1, literal2));
assertThat(inCall.getKind(), is(SqlKind.SEARCH));
final RexNode sarg = ((RexCall) inCall).operands.get(1);
RelDataType expected = typeFactory.createSqlType(SqlTypeName.DECIMAL, 6, 1);
assertEquals(sarg.getType(), expected);
}
/** Test case for
* <a href="https://issues.apache.org/jira/browse/CALCITE-4632">[CALCITE-4632]
* Find the least restrictive datatype for SARG</a>. */
@Test void testLeastRestrictiveTypeForSargMakeBetween() {
final RelDataTypeFactory typeFactory = new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
final RexBuilder rexBuilder = new RexBuilder(typeFactory);
final RelDataType decimalType = typeFactory.createSqlType(SqlTypeName.DECIMAL);
RexNode left = rexBuilder.makeInputRef(decimalType, 0);
final RexNode literal1 = rexBuilder.makeExactLiteral(new BigDecimal("1.0"));
final RexNode literal2 = rexBuilder.makeExactLiteral(new BigDecimal("20000.0"));
RexNode betweenCall = rexBuilder.makeBetween(left, literal1, literal2);
assertThat(betweenCall.getKind(), is(SqlKind.SEARCH));
final RexNode sarg = ((RexCall) betweenCall).operands.get(1);
RelDataType expected = typeFactory.createSqlType(SqlTypeName.DECIMAL, 6, 1);
assertEquals(sarg.getType(), expected);
}
/** Tests {@link RexCopier#visitOver(RexOver)}. */
@Test void testCopyOver() {
final RelDataTypeFactory sourceTypeFactory =
new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
RelDataType type = sourceTypeFactory.createSqlType(SqlTypeName.VARCHAR, 65536);
final RelDataTypeFactory targetTypeFactory =
new MySqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
final RexBuilder builder = new RexBuilder(targetTypeFactory);
final RexOver node =
(RexOver) builder.makeOver(type, SqlStdOperatorTable.COUNT,
ImmutableList.of(builder.makeInputRef(type, 0)),
ImmutableList.of(builder.makeInputRef(type, 1)),
ImmutableList.of(
new RexFieldCollation(builder.makeInputRef(type, 2),
ImmutableSet.of())),
RexWindowBounds.UNBOUNDED_PRECEDING,
RexWindowBounds.CURRENT_ROW,
true, true, false, false, false);
final RexNode copy = builder.copy(node);
assertTrue(copy instanceof RexOver);
RexOver result = (RexOver) copy;
assertThat(result.getType().getSqlTypeName(), is(SqlTypeName.VARCHAR));
assertThat(result.getType().getPrecision(), is(PRECISION));
assertThat(result.getWindow(), is(node.getWindow()));
assertThat(result.getAggOperator(), is(node.getAggOperator()));
assertThat(result.getAggOperator(), is(node.getAggOperator()));
assertEquals(node.isDistinct(), result.isDistinct());
assertEquals(node.ignoreNulls(), result.ignoreNulls());
for (int i = 0; i < node.getOperands().size(); i++) {
assertThat(result.getOperands().get(i).getType().getSqlTypeName(),
is(node.getOperands().get(i).getType().getSqlTypeName()));
assertThat(result.getOperands().get(i).getType().getPrecision(),
is(PRECISION));
}
}
/** Tests {@link RexCopier#visitCorrelVariable(RexCorrelVariable)}. */
@Test void testCopyCorrelVariable() {
final RelDataTypeFactory sourceTypeFactory =
new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
RelDataType type = sourceTypeFactory.createSqlType(SqlTypeName.VARCHAR, 65536);
final RelDataTypeFactory targetTypeFactory =
new MySqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
final RexBuilder builder = new RexBuilder(targetTypeFactory);
final RexCorrelVariable node =
(RexCorrelVariable) builder.makeCorrel(type, new CorrelationId(0));
final RexNode copy = builder.copy(node);
assertTrue(copy instanceof RexCorrelVariable);
final RexCorrelVariable result = (RexCorrelVariable) copy;
assertThat(result.id, is(node.id));
assertThat(result.getType().getSqlTypeName(), is(SqlTypeName.VARCHAR));
assertThat(result.getType().getPrecision(), is(PRECISION));
}
/** Tests {@link RexCopier#visitLocalRef(RexLocalRef)}. */
@Test void testCopyLocalRef() {
final RelDataTypeFactory sourceTypeFactory =
new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
RelDataType type = sourceTypeFactory.createSqlType(SqlTypeName.VARCHAR, 65536);
final RelDataTypeFactory targetTypeFactory =
new MySqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
final RexBuilder builder = new RexBuilder(targetTypeFactory);
final RexLocalRef node = new RexLocalRef(0, type);
final RexNode copy = builder.copy(node);
assertTrue(copy instanceof RexLocalRef);
final RexLocalRef result = (RexLocalRef) copy;
assertThat(result.getIndex(), is(node.getIndex()));
assertThat(result.getType().getSqlTypeName(), is(SqlTypeName.VARCHAR));
assertThat(result.getType().getPrecision(), is(PRECISION));
}
/** Tests {@link RexCopier#visitDynamicParam(RexDynamicParam)}. */
@Test void testCopyDynamicParam() {
final RelDataTypeFactory sourceTypeFactory =
new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
RelDataType type = sourceTypeFactory.createSqlType(SqlTypeName.VARCHAR, 65536);
final RelDataTypeFactory targetTypeFactory =
new MySqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
final RexBuilder builder = new RexBuilder(targetTypeFactory);
final RexDynamicParam node = builder.makeDynamicParam(type, 0);
final RexNode copy = builder.copy(node);
assertTrue(copy instanceof RexDynamicParam);
final RexDynamicParam result = (RexDynamicParam) copy;
assertThat(result.getIndex(), is(node.getIndex()));
assertThat(result.getType().getSqlTypeName(), is(SqlTypeName.VARCHAR));
assertThat(result.getType().getPrecision(), is(PRECISION));
}
/** Tests {@link RexCopier#visitRangeRef(RexRangeRef)}. */
@Test void testCopyRangeRef() {
final RelDataTypeFactory sourceTypeFactory =
new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
RelDataType type = sourceTypeFactory.createSqlType(SqlTypeName.VARCHAR, 65536);
final RelDataTypeFactory targetTypeFactory =
new MySqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
final RexBuilder builder = new RexBuilder(targetTypeFactory);
final RexRangeRef node = builder.makeRangeReference(type, 1, true);
final RexNode copy = builder.copy(node);
assertTrue(copy instanceof RexRangeRef);
final RexRangeRef result = (RexRangeRef) copy;
assertThat(result.getOffset(), is(node.getOffset()));
assertThat(result.getType().getSqlTypeName(), is(SqlTypeName.VARCHAR));
assertThat(result.getType().getPrecision(), is(PRECISION));
}
private void checkBigDecimalLiteral(RexBuilder builder, String val) {
final RexLiteral literal = builder.makeExactLiteral(new BigDecimal(val));
assertThat("builder.makeExactLiteral(new BigDecimal(" + val
+ ")).getValueAs(BigDecimal.class).toString()",
literal.getValueAs(BigDecimal.class), hasToString(val));
}
@Test void testValidateRexFieldAccess() {
final RelDataTypeFactory typeFactory =
new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
final RexBuilder builder = new RexBuilder(typeFactory);
RelDataType intType = typeFactory.createSqlType(SqlTypeName.INTEGER);
RelDataType longType = typeFactory.createSqlType(SqlTypeName.BIGINT);
RelDataType structType =
typeFactory.createStructType(Arrays.asList(intType, longType),
Arrays.asList("x", "y"));
RexInputRef inputRef = builder.makeInputRef(structType, 0);
// construct RexFieldAccess fails because of negative index
IllegalArgumentException e1 = assertThrows(IllegalArgumentException.class, () -> {
RelDataTypeField field = new RelDataTypeFieldImpl("z", -1, intType);
new RexFieldAccess(inputRef, field);
});
assertThat(e1.getMessage(),
is("Field #-1: z INTEGER does not exist for expression $0"));
// construct RexFieldAccess fails because of too large index
IllegalArgumentException e2 = assertThrows(IllegalArgumentException.class, () -> {
RelDataTypeField field = new RelDataTypeFieldImpl("z", 2, intType);
new RexFieldAccess(inputRef, field);
});
assertThat(e2.getMessage(),
is("Field #2: z INTEGER does not exist for expression $0"));
// construct RexFieldAccess fails because of incorrect type
IllegalArgumentException e3 = assertThrows(IllegalArgumentException.class, () -> {
RelDataTypeField field = new RelDataTypeFieldImpl("z", 0, longType);
new RexFieldAccess(inputRef, field);
});
assertThat(e3.getMessage(),
is("Field #0: z BIGINT does not exist for expression $0"));
// construct RexFieldAccess successfully
RelDataTypeField field = new RelDataTypeFieldImpl("x", 0, intType);
RexFieldAccess fieldAccess = new RexFieldAccess(inputRef, field);
RexChecker checker = new RexChecker(structType, () -> null, Litmus.THROW);
assertThat(fieldAccess.accept(checker), is(true));
}
/** Emulate a user defined type. */
private static class UDT extends RelDataTypeImpl {
UDT() {
this.digest = "(udt)NOT NULL";
}
@Override protected void generateTypeString(StringBuilder sb, boolean withDetail) {
sb.append("udt");
}
}
@Test void testUDTLiteralDigest() {
RexLiteral literal = new RexLiteral(new BigDecimal(0L), new UDT(), SqlTypeName.BIGINT);
// when the space before "NOT NULL" is missing, the digest is not correct
// and the suffix should not be removed.
assertThat(literal.digest, is("0L:(udt)NOT NULL"));
}
/** Test case for
* <a href="https://issues.apache.org/jira/browse/CALCITE-5489">[CALCITE-5489]
* RexCall to TIMESTAMP_DIFF function fails to convert a TIMESTAMP literal to
* a org.apache.calcite.avatica.util.TimeUnit</a>. */
@Test void testTimestampDiffCall() {
final RexImplicationCheckerFixtures.Fixture f =
new RexImplicationCheckerFixtures.Fixture();
final TimestampString ts =
TimestampString.fromCalendarFields(Util.calendar());
final RexNode literal = f.timestampLiteral(ts);
final RexLiteral flag = f.rexBuilder.makeFlag(TimeUnit.QUARTER);
assertThat(
f.rexBuilder.makeCall(SqlLibraryOperators.DATEDIFF,
flag, literal, literal),
notNullValue());
assertThat(
f.rexBuilder.makeCall(SqlStdOperatorTable.TIMESTAMP_DIFF,
flag, literal, literal),
notNullValue());
assertThat(
f.rexBuilder.makeCall(SqlLibraryOperators.TIMESTAMP_DIFF3,
literal, literal, flag),
notNullValue());
assertThat(
f.rexBuilder.makeCall(SqlLibraryOperators.TIME_DIFF,
literal, literal, flag),
notNullValue());
}
}