Skip to content
Permalink
Browse files
IGNITE-16633 Adoption of a bunch of calcite related tickets from Igni…
…te-2 - Fixes #698.

IGNITE-15109 TIMESTAMPDIFF for MICROSECOND unit doesn't work.
IGNITE-14975 Introduce suppot INTERVAL type.
IGNITE-15596 java.lang.Integer cannot be cast to java.lang.Long with JOIN and EXISTS.
IGNITE-16129 RelJsonReader can't deserialize enums.

Signed-off-by: zstan <stanilovsky@gmail.com>
  • Loading branch information
zstan committed Mar 4, 2022
1 parent ed519fb commit 157a8f09bd98314f548f1672467f7dd682b32df2
Show file tree
Hide file tree
Showing 17 changed files with 897 additions and 43 deletions.
@@ -77,7 +77,7 @@ public void testUnicodeStrings() {
@Disabled("https://issues.apache.org/jira/browse/IGNITE-16292")
@Test
public void testCheckDefaultsAndNullables() {
sql("CREATE TABLE tbl(c1 int primary key, c2 int NOT NULL, c3 int NOT NULL DEFAULT 100)");
sql("CREATE TABLE tbl(c1 int PRIMARY KEY, c2 int NOT NULL, c3 int NOT NULL DEFAULT 100)");

sql("INSERT INTO tbl(c1, c2) VALUES (1, 2)");

@@ -94,4 +94,66 @@ public void testCheckDefaultsAndNullables() {
//todo: correct exception https://issues.apache.org/jira/browse/IGNITE-16095
assertThrows(IgniteException.class, () -> sql("INSERT INTO tbl(c1, c2) VALUES (2, NULL)"));
}

/**
* Tests numeric types mapping on Java types.
*/
@Test
public void testNumericRanges() {
try {
sql("CREATE TABLE tbl(id int PRIMARY KEY, tiny TINYINT, small SMALLINT, i INTEGER, big BIGINT)");

sql("INSERT INTO tbl VALUES (1, " + Byte.MAX_VALUE + ", " + Short.MAX_VALUE + ", "
+ Integer.MAX_VALUE + ", " + Long.MAX_VALUE + ')');

assertQuery("SELECT tiny FROM tbl").returns(Byte.MAX_VALUE).check();
assertQuery("SELECT small FROM tbl").returns(Short.MAX_VALUE).check();
assertQuery("SELECT i FROM tbl").returns(Integer.MAX_VALUE).check();
assertQuery("SELECT big FROM tbl").returns(Long.MAX_VALUE).check();

sql("DELETE from tbl");

sql("INSERT INTO tbl VALUES (1, " + Byte.MIN_VALUE + ", " + Short.MIN_VALUE + ", "
+ Integer.MIN_VALUE + ", " + Long.MIN_VALUE + ')');

assertQuery("SELECT tiny FROM tbl").returns(Byte.MIN_VALUE).check();
assertQuery("SELECT small FROM tbl").returns(Short.MIN_VALUE).check();
assertQuery("SELECT i FROM tbl").returns(Integer.MIN_VALUE).check();
assertQuery("SELECT big FROM tbl").returns(Long.MIN_VALUE).check();
} finally {
sql("DROP TABLE if exists tbl");
}
}

/**
* Tests numeric type convertation on equals.
*/
@Test
public void testNumericConvertingOnEquals() {
try {
sql("CREATE TABLE tbl(id int PRIMARY KEY, tiny TINYINT, small SMALLINT, i INTEGER, big BIGINT)");

sql("INSERT INTO tbl VALUES (-1, 1, 2, 3, 4), (0, 5, 5, 5, 5)");

assertQuery("SELECT t1.tiny FROM tbl t1 JOIN tbl t2 ON (t1.tiny=t2.small)").returns((byte) 5).check();
assertQuery("SELECT t1.small FROM tbl t1 JOIN tbl t2 ON (t1.small=t2.tiny)").returns((short) 5).check();

assertQuery("SELECT t1.tiny FROM tbl t1 JOIN tbl t2 ON (t1.tiny=t2.i)").returns((byte) 5).check();
assertQuery("SELECT t1.i FROM tbl t1 JOIN tbl t2 ON (t1.i=t2.tiny)").returns(5).check();

assertQuery("SELECT t1.tiny FROM tbl t1 JOIN tbl t2 ON (t1.tiny=t2.big)").returns((byte) 5).check();
assertQuery("SELECT t1.big FROM tbl t1 JOIN tbl t2 ON (t1.big=t2.tiny)").returns(5L).check();

assertQuery("SELECT t1.small FROM tbl t1 JOIN tbl t2 ON (t1.small=t2.i)").returns((short) 5).check();
assertQuery("SELECT t1.i FROM tbl t1 JOIN tbl t2 ON (t1.i=t2.small)").returns(5).check();

assertQuery("SELECT t1.small FROM tbl t1 JOIN tbl t2 ON (t1.small=t2.big)").returns((short) 5).check();
assertQuery("SELECT t1.big FROM tbl t1 JOIN tbl t2 ON (t1.big=t2.small)").returns(5L).check();

assertQuery("SELECT t1.i FROM tbl t1 JOIN tbl t2 ON (t1.i=t2.big)").returns(5).check();
assertQuery("SELECT t1.big FROM tbl t1 JOIN tbl t2 ON (t1.big=t2.i)").returns(5L).check();
} finally {
sql("DROP TABLE if exists tbl");
}
}
}
@@ -46,6 +46,15 @@
public class ItFunctionsTest extends AbstractBasicIntegrationTest {
private static final Object[] NULL_RESULT = new Object[] { null };

@Test
public void testTimestampDiffWithFractionsOfSecond() {
assertQuery("SELECT TIMESTAMPDIFF(MICROSECOND, TIMESTAMP '2022-02-01 10:30:28.000', "
+ "TIMESTAMP '2022-02-01 10:30:28.128')").returns(128000).check();

assertQuery("SELECT TIMESTAMPDIFF(NANOSECOND, TIMESTAMP '2022-02-01 10:30:28.000', "
+ "TIMESTAMP '2022-02-01 10:30:28.128')").returns(128000000L).check();
}

@Test
public void testLength() {
assertQuery("SELECT LENGTH('TEST')").returns(4).check();

Large diffs are not rendered by default.

@@ -79,9 +79,12 @@ public void columnNames() {

@Test
public void infixTypeCast() {
assertQuery("select id, id::tinyint as tid, id::smallint as sid, id::varchar as vid from person")
.columnNames("ID", "TID", "SID", "VID")
.columnTypes(Integer.class, Byte.class, Short.class, String.class)
assertQuery("select id, id::tinyint as tid, id::smallint as sid, id::varchar as vid, id::interval hour, "
+ "id::interval year from person")
.columnNames("ID", "TID", "SID", "VID", "ID :: INTERVAL INTERVAL_HOUR", "ID :: INTERVAL INTERVAL_YEAR")
// TODO: IGNITE-16635 replace byte arrays for correct types.
//.columnTypes(Integer.class, Byte.class, Short.class, String.class, Duration.class, Period.class)
.columnTypes(Integer.class, Byte.class, Short.class, String.class, byte[].class, byte[].class)
.check();
}

@@ -38,6 +38,7 @@ data: {
"org.apache.ignite.internal.sql.engine.sql.IgniteSqlCreateTableOption",
"org.apache.ignite.internal.sql.engine.sql.IgniteSqlCreateTableOptionEnum",
"org.apache.ignite.internal.sql.engine.sql.IgniteSqlDropIndex",
"org.apache.ignite.internal.sql.engine.sql.IgniteSqlIntervalTypeNameSpec",
"org.apache.calcite.sql.ddl.SqlDdlNodes",
]

@@ -66,6 +66,32 @@ void CreateTableOption(List<SqlNode> list) :
}
}

SqlDataTypeSpec DataTypeEx() :
{
final SqlDataTypeSpec dt;
}
{
(
dt = DataType()
|
dt = IntervalType()
)
{
return dt;
}
}

SqlDataTypeSpec IntervalType() :
{
final Span s;
final SqlIntervalQualifier intervalQualifier;
}
{
<INTERVAL> { s = span(); } intervalQualifier = IntervalQualifier() {
return new SqlDataTypeSpec(new IgniteSqlIntervalTypeNameSpec(intervalQualifier, s.end(this)), s.pos());
}
}

void TableElement(List<SqlNode> list) :
{
final SqlDataTypeSpec type;
@@ -77,7 +103,7 @@ void TableElement(List<SqlNode> list) :
SqlIdentifier id = null;
}
{
id = SimpleIdentifier() type = DataType() nullable = NullableOptDefaultNull()
id = SimpleIdentifier() type = DataTypeEx() nullable = NullableOptDefaultNull()
(
<DEFAULT_> { s.add(this); } dflt = Literal() {
strategy = ColumnStrategy.DEFAULT;
@@ -257,7 +283,7 @@ void InfixCast(List<Object> list, ExprContext exprContext, Span s) :
<INFIX_CAST> {
checkNonQueryExpression(exprContext);
}
dt = DataType() {
dt = DataTypeEx() {
list.add(
new SqlParserUtil.ToTreeListItem(SqlLibraryOperators.INFIX_CAST,
s.pos()));
@@ -293,7 +319,7 @@ SqlNode ColumnWithType() :
}
{
id = SimpleIdentifier()
type = DataType()
type = DataTypeEx()
[
<NOT> <NULL> {
nullable = false;
@@ -19,14 +19,20 @@

import java.lang.reflect.Method;
import org.apache.calcite.linq4j.tree.Types;
import org.apache.calcite.sql.SqlIntervalQualifier;
import org.apache.calcite.sql.parser.SqlParserUtil;

/**
* Built-in methods.
*/
public enum IgniteBuiltInMethod {
SYSTEM_RANGE2(IgniteSqlFunctions.class, "systemRange", Object.class, Object.class),

SYSTEM_RANGE3(IgniteSqlFunctions.class, "systemRange", Object.class, Object.class, Object.class);
SYSTEM_RANGE3(IgniteSqlFunctions.class, "systemRange", Object.class, Object.class, Object.class),

PARSE_INTERVAL_YEAR_MONTH(SqlParserUtil.class, "intervalToMonths", String.class, SqlIntervalQualifier.class),

PARSE_INTERVAL_DAY_TIME(SqlParserUtil.class, "intervalToMillis", String.class, SqlIntervalQualifier.class);

public final Method method;

@@ -1320,6 +1320,8 @@ Expression implementSafe(final RexToLixTranslator translator,
Expression operand = argValueList.get(1);
final SqlTypeName sqlTypeName =
call.operands.get(1).getType().getSqlTypeName();
final boolean isIntervalType = SqlTypeUtil.isInterval(call.operands.get(1).getType());

switch (unit) {
case MILLENNIUM:
case CENTURY:
@@ -1372,7 +1374,7 @@ Expression implementSafe(final RexToLixTranslator translator,
return Expressions.constant(0L);
}

operand = mod(operand, TimeUnit.MINUTE.multiplier.longValue());
operand = mod(operand, TimeUnit.MINUTE.multiplier.longValue(), !isIntervalType);
return Expressions.multiply(
operand, Expressions.constant((long) (1 / unit.multiplier.doubleValue())));
case EPOCH:
@@ -1426,7 +1428,10 @@ Expression implementSafe(final RexToLixTranslator translator,
// No-Op.
}

operand = mod(operand, getFactor(unit));
// According to SQL standard result for interval data types should have the same sign as the source,
// but QUARTER is not covered by standard and negative values for QUARTER make no sense.
operand = mod(operand, getFactor(unit), unit == TimeUnit.QUARTER || !isIntervalType);

if (unit == TimeUnit.QUARTER) {
operand = Expressions.subtract(operand, Expressions.constant(1L));
}
@@ -1441,12 +1446,12 @@ Expression implementSafe(final RexToLixTranslator translator,
}
}

private static Expression mod(Expression operand, long factor) {
private static Expression mod(Expression operand, long factor, boolean floorMod) {
if (factor == 1L) {
return operand;
} else {
return Expressions.call(BuiltInMethod.FLOOR_MOD.method,
operand, Expressions.constant(factor));
return floorMod ? Expressions.call(BuiltInMethod.FLOOR_MOD.method, operand, Expressions.constant(factor)) :
Expressions.modulo(operand, Expressions.constant(factor));
}
}

@@ -20,6 +20,7 @@
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.CASE;
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.SEARCH;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.math.BigDecimal;
@@ -66,6 +67,7 @@
import org.apache.calcite.sql.SqlIntervalQualifier;
import org.apache.calcite.sql.SqlOperator;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
import org.apache.calcite.sql.parser.SqlParserPos;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.sql.type.SqlTypeUtil;
import org.apache.calcite.sql.validate.SqlConformance;
@@ -514,6 +516,43 @@ Expression translateCast(
// No-Op.
}
break;
case INTERVAL_YEAR:
case INTERVAL_YEAR_MONTH:
case INTERVAL_MONTH:
case INTERVAL_DAY:
case INTERVAL_DAY_HOUR:
case INTERVAL_DAY_MINUTE:
case INTERVAL_DAY_SECOND:
case INTERVAL_HOUR:
case INTERVAL_HOUR_MINUTE:
case INTERVAL_HOUR_SECOND:
case INTERVAL_MINUTE:
case INTERVAL_MINUTE_SECOND:
case INTERVAL_SECOND:
switch (sourceType.getSqlTypeName().getFamily()) {
case CHARACTER:
SqlIntervalQualifier intervalQualifier = targetType.getIntervalQualifier();

Method method = intervalQualifier.isYearMonth()
? IgniteBuiltInMethod.PARSE_INTERVAL_YEAR_MONTH.method
: IgniteBuiltInMethod.PARSE_INTERVAL_DAY_TIME.method;

convert = Expressions.call(
method,
operand,
Expressions.new_(SqlIntervalQualifier.class,
Expressions.constant(intervalQualifier.getStartUnit()),
Expressions.constant(intervalQualifier.getStartPrecisionPreservingDefault()),
Expressions.constant(intervalQualifier.getEndUnit()),
Expressions.constant(intervalQualifier.getFractionalSecondPrecisionPreservingDefault()),
Expressions.field(null, SqlParserPos.class, "ZERO")
)
);
break;
default:
// No-Op.
}
break;
default:
// No-Op.
}
@@ -626,16 +626,16 @@ RelDataType toType(RelDataTypeFactory typeFactory, Object o) {
SqlTypeName sqlTypeName = toEnum(map.get("type"));
Integer precision = (Integer) map.get("precision");
Integer scale = (Integer) map.get("scale");
RelDataType type;

if (SqlTypeName.INTERVAL_TYPES.contains(sqlTypeName)) {
TimeUnit startUnit = sqlTypeName.getStartUnit();
TimeUnit endUnit = sqlTypeName.getEndUnit();
return typeFactory.createSqlIntervalType(
type = typeFactory.createSqlIntervalType(
new SqlIntervalQualifier(startUnit, endUnit, SqlParserPos.ZERO));
} else if (sqlTypeName == SqlTypeName.ARRAY) {
return typeFactory.createArrayType(toType(typeFactory, map.get("elementType")), -1);
}
RelDataType type;
if (precision == null) {
type = typeFactory.createArrayType(toType(typeFactory, map.get("elementType")), -1);
} else if (precision == null) {
type = typeFactory.createSqlType(sqlTypeName);
} else if (scale == null) {
type = typeFactory.createSqlType(sqlTypeName, precision);

0 comments on commit 157a8f0

Please sign in to comment.