diff --git a/arrow/src/test/java/org/apache/calcite/adapter/arrow/ArrowAdapterTest.java b/arrow/src/test/java/org/apache/calcite/adapter/arrow/ArrowAdapterTest.java index 295ae6eae6b..05bc053afcb 100644 --- a/arrow/src/test/java/org/apache/calcite/adapter/arrow/ArrowAdapterTest.java +++ b/arrow/src/test/java/org/apache/calcite/adapter/arrow/ArrowAdapterTest.java @@ -374,7 +374,7 @@ static void initializeArrowState(@TempDir Path sharedTempDir) String sql = "select * from arrowdata\n" + " where \"floatField\"=15.0"; String plan = "PLAN=ArrowToEnumerableConverter\n" - + " ArrowFilter(condition=[=(CAST($2):DOUBLE, 15.0E0)])\n" + + " ArrowFilter(condition=[=($2, 15.0E0)])\n" + " ArrowTableScan(table=[[ARROW, ARROWDATA]], fields=[[0, 1, 2, 3]])\n\n"; String result = "intField=15; stringField=15; floatField=15.0; longField=15\n"; @@ -666,7 +666,7 @@ static void initializeArrowState(@TempDir Path sharedTempDir) @Test void testFilteredAgg() { String sql = "select SUM(SAL) FILTER (WHERE COMM > 400) as SALESSUM from EMP"; String plan = "PLAN=EnumerableAggregate(group=[{}], SALESSUM=[SUM($0) FILTER $1])\n" - + " EnumerableCalc(expr#0..7=[{inputs}], expr#8=[400], expr#9=[>($t6, $t8)], " + + " EnumerableCalc(expr#0..7=[{inputs}], expr#8=[400:DECIMAL(19, 0)], expr#9=[>($t6, $t8)], " + "expr#10=[IS TRUE($t9)], SAL=[$t5], $f1=[$t10])\n" + " ArrowToEnumerableConverter\n" + " ArrowTableScan(table=[[ARROW, EMP]], fields=[[0, 1, 2, 3, 4, 5, 6, 7]])\n\n"; @@ -684,7 +684,7 @@ static void initializeArrowState(@TempDir Path sharedTempDir) String sql = "select SUM(SAL) FILTER (WHERE COMM > 400) as SALESSUM from EMP group by EMPNO"; String plan = "PLAN=EnumerableCalc(expr#0..1=[{inputs}], SALESSUM=[$t1])\n" + " EnumerableAggregate(group=[{0}], SALESSUM=[SUM($1) FILTER $2])\n" - + " EnumerableCalc(expr#0..7=[{inputs}], expr#8=[400], expr#9=[>($t6, $t8)], " + + " EnumerableCalc(expr#0..7=[{inputs}], expr#8=[400:DECIMAL(19, 0)], expr#9=[>($t6, $t8)], " + "expr#10=[IS TRUE($t9)], EMPNO=[$t0], SAL=[$t5], $f2=[$t10])\n" + " ArrowToEnumerableConverter\n" + " ArrowTableScan(table=[[ARROW, EMP]], fields=[[0, 1, 2, 3, 4, 5, 6, 7]])\n\n"; diff --git a/core/src/main/java/org/apache/calcite/sql/validate/implicit/AbstractTypeCoercion.java b/core/src/main/java/org/apache/calcite/sql/validate/implicit/AbstractTypeCoercion.java index 84757977171..e313a7bfd1a 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/implicit/AbstractTypeCoercion.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/implicit/AbstractTypeCoercion.java @@ -492,8 +492,8 @@ private RelDataType getTightestCommonTypeOrThrow( } /** - * Determines common type for a comparison operator when one operand is String type and the - * other is not. For date + timestamp operands, use timestamp as common type, + * Determines common type for a comparison operator. + * For date and timestamp operands, use timestamp as common type, * i.e. Timestamp(2017-01-01 00:00 ...) > Date(2018) evaluates to be false. */ @Override public @Nullable RelDataType commonTypeForBinaryComparison( @@ -509,9 +509,11 @@ private RelDataType getTightestCommonTypeOrThrow( return null; } - // DATETIME + CHARACTER -> DATETIME - // REVIEW Danny 2019-09-23: There is some legacy redundant code in SqlToRelConverter - // that coerce Datetime and CHARACTER comparison. + if (SqlTypeUtil.sameNamedType(type1, type2)) { + return factory.leastRestrictive(ImmutableList.of(type1, type2)); + } + + // DATETIME < CHARACTER -> DATETIME if (SqlTypeUtil.isCharacter(type1) && SqlTypeUtil.isDatetime(type2)) { return factory.createTypeWithNullability(type2, type1.isNullable()); } @@ -520,7 +522,7 @@ private RelDataType getTightestCommonTypeOrThrow( return factory.createTypeWithNullability(type1, type2.isNullable()); } - // DATE + TIMESTAMP -> TIMESTAMP + // DATE < TIMESTAMP -> TIMESTAMP if (SqlTypeUtil.isDate(type1) && SqlTypeUtil.isTimestamp(type2)) { return factory.createTypeWithNullability(type2, type1.isNullable()); } @@ -556,6 +558,19 @@ private RelDataType getTightestCommonTypeOrThrow( return null; } + if (SqlTypeUtil.isString(type1) && SqlTypeUtil.isString(type2)) { + // Return the string with the larger precision + if (type1.getPrecision() == RelDataType.PRECISION_NOT_SPECIFIED) { + return factory.createTypeWithNullability(type1, type2.isNullable()); + } else if (type2.getPrecision() == RelDataType.PRECISION_NOT_SPECIFIED) { + return factory.createTypeWithNullability(type2, type1.isNullable()); + } else if (type1.getPrecision() > type2.getPrecision()) { + return factory.createTypeWithNullability(type1, type2.isNullable()); + } else { + return factory.createTypeWithNullability(type2, type1.isNullable()); + } + } + // 1 > '1' will be coerced to 1 > 1. if (SqlTypeUtil.isAtomic(type1) && SqlTypeUtil.isCharacter(type2)) { if (SqlTypeUtil.isTimestamp(type1)) { @@ -581,6 +596,43 @@ private RelDataType getTightestCommonTypeOrThrow( } } + if (SqlTypeUtil.isApproximateNumeric(type1) && SqlTypeUtil.isApproximateNumeric(type2)) { + if (type1.getPrecision() > type2.getPrecision()) { + return factory.createTypeWithNullability(type1, type2.isNullable()); + } else { + return factory.createTypeWithNullability(type2, type1.isNullable()); + } + } + + if (SqlTypeUtil.isApproximateNumeric(type1) && SqlTypeUtil.isExactNumeric(type2)) { + return factory.createTypeWithNullability(type1, type2.isNullable()); + } + + if (SqlTypeUtil.isApproximateNumeric(type2) && SqlTypeUtil.isExactNumeric(type1)) { + return factory.createTypeWithNullability(type2, type1.isNullable()); + } + + if (SqlTypeUtil.isExactNumeric(type1) && SqlTypeUtil.isExactNumeric(type2)) { + if (SqlTypeUtil.isDecimal(type1)) { + // Use max precision + RelDataType result = + factory.createSqlType(type1.getSqlTypeName(), + Math.max(type1.getPrecision(), type2.getPrecision()), type1.getScale()); + return factory.createTypeWithNullability(result, type1.isNullable() || type2.isNullable()); + } else if (SqlTypeUtil.isDecimal(type2)) { + // Use max precision + RelDataType result = + factory.createSqlType(type2.getSqlTypeName(), + Math.max(type1.getPrecision(), type2.getPrecision()), type2.getScale()); + return factory.createTypeWithNullability(result, type1.isNullable() || type2.isNullable()); + } + if (type1.getPrecision() > type2.getPrecision()) { + return factory.createTypeWithNullability(type1, type2.isNullable()); + } else { + return factory.createTypeWithNullability(type2, type1.isNullable()); + } + } + return null; } diff --git a/core/src/main/java/org/apache/calcite/sql/validate/implicit/TypeCoercion.java b/core/src/main/java/org/apache/calcite/sql/validate/implicit/TypeCoercion.java index e3a61764862..2a343cca1b9 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/implicit/TypeCoercion.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/implicit/TypeCoercion.java @@ -93,8 +93,7 @@ public interface TypeCoercion { @Nullable RelDataType type1, @Nullable RelDataType type2); /** - * Determines common type for a comparison operator whose operands are STRING - * type and the other (non STRING) type. + * Determines common type for a comparison operator. */ @Nullable RelDataType commonTypeForBinaryComparison( @Nullable RelDataType type1, @Nullable RelDataType type2); diff --git a/core/src/main/java/org/apache/calcite/sql/validate/implicit/TypeCoercionImpl.java b/core/src/main/java/org/apache/calcite/sql/validate/implicit/TypeCoercionImpl.java index 73de170e5c1..711c8d22dc8 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/implicit/TypeCoercionImpl.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/implicit/TypeCoercionImpl.java @@ -48,7 +48,6 @@ import java.math.BigDecimal; import java.util.AbstractList; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; @@ -291,18 +290,9 @@ protected boolean binaryArithmeticWithStrings( return null; } - RelDataType commonType; - if (SqlTypeUtil.sameNamedType(type1, type2)) { - commonType = factory.leastRestrictive(Arrays.asList(type1, type2)); - } else { - commonType = commonTypeForBinaryComparison(type1, type2); - } + RelDataType commonType = commonTypeForBinaryComparison(type1, type2); for (int i = 2; i < dataTypes.size() && commonType != null; i++) { - if (SqlTypeUtil.sameNamedType(commonType, dataTypes.get(i))) { - commonType = factory.leastRestrictive(Arrays.asList(commonType, dataTypes.get(i))); - } else { - commonType = commonTypeForBinaryComparison(commonType, dataTypes.get(i)); - } + commonType = commonTypeForBinaryComparison(commonType, dataTypes.get(i)); } return commonType; } diff --git a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java index c52851e14e2..fd72b876b0e 100644 --- a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java +++ b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java @@ -319,33 +319,33 @@ private static String toSql(RelNode root, SqlDialect dialect, + "where \"product_id\" > 0\n" + "group by \"product_id\""; final String expectedDefault = "SELECT" - + " SUM(\"shelf_width\") FILTER (WHERE \"net_weight\" > 0 IS TRUE)," + + " SUM(\"shelf_width\") FILTER (WHERE \"net_weight\" > 0E0 IS TRUE)," + " SUM(\"shelf_width\")\n" + "FROM \"foodmart\".\"product\"\n" + "WHERE \"product_id\" > 0\n" + "GROUP BY \"product_id\""; final String expectedBigQuery = "SELECT" - + " SUM(CASE WHEN net_weight > 0 IS TRUE" + + " SUM(CASE WHEN net_weight > 0E0 IS TRUE" + " THEN shelf_width ELSE NULL END), " + "SUM(shelf_width)\n" + "FROM foodmart.product\n" + "WHERE product_id > 0\n" + "GROUP BY product_id"; final String expectedFirebolt = "SELECT" - + " SUM(CASE WHEN \"net_weight\" > 0 IS TRUE" + + " SUM(CASE WHEN \"net_weight\" > 0E0 IS TRUE" + " THEN \"shelf_width\" ELSE NULL END), " + "SUM(\"shelf_width\")\n" + "FROM \"foodmart\".\"product\"\n" + "WHERE \"product_id\" > 0\n" + "GROUP BY \"product_id\""; final String expectedMysql = "SELECT" - + " SUM(CASE WHEN `net_weight` > 0 IS TRUE" + + " SUM(CASE WHEN `net_weight` > 0E0 IS TRUE" + " THEN `shelf_width` ELSE NULL END), SUM(`shelf_width`)\n" + "FROM `foodmart`.`product`\n" + "WHERE `product_id` > 0\n" + "GROUP BY `product_id`"; final String expectedStarRocks = "SELECT" - + " SUM(CASE WHEN `net_weight` > 0 IS TRUE" + + " SUM(CASE WHEN `net_weight` > 0E0 IS TRUE" + " THEN `shelf_width` ELSE NULL END), SUM(`shelf_width`)\n" + "FROM `foodmart`.`product`\n" + "WHERE `product_id` > 0\n" @@ -539,7 +539,7 @@ private static String toSql(RelNode root, SqlDialect dialect, final String expected = "SELECT *\n" + "FROM \"foodmart\".\"product\"\n" + "WHERE (\"product_id\" = 10 OR \"product_id\" <= 5) " - + "AND (80 >= \"shelf_width\" OR \"shelf_width\" > 30)"; + + "AND (CAST(80 AS DOUBLE) >= \"shelf_width\" OR \"shelf_width\" > CAST(30 AS DOUBLE))"; sql(query).ok(expected); } @@ -2097,26 +2097,26 @@ private void checkHavingAliasSameAsColumn(boolean upperAlias) { + " sum(\"gross_weight\") as \"" + alias + "\"\n" + "from \"product\"\n" + "group by \"product_id\"\n" - + "having sum(\"product\".\"gross_weight\") < 200"; + + "having sum(\"product\".\"gross_weight\") < 2.000E2"; // PostgreSQL has isHavingAlias=false, case-sensitive=true final String expectedPostgresql = "SELECT \"product_id\" + 1," + " SUM(\"gross_weight\") AS \"" + alias + "\"\n" + "FROM \"foodmart\".\"product\"\n" + "GROUP BY \"product_id\"\n" - + "HAVING SUM(\"gross_weight\") < 200"; + + "HAVING SUM(\"gross_weight\") < 2.000E2"; // MySQL has isHavingAlias=true, case-sensitive=true final String expectedMysql = "SELECT `product_id` + 1, `" + alias + "`\n" + "FROM (SELECT `product_id`, SUM(`gross_weight`) AS `" + alias + "`\n" + "FROM `foodmart`.`product`\n" + "GROUP BY `product_id`\n" - + "HAVING `" + alias + "` < 200) AS `t1`"; + + "HAVING `" + alias + "` < 2.000E2) AS `t1`"; // BigQuery has isHavingAlias=true, case-sensitive=false final String expectedBigQuery = upperAlias ? "SELECT product_id + 1, GROSS_WEIGHT\n" + "FROM (SELECT product_id, SUM(gross_weight) AS GROSS_WEIGHT\n" + "FROM foodmart.product\n" + "GROUP BY product_id\n" - + "HAVING GROSS_WEIGHT < 200) AS t1" + + "HAVING GROSS_WEIGHT < 2.000E2) AS t1" // Before [CALCITE-3896] was fixed, we got // "HAVING SUM(gross_weight) < 200) AS t1" // which on BigQuery gives you an error about aggregating aggregates @@ -2124,7 +2124,7 @@ private void checkHavingAliasSameAsColumn(boolean upperAlias) { + "FROM (SELECT product_id, SUM(gross_weight) AS gross_weight\n" + "FROM foodmart.product\n" + "GROUP BY product_id\n" - + "HAVING gross_weight < 200) AS t1"; + + "HAVING gross_weight < 2.000E2) AS t1"; sql(query) .withBigQuery().ok(expectedBigQuery) .withPostgresql().ok(expectedPostgresql) @@ -2144,11 +2144,11 @@ private void checkHavingAliasSameAsColumn(boolean upperAlias) { final String expected = "SELECT \"product_id\"\n" + "FROM (SELECT \"product_id\", AVG(\"gross_weight\") AS \"AGW\"\n" + "FROM \"foodmart\".\"product\"\n" - + "WHERE \"net_weight\" < 100\n" + + "WHERE \"net_weight\" < CAST(100 AS DOUBLE)\n" + "GROUP BY \"product_id\"\n" - + "HAVING AVG(\"gross_weight\") > 50) AS \"t2\"\n" + + "HAVING AVG(\"gross_weight\") > CAST(50 AS DOUBLE)) AS \"t2\"\n" + "GROUP BY \"product_id\"\n" - + "HAVING AVG(\"AGW\") > 60"; + + "HAVING AVG(\"AGW\") > 6.00E1"; sql(query).ok(expected); } @@ -5366,21 +5366,21 @@ private void checkLiteral2(String expression, String expected) { + "UNION ALL\n" + "SELECT NULL) END AS `$f0`\n" + "FROM `foodmart`.`product`) AS `t0` ON TRUE\n" - + "WHERE `product`.`net_weight` > `t0`.`$f0`"; + + "WHERE `product`.`net_weight` > CAST(`t0`.`$f0` AS DOUBLE)"; final String expectedPostgresql = "SELECT \"product\".\"product_class_id\" AS \"C\"\n" + "FROM \"foodmart\".\"product\"\n" + "LEFT JOIN (SELECT CASE COUNT(*) WHEN 0 THEN NULL WHEN 1 THEN MIN(\"product_class_id\") ELSE (SELECT CAST(NULL AS INTEGER)\n" + "UNION ALL\n" + "SELECT CAST(NULL AS INTEGER)) END AS \"$f0\"\n" + "FROM \"foodmart\".\"product\") AS \"t0\" ON TRUE\n" - + "WHERE \"product\".\"net_weight\" > \"t0\".\"$f0\""; + + "WHERE \"product\".\"net_weight\" > CAST(\"t0\".\"$f0\" AS DOUBLE PRECISION)"; final String expectedHsqldb = "SELECT product.product_class_id AS C\n" + "FROM foodmart.product\n" + "LEFT JOIN (SELECT CASE COUNT(*) WHEN 0 THEN NULL WHEN 1 THEN MIN(product_class_id) ELSE ((VALUES 0E0)\n" + "UNION ALL\n" + "(VALUES 0E0)) END AS $f0\n" + "FROM foodmart.product) AS t0 ON TRUE\n" - + "WHERE product.net_weight > t0.$f0"; + + "WHERE product.net_weight > CAST(t0.$f0 AS DOUBLE)"; sql(query) .withConfig(c -> c.withExpand(true)) .withMysql().ok(expectedMysql) @@ -6905,7 +6905,7 @@ private void checkLiteral2(String expression, String expected) { + "within group (order by \"net_weight\" desc) filter (where \"net_weight\" > 0)" + "from \"product\" group by \"product_class_id\""; final String expected = "SELECT \"product_class_id\", COLLECT(\"net_weight\") " - + "FILTER (WHERE \"net_weight\" > 0 IS TRUE) " + + "FILTER (WHERE \"net_weight\" > 0E0 IS TRUE) " + "WITHIN GROUP (ORDER BY \"net_weight\" DESC)\n" + "FROM \"foodmart\".\"product\"\n" + "GROUP BY \"product_class_id\""; @@ -8241,7 +8241,7 @@ private void checkLiteral2(String expression, String expected) { final String expected = "SELECT *\n" + "FROM TABLE(DEDUP(CURSOR ((SELECT \"product_id\", \"product_name\"\n" + "FROM \"foodmart\".\"product\"\n" - + "WHERE \"net_weight\" > 100 AND \"product_name\" = 'Hello World')), " + + "WHERE \"net_weight\" > CAST(100 AS DOUBLE) AND \"product_name\" = 'Hello World')), " + "CURSOR ((SELECT \"employee_id\", \"full_name\"\n" + "FROM \"foodmart\".\"employee\"\n" + "GROUP BY \"employee_id\", \"full_name\")), 'NAME'))"; diff --git a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java index 380b543409a..4862a89039e 100644 --- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java +++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java @@ -3112,6 +3112,15 @@ private void checkPushJoinThroughUnionOnRightDoesNotMatchSemiOrAntiJoin(JoinRelT .check(); } + /** Test case for + * [CALCITE-6617] + * TypeCoercion is not applied correctly to comparisons. */ + @Test void testRand() { + final String sql = "SELECT * FROM (SELECT 1, ROUND(RAND()) AS A)\n" + + "WHERE A BETWEEN 1 AND 10 OR A IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)"; + sql(sql).withRule(CoreRules.PROJECT_REDUCE_EXPRESSIONS).check(); + } + /** Test case for * [CALCITE-6481] * Optimize 'VALUES...UNION...VALUES' to a single 'VALUES' the IN-list contains CAST diff --git a/core/src/test/java/org/apache/calcite/test/TCatalogReader.java b/core/src/test/java/org/apache/calcite/test/TCatalogReader.java index afd195211ae..c6ebd03869d 100644 --- a/core/src/test/java/org/apache/calcite/test/TCatalogReader.java +++ b/core/src/test/java/org/apache/calcite/test/TCatalogReader.java @@ -49,7 +49,7 @@ public class TCatalogReader extends MockCatalogReader { t1.addColumn("t1_smallint", f.smallintType); t1.addColumn("t1_int", f.intType); t1.addColumn("t1_bigint", f.bigintType); - t1.addColumn("t1_float", f.floatType); + t1.addColumn("t1_real", f.realType); t1.addColumn("t1_double", f.doubleType); t1.addColumn("t1_decimal", f.decimalType); t1.addColumn("t1_timestamp", f.timestampType); @@ -64,7 +64,7 @@ public class TCatalogReader extends MockCatalogReader { t2.addColumn("t2_smallint", f.smallintType); t2.addColumn("t2_int", f.intType); t2.addColumn("t2_bigint", f.bigintType); - t2.addColumn("t2_float", f.floatType); + t2.addColumn("t2_real", f.realType); t2.addColumn("t2_double", f.doubleType); t2.addColumn("t2_decimal", f.decimalType); t2.addColumn("t2_timestamp", f.timestampType); diff --git a/core/src/test/java/org/apache/calcite/test/TypeCoercionConverterTest.java b/core/src/test/java/org/apache/calcite/test/TypeCoercionConverterTest.java index 41c14ed8d90..228b5690627 100644 --- a/core/src/test/java/org/apache/calcite/test/TypeCoercionConverterTest.java +++ b/core/src/test/java/org/apache/calcite/test/TypeCoercionConverterTest.java @@ -161,14 +161,14 @@ public static void checkActualAndReferenceFiles() { // char decimal float double // char decimal smallint double final String sql = "select t1_int, t1_decimal, t1_smallint, t1_double from t1 " - + "union select t2_varchar20, t2_decimal, t2_float, t2_bigint from t2 " - + "union select t1_varchar20, t1_decimal, t1_float, t1_double from t1 " + + "union select t2_varchar20, t2_decimal, t2_real, t2_bigint from t2 " + + "union select t1_varchar20, t1_decimal, t1_real, t1_double from t1 " + "union select t2_varchar20, t2_decimal, t2_smallint, t2_double from t2"; sql(sql).ok(); } @Test void testInsertQuerySourceCoercion() { - final String sql = "insert into t1 select t2_smallint, t2_int, t2_bigint, t2_float,\n" + final String sql = "insert into t1 select t2_smallint, t2_int, t2_bigint, t2_real,\n" + "t2_double, t2_decimal, t2_int, t2_date, t2_timestamp, t2_varchar20, t2_int from t2"; sql(sql).ok(); } diff --git a/core/src/test/java/org/apache/calcite/test/TypeCoercionTest.java b/core/src/test/java/org/apache/calcite/test/TypeCoercionTest.java index b20c32ebc78..dc7e357e696 100644 --- a/core/src/test/java/org/apache/calcite/test/TypeCoercionTest.java +++ b/core/src/test/java/org/apache/calcite/test/TypeCoercionTest.java @@ -33,6 +33,7 @@ import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.Nullable; import org.junit.jupiter.api.Test; import java.util.List; @@ -118,17 +119,17 @@ private static ImmutableList combine( f.checkCommonType(f.intType, f.bigintType, f.bigintType, true); f.checkCommonType(f.bigintType, f.bigintType, f.bigintType, true); // FLOAT/DOUBLE - f.checkCommonType(f.nullType, f.floatType, f.nullableFloatType, true); + f.checkCommonType(f.nullType, f.realType, f.nullableRealType, true); f.checkCommonType(f.nullType, f.doubleType, f.nullableDoubleType, true); // Use RelDataTypeFactory#leastRestrictive to find the common type; it's not // symmetric but it's ok because precision does not become lower. - f.checkCommonType(f.floatType, f.doubleType, f.floatType, false); - f.checkCommonType(f.floatType, f.floatType, f.floatType, true); + f.checkCommonType(f.realType, f.doubleType, f.doubleType, false); + f.checkCommonType(f.realType, f.realType, f.realType, true); f.checkCommonType(f.doubleType, f.doubleType, f.doubleType, true); // EXACT + FRACTIONAL - f.checkCommonType(f.intType, f.floatType, f.floatType, true); + f.checkCommonType(f.intType, f.realType, f.realType, true); f.checkCommonType(f.intType, f.doubleType, f.doubleType, true); - f.checkCommonType(f.bigintType, f.floatType, f.floatType, true); + f.checkCommonType(f.bigintType, f.realType, f.realType, true); f.checkCommonType(f.bigintType, f.doubleType, f.doubleType, true); // Fixed precision decimal RelDataType decimal54 = @@ -226,12 +227,12 @@ private static ImmutableList combine( sql("select LOCALTIME from (values(true)) union values '1'") .type("RecordType(VARCHAR NOT NULL LOCALTIME) NOT NULL"); sql("select t1_int, t1_decimal, t1_smallint, t1_double from t1 " - + "union select t2_varchar20, t2_decimal, t2_float, t2_bigint from t2 " - + "union select t1_varchar20, t1_decimal, t1_float, t1_double from t1 " + + "union select t2_varchar20, t2_decimal, t2_real, t2_bigint from t2 " + + "union select t1_varchar20, t1_decimal, t1_real, t1_double from t1 " + "union select t2_varchar20, t2_decimal, t2_smallint, t2_double from t2") .type("RecordType(VARCHAR NOT NULL T1_INT," + " DECIMAL(19, 0) NOT NULL T1_DECIMAL," - + " FLOAT NOT NULL T1_SMALLINT," + + " REAL NOT NULL T1_SMALLINT," + " DOUBLE NOT NULL T1_DOUBLE) NOT NULL"); // (int) union (int) union (varchar(20)) sql("select t1_int from t1 " @@ -253,17 +254,17 @@ private static ImmutableList combine( // intersect sql("select t1_int, t1_decimal, t1_smallint, t1_double from t1 " - + "intersect select t2_varchar20, t2_decimal, t2_float, t2_bigint from t2 ") + + "intersect select t2_varchar20, t2_decimal, t2_real, t2_bigint from t2 ") .type("RecordType(VARCHAR NOT NULL T1_INT," + " DECIMAL(19, 0) NOT NULL T1_DECIMAL," - + " FLOAT NOT NULL T1_SMALLINT," + + " REAL NOT NULL T1_SMALLINT," + " DOUBLE NOT NULL T1_DOUBLE) NOT NULL"); // except sql("select t1_int, t1_decimal, t1_smallint, t1_double from t1 " - + "except select t2_varchar20, t2_decimal, t2_float, t2_bigint from t2 ") + + "except select t2_varchar20, t2_decimal, t2_real, t2_bigint from t2 ") .type("RecordType(VARCHAR NOT NULL T1_INT," + " DECIMAL(19, 0) NOT NULL T1_DECIMAL," - + " FLOAT NOT NULL T1_SMALLINT," + + " REAL NOT NULL T1_SMALLINT," + " DOUBLE NOT NULL T1_DOUBLE) NOT NULL"); } @@ -353,11 +354,65 @@ private static ImmutableList combine( .columnType("BOOLEAN NOT NULL"); } + @Test void testComparisonCoercion() { + // NULL + final Fixture f = fixture(); + f.comparisonCommonType(f.nullType, f.nullType, f.nullType); + // BOOLEAN + f.comparisonCommonType(f.nullType, f.booleanType, null); + f.comparisonCommonType(f.booleanType, f.booleanType, f.booleanType); + f.comparisonCommonType(f.intType, f.booleanType, null); + f.comparisonCommonType(f.bigintType, f.booleanType, null); + // INT + f.comparisonCommonType(f.smallintType, f.intType, f.intType); + f.comparisonCommonType(f.smallintType, f.bigintType, f.bigintType); + f.comparisonCommonType(f.intType, f.bigintType, f.bigintType); + f.comparisonCommonType(f.bigintType, f.bigintType, f.bigintType); + // FLOAT/DOUBLE + f.comparisonCommonType(f.realType, f.doubleType, f.doubleType); + f.comparisonCommonType(f.realType, f.realType, f.realType); + f.comparisonCommonType(f.doubleType, f.doubleType, f.doubleType); + // EXACT + FRACTIONAL + f.comparisonCommonType(f.intType, f.realType, f.realType); + f.comparisonCommonType(f.intType, f.doubleType, f.doubleType); + f.comparisonCommonType(f.bigintType, f.realType, f.realType); + f.comparisonCommonType(f.bigintType, f.doubleType, f.doubleType); + // Fixed precision decimal + RelDataType decimal54 = + f.typeFactory.createSqlType(SqlTypeName.DECIMAL, 5, 4); + RelDataType decimal71 = + f.typeFactory.createSqlType(SqlTypeName.DECIMAL, 7, 1); + RelDataType decimal104 = + f.typeFactory.createSqlType(SqlTypeName.DECIMAL, 10, 4); + f.comparisonCommonType(decimal54, decimal71, decimal104); + f.comparisonCommonType(decimal54, f.doubleType, f.doubleType); + f.comparisonCommonType(decimal54, f.intType, decimal104); + // CHAR/VARCHAR + f.comparisonCommonType(f.charType, f.varcharType, f.varcharType); + f.comparisonCommonType(f.intType, f.charType, f.intType); + f.comparisonCommonType(f.doubleType, f.charType, f.doubleType); + // TIMESTAMP + f.comparisonCommonType(f.timestampType, f.timestampType, f.timestampType); + f.comparisonCommonType(f.dateType, f.timestampType, f.timestampType); + f.comparisonCommonType(f.intType, f.timestampType, null); + f.comparisonCommonType(f.varcharType, f.timestampType, f.timestampType); + // generic + f.comparisonCommonType(f.charType, f.mapType(f.intType, f.charType), null); + f.comparisonCommonType(f.arrayType(f.intType), f.recordType(ImmutableList.of()), + null); + f.comparisonCommonType(f.recordType("a", f.intType), + f.recordType("a", f.intType), f.recordType("a", f.intType)); + f.comparisonCommonType(f.recordType("a", f.arrayType(f.intType)), + f.recordType("a", f.arrayType(f.intType)), + f.recordType("a", f.arrayType(f.intType))); + } + + /** Test case for case when expression and COALESCE operator. */ @Test void testCaseWhen() { // coalesce // double int float - sql("select COALESCE(t1_double, t1_int, t1_float) from t1") + sql("select COALESCE(t1_double, t1_int, t1_real) from t1") .type("RecordType(DOUBLE NOT NULL EXPR$0) NOT NULL"); // bigint int decimal sql("select COALESCE(t1_bigint, t1_int, t1_decimal) from t1") @@ -369,13 +424,13 @@ private static ImmutableList combine( sql("select COALESCE(t1_varchar20, t1_timestamp) from t1") .type("RecordType(VARCHAR NOT NULL EXPR$0) NOT NULL"); // null float int - sql("select COALESCE(null, t1_float, t1_int) from t1") - .type("RecordType(FLOAT EXPR$0) NOT NULL"); + sql("select COALESCE(null, t1_real, t1_int) from t1") + .type("RecordType(REAL EXPR$0) NOT NULL"); // null int decimal double sql("select COALESCE(null, t1_int, t1_decimal, t1_double) from t1") .type("RecordType(DOUBLE EXPR$0) NOT NULL"); // null float double varchar - sql("select COALESCE(null, t1_float, t1_double, t1_varchar20) from t1") + sql("select COALESCE(null, t1_real, t1_double, t1_varchar20) from t1") .type("RecordType(VARCHAR EXPR$0) NOT NULL"); // timestamp int varchar sql("select COALESCE(t1_timestamp, t1_int, t1_varchar20) from t1") @@ -404,7 +459,7 @@ private static ImmutableList combine( + "else t2_varchar20 end from t2") .type("RecordType(VARCHAR NOT NULL EXPR$0) NOT NULL"); // float decimal - sql("select case when 1 > 0 then t2_float else t2_decimal end from t2") + sql("select case when 1 > 0 then t2_real else t2_decimal end from t2") .type("RecordType(DOUBLE NOT NULL EXPR$0) NOT NULL"); // bigint decimal sql("select case when 1 > 0 then t2_bigint else t2_decimal end from t2") @@ -459,7 +514,7 @@ private static ImmutableList combine( f.shouldNotCast(checkedType4, SqlTypeFamily.APPROXIMATE_NUMERIC); // FLOAT/REAL - RelDataType checkedType5 = f.floatType; + RelDataType checkedType5 = f.realType; f.checkShouldCast(checkedType5, combine(f.numericTypes, charTypes)); f.shouldCast(checkedType5, SqlTypeFamily.DECIMAL, f.typeFactory.decimalOf(checkedType5)); @@ -581,7 +636,7 @@ private static ImmutableList combine( expr("select t1_smallint||t1_int||t1_double from t1") .columnType("VARCHAR"); // boolean float smallint - expr("select t1_boolean||t1_float||t1_smallint from t1") + expr("select t1_boolean||t1_real||t1_smallint from t1") .columnType("VARCHAR"); // decimal expr("select t1_decimal||t1_varchar20 from t1") @@ -598,7 +653,7 @@ private static ImmutableList combine( + "SMALLINT NOT NULL t1_smallint, " + "INTEGER NOT NULL t1_int, " + "BIGINT NOT NULL t1_bigint, " - + "FLOAT NOT NULL t1_float, " + + "REAL NOT NULL t1_real, " + "DOUBLE NOT NULL t1_double, " + "DECIMAL(19, 0) NOT NULL t1_decimal, " + "TIMESTAMP(0) NOT NULL t1_timestamp, " @@ -606,12 +661,12 @@ private static ImmutableList combine( + "BINARY(1) NOT NULL t1_binary, " + "BOOLEAN NOT NULL t1_boolean) NOT NULL"; - final String sql = "insert into t1 select t2_smallint, t2_int, t2_bigint, t2_float,\n" + final String sql = "insert into t1 select t2_smallint, t2_int, t2_bigint, t2_real,\n" + "t2_double, t2_decimal, t2_int, t2_date, t2_timestamp, t2_varchar20, t2_int from t2"; sql(sql).type(expectRowType); final String sql1 = "insert into ^t1^(t1_varchar20, t1_date, t1_int)\n" - + "select t2_smallint, t2_timestamp, t2_float from t2"; + + "select t2_smallint, t2_timestamp, t2_real from t2"; sql(sql1).fails("(?s).*Column 't1_smallint' has no default value and does not allow NULLs.*"); final String sql2 = "update t1 set t1_varchar20=123, " @@ -647,8 +702,8 @@ static class Fixture { final RelDataType nullableIntType; final RelDataType bigintType; final RelDataType nullableBigintType; - final RelDataType floatType; - final RelDataType nullableFloatType; + final RelDataType realType; + final RelDataType nullableRealType; final RelDataType doubleType; final RelDataType nullableDoubleType; final RelDataType decimalType; @@ -695,8 +750,8 @@ protected Fixture(RelDataTypeFactory typeFactory, nullableIntType = this.typeFactory.createTypeWithNullability(intType, true); bigintType = this.typeFactory.createSqlType(SqlTypeName.BIGINT); nullableBigintType = this.typeFactory.createTypeWithNullability(bigintType, true); - floatType = this.typeFactory.createSqlType(SqlTypeName.FLOAT); - nullableFloatType = this.typeFactory.createTypeWithNullability(floatType, true); + realType = this.typeFactory.createSqlType(SqlTypeName.REAL); + nullableRealType = this.typeFactory.createTypeWithNullability(realType, true); doubleType = this.typeFactory.createSqlType(SqlTypeName.DOUBLE); nullableDoubleType = this.typeFactory.createTypeWithNullability(doubleType, true); decimalType = this.typeFactory.createSqlType(SqlTypeName.DECIMAL); @@ -871,7 +926,7 @@ private static boolean contains(List types, RelDataType type) { return false; } - private String toStringNullable(Object o1) { + private String toStringNullable(@Nullable Object o1) { if (o1 == null) { return "NULL"; } @@ -882,7 +937,7 @@ private String toStringNullable(Object o1) { private void checkCommonType( RelDataType type1, RelDataType type2, - RelDataType expected, + @Nullable RelDataType expected, boolean isSymmetric) { RelDataType result = typeCoercion.getTightestCommonType(type1, type2); assertThat("Expected " + toStringNullable(expected) @@ -901,11 +956,30 @@ private void checkCommonType( } } + private void comparisonCommonType( + RelDataType type1, + RelDataType type2, + @Nullable RelDataType expected) { + RelDataType result = typeCoercion.commonTypeForBinaryComparison(type1, type2); + assertThat("Expected " + toStringNullable(expected) + + " as comparison common type for " + type1 + + " and " + type2 + + ", but found " + toStringNullable(result), + result, + sameInstance(expected)); + RelDataType result1 = typeCoercion.commonTypeForBinaryComparison(type2, type1); + assertThat("Expected " + toStringNullable(expected) + + " as common type for " + type2 + + " and " + type1 + + ", but found " + toStringNullable(result1), + result1, sameInstance(expected)); + } + /** Decision method for finding a wider type. */ private void checkWiderType( RelDataType type1, RelDataType type2, - RelDataType expected, + @Nullable RelDataType expected, boolean stringPromotion, boolean symmetric) { RelDataType result = diff --git a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml index eca85f5e4b7..babaae62013 100644 --- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml +++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml @@ -1566,7 +1566,7 @@ case when cast(ename as double) < 5 then 0.0 ($1, 'abc'), $1, null:VARCHAR(20))):DOUBLE, 5), 0.0E0:DOUBLE, CASE(IS NOT NULL(CAST(CASE(>($1, 'abc'), $1, null:VARCHAR(20))):DOUBLE), CAST(CAST(CASE(>($1, 'abc'), $1, null:VARCHAR(20))):DOUBLE):DOUBLE NOT NULL, 1.0E0:DOUBLE))]) +LogicalProject(T=[CASE(<(CAST(CASE(>($1, 'abc'), $1, null:VARCHAR(20))):DOUBLE, 5.0E0), 0.0E0:DOUBLE, CASE(IS NOT NULL(CAST(CASE(>($1, 'abc'), $1, null:VARCHAR(20))):DOUBLE), CAST(CAST(CASE(>($1, 'abc'), $1, null:VARCHAR(20))):DOUBLE):DOUBLE NOT NULL, 1.0E0:DOUBLE))]) LogicalTableScan(table=[[CATALOG, SALES, EMP]]) ]]> @@ -12192,6 +12192,28 @@ LogicalAggregate(group=[{0}], EXPR$1=[SUM($1)]) LogicalAggregate(group=[{0}], EXPR$1=[SUM($1)]) LogicalProject(ENAME=[$1], MGR=[$3]) LogicalTableScan(table=[[CATALOG, SALES, EMP]]) +]]> + + + + + + + + =($1, CAST(1):DOUBLE NOT NULL), <=($1, CAST(10):DOUBLE NOT NULL)), =($1, CAST(1):DOUBLE NOT NULL), =($1, CAST(2):DOUBLE NOT NULL), =($1, CAST(3):DOUBLE NOT NULL), =($1, CAST(4):DOUBLE NOT NULL), =($1, CAST(5):DOUBLE NOT NULL), =($1, CAST(6):DOUBLE NOT NULL), =($1, CAST(7):DOUBLE NOT NULL), =($1, CAST(8):DOUBLE NOT NULL), =($1, CAST(9):DOUBLE NOT NULL), =($1, CAST(10):DOUBLE NOT NULL))]) + LogicalProject(EXPR$0=[1], A=[ROUND(RAND())]) + LogicalValues(tuples=[[{ 0 }]]) +]]> + + + =($1, CAST(1):DOUBLE NOT NULL), <=($1, CAST(10):DOUBLE NOT NULL)), =($1, CAST(1):DOUBLE NOT NULL), =($1, CAST(2):DOUBLE NOT NULL), =($1, CAST(3):DOUBLE NOT NULL), =($1, CAST(4):DOUBLE NOT NULL), =($1, CAST(5):DOUBLE NOT NULL), =($1, CAST(6):DOUBLE NOT NULL), =($1, CAST(7):DOUBLE NOT NULL), =($1, CAST(8):DOUBLE NOT NULL), =($1, CAST(9):DOUBLE NOT NULL), =($1, CAST(10):DOUBLE NOT NULL))]) + LogicalProject(EXPR$0=[1], A=[ROUND(RAND())]) + LogicalValues(tuples=[[{ 0 }]]) ]]> @@ -13080,14 +13102,14 @@ LogicalProject($0=[$3], $1=[$4]) ($5, 100.0:DECIMAL(4, 1))]) + LogicalFilter(condition=[>(CAST($5):DECIMAL(10, 1) NOT NULL, 100.0)]) LogicalTableScan(table=[[CATALOG, SALES, EMP]]) ]]> ($t10, $t12)], proj#0..8=[{exprs}], $condition=[$t13]) + LogicalCalc(expr#0..8=[{inputs}], expr#9=[10:BIGINT], expr#10=[*($t5, $t9)], expr#11=[true], expr#12=[Reinterpret($t10, $t11)], expr#13=[Reinterpret($t12)], expr#14=[100.0:DECIMAL(10, 1)], expr#15=[Reinterpret($t14)], expr#16=[>($t13, $t15)], proj#0..8=[{exprs}], $condition=[$t16]) LogicalTableScan(table=[[CATALOG, SALES, EMP]]) ]]> diff --git a/core/src/test/resources/org/apache/calcite/test/TypeCoercionConverterTest.xml b/core/src/test/resources/org/apache/calcite/test/TypeCoercionConverterTest.xml index c5476150a70..4253f0b7e1e 100644 --- a/core/src/test/resources/org/apache/calcite/test/TypeCoercionConverterTest.xml +++ b/core/src/test/resources/org/apache/calcite/test/TypeCoercionConverterTest.xml @@ -136,13 +136,13 @@ LogicalProject(F0=[null:BOOLEAN], F1=[CAST(true):BOOLEAN], F2=[null:BOOLEAN], F3 - ($2, 0)]) + LogicalProject(t1_varchar20=[CAST($1):VARCHAR(20) NOT NULL], t1_smallint=[CAST($2):SMALLINT NOT NULL], t1_int=[CAST($3):INTEGER NOT NULL], t1_bigint=[CAST($4):BIGINT NOT NULL], t1_real=[CAST($5):REAL NOT NULL], t1_double=[CAST($6):DOUBLE NOT NULL], t1_decimal=[CAST($2):DECIMAL(19, 0) NOT NULL], t1_timestamp=[CAST($8):TIMESTAMP(0) NOT NULL], t1_date=[CAST($7):DATE NOT NULL], t1_binary=[CAST($0):BINARY(1) NOT NULL], t1_boolean=[<>($2, 0)]) LogicalTableScan(table=[[CATALOG, SALES, T2]]) ]]> @@ -211,7 +211,7 @@ LogicalProject(F0=[null:BOOLEAN], F1=[CAST(false):BOOLEAN], F2=[null:BOOLEAN], F - + diff --git a/core/src/test/resources/sql/agg.iq b/core/src/test/resources/sql/agg.iq index d109a763f5b..733ae7a9c0c 100644 --- a/core/src/test/resources/sql/agg.iq +++ b/core/src/test/resources/sql/agg.iq @@ -3120,7 +3120,7 @@ group by dept.deptno; !ok EnumerableAggregate(group=[{0}], S=[COLLECT($1) WITHIN GROUP ([1 DESC])], S1=[COLLECT($1) WITHIN GROUP ([2])], S2=[COLLECT($1) WITHIN GROUP ([1]) FILTER $3]) - EnumerableCalc(expr#0..3=[{inputs}], expr#4=[1], expr#5=[2000], expr#6=[>($t2, $t5)], expr#7=[IS TRUE($t6)], DEPTNO=[$t0], SAL=[$t2], $f2=[$t4], $f3=[$t7]) + EnumerableCalc(expr#0..3=[{inputs}], expr#4=[1], expr#5=[CAST($t2):DECIMAL(10, 2)], expr#6=[2000.00:DECIMAL(10, 2)], expr#7=[>($t5, $t6)], expr#8=[IS TRUE($t7)], DEPTNO=[$t0], SAL=[$t2], $f2=[$t4], $f3=[$t8]) EnumerableHashJoin(condition=[=($0, $3)], joinType=[inner]) EnumerableCalc(expr#0..2=[{inputs}], DEPTNO=[$t0]) EnumerableTableScan(table=[[scott, DEPT]]) diff --git a/core/src/test/resources/sql/cast.iq b/core/src/test/resources/sql/cast.iq index 18198eaf82f..950bd6c731a 100644 --- a/core/src/test/resources/sql/cast.iq +++ b/core/src/test/resources/sql/cast.iq @@ -1615,4 +1615,47 @@ values cast(cast(156.6 as real) as decimal(3, -1)); !ok +# [CALCITE-6617] TypeCoercion is not applied correctly to comparisons +VALUES (1, 2, 3), + (CAST(5E0 AS REAL), 5E0, NULL); ++--------+--------+--------+ +| EXPR$0 | EXPR$1 | EXPR$2 | ++--------+--------+--------+ +| 1.0 | 2.0 | 3 | +| 5.0 | 5.0 | | ++--------+--------+--------+ +(2 rows) + +!ok + +# [CALCITE-6617] TypeCoercion is not applied correctly to comparisons +# also occurs when simplifying an expression that compares DOUBLE to INTEGER +WITH emp2 AS (SELECT CAST(sal AS DOUBLE) as x FROM emp) +SELECT COUNT(*) AS c +FROM emp2 +WHERE x BETWEEN 1 AND 4 + OR x IN (1, 2, 3, 4); ++---+ +| C | ++---+ +| 0 | ++---+ +(1 row) + +!ok + +WITH emp2 AS (SELECT CAST(sal AS DOUBLE) as x FROM emp) +SELECT COUNT(*) AS c +FROM emp2 +WHERE x BETWEEN 2 AND 4 + OR x IN (1, 2, 4, 800); ++---+ +| C | ++---+ +| 1 | ++---+ +(1 row) + +!ok + # End cast.iq diff --git a/core/src/test/resources/sql/sub-query.iq b/core/src/test/resources/sql/sub-query.iq index 7bfedac3a85..ace96e5ec69 100644 --- a/core/src/test/resources/sql/sub-query.iq +++ b/core/src/test/resources/sql/sub-query.iq @@ -2176,8 +2176,8 @@ where sal + 100 not in ( !ok EnumerableAggregate(group=[{}], C=[COUNT()]) - EnumerableCalc(expr#0..7=[{inputs}], expr#8=[0], expr#9=[=($t1, $t8)], expr#10=[IS NULL($t0)], expr#11=[IS NOT NULL($t6)], expr#12=[<($t2, $t1)], expr#13=[OR($t10, $t11, $t12)], expr#14=[IS NOT TRUE($t13)], expr#15=[OR($t9, $t14)], proj#0..7=[{exprs}], $condition=[$t15]) - EnumerableMergeJoin(condition=[AND(=($3, $5), =($4, $7))], joinType=[left]) + EnumerableCalc(expr#0..7=[{inputs}], expr#8=[0], expr#9=[=($t1, $t8)], expr#10=[IS NULL($t0)], expr#11=[IS NOT NULL($t7)], expr#12=[<($t2, $t1)], expr#13=[OR($t10, $t11, $t12)], expr#14=[IS NOT TRUE($t13)], expr#15=[OR($t9, $t14)], proj#0..7=[{exprs}], $condition=[$t15]) + EnumerableMergeJoin(condition=[AND(=($3, $5), =($4, $6))], joinType=[left]) EnumerableSort(sort0=[$3], sort1=[$4], dir0=[ASC], dir1=[ASC]) EnumerableCalc(expr#0..6=[{inputs}], expr#7=[100], expr#8=[+($t2, $t7)], expr#9=[CAST($t1):VARCHAR(14)], SAL=[$t2], c=[$t4], ck=[$t5], $f5=[$t8], ENAME0=[$t9]) EnumerableMergeJoin(condition=[=($3, $6)], joinType=[left]) @@ -2187,8 +2187,8 @@ EnumerableAggregate(group=[{}], C=[COUNT()]) EnumerableSort(sort0=[$2], dir0=[ASC]) EnumerableCalc(expr#0..2=[{inputs}], expr#3=[1:BIGINT], expr#4=[IS NOT NULL($t1)], c=[$t3], ck=[$t3], DNAME=[$t1], $condition=[$t4]) EnumerableTableScan(table=[[scott, DEPT]]) - EnumerableSort(sort0=[$0], sort1=[$2], dir0=[ASC], dir1=[ASC]) - EnumerableCalc(expr#0..2=[{inputs}], expr#3=[true], expr#4=[IS NOT NULL($t1)], DEPTNO=[$t0], i=[$t3], DNAME=[$t1], $condition=[$t4]) + EnumerableSort(sort0=[$0], sort1=[$1], dir0=[ASC], dir1=[ASC]) + EnumerableCalc(expr#0..2=[{inputs}], expr#3=[CAST($t0):DECIMAL(13, 2) NOT NULL], expr#4=[true], expr#5=[IS NOT NULL($t1)], EXPR$0=[$t3], DNAME=[$t1], i=[$t4], $condition=[$t5]) EnumerableTableScan(table=[[scott, DEPT]]) !plan @@ -2894,7 +2894,7 @@ EnumerableCalc(expr#0..1=[{inputs}], expr#2=[IS NULL($t1)], DEPTNO=[$t0], $condi EnumerableAggregate(group=[{0}]) EnumerableCalc(expr#0..2=[{inputs}], expr#3=[true], expr#4=[1], expr#5=[>($t2, $t4)], i=[$t3], $condition=[$t5]) EnumerableAggregate(group=[{5, 7}], c=[COUNT()]) - EnumerableCalc(expr#0..7=[{inputs}], expr#8=[CAST($t5):DECIMAL(12, 2)], expr#9=[3000.00:DECIMAL(12, 2)], expr#10=[=($t8, $t9)], expr#11=[IS NOT NULL($t7)], expr#12=[AND($t10, $t11)], proj#0..7=[{exprs}], $condition=[$t12]) + EnumerableCalc(expr#0..7=[{inputs}], expr#8=[CAST($t5):DECIMAL(10, 2)], expr#9=[3000.00:DECIMAL(10, 2)], expr#10=[=($t8, $t9)], expr#11=[IS NOT NULL($t7)], expr#12=[AND($t10, $t11)], proj#0..7=[{exprs}], $condition=[$t12]) EnumerableTableScan(table=[[scott, EMP]]) !plan @@ -2920,7 +2920,7 @@ EnumerableCalc(expr#0..1=[{inputs}], expr#2=[IS NULL($t1)], DEPTNO=[$t0], U=[$t2 EnumerableAggregate(group=[{0}]) EnumerableCalc(expr#0..2=[{inputs}], expr#3=[true], expr#4=[1], expr#5=[>($t2, $t4)], i=[$t3], $condition=[$t5]) EnumerableAggregate(group=[{5, 7}], c=[COUNT()]) - EnumerableCalc(expr#0..7=[{inputs}], expr#8=[CAST($t5):DECIMAL(12, 2)], expr#9=[3000.00:DECIMAL(12, 2)], expr#10=[=($t8, $t9)], expr#11=[IS NOT NULL($t7)], expr#12=[AND($t10, $t11)], proj#0..7=[{exprs}], $condition=[$t12]) + EnumerableCalc(expr#0..7=[{inputs}], expr#8=[CAST($t5):DECIMAL(10, 2)], expr#9=[3000.00:DECIMAL(10, 2)], expr#10=[=($t8, $t9)], expr#11=[IS NOT NULL($t7)], expr#12=[AND($t10, $t11)], proj#0..7=[{exprs}], $condition=[$t12]) EnumerableTableScan(table=[[scott, EMP]]) !plan @@ -2946,7 +2946,7 @@ EnumerableCalc(expr#0..1=[{inputs}], expr#2=[IS NOT NULL($t1)], DEPTNO=[$t0], U= EnumerableAggregate(group=[{0}]) EnumerableCalc(expr#0..2=[{inputs}], expr#3=[true], expr#4=[1], expr#5=[>($t2, $t4)], i=[$t3], $condition=[$t5]) EnumerableAggregate(group=[{5, 7}], c=[COUNT()]) - EnumerableCalc(expr#0..7=[{inputs}], expr#8=[CAST($t5):DECIMAL(12, 2)], expr#9=[3000.00:DECIMAL(12, 2)], expr#10=[=($t8, $t9)], expr#11=[IS NOT NULL($t7)], expr#12=[AND($t10, $t11)], proj#0..7=[{exprs}], $condition=[$t12]) + EnumerableCalc(expr#0..7=[{inputs}], expr#8=[CAST($t5):DECIMAL(10, 2)], expr#9=[3000.00:DECIMAL(10, 2)], expr#10=[=($t8, $t9)], expr#11=[IS NOT NULL($t7)], expr#12=[AND($t10, $t11)], proj#0..7=[{exprs}], $condition=[$t12]) EnumerableTableScan(table=[[scott, EMP]]) !plan @@ -3290,7 +3290,7 @@ select *, (comm <> 300 and comm <> 500 and comm <> null) as i from "scott".emp; (14 rows) !ok -EnumerableCalc(expr#0..7=[{inputs}], expr#8=[CAST($t6):DECIMAL(12, 2)], expr#9=[Sarg[(-∞..300.00:DECIMAL(12, 2)), (300.00:DECIMAL(12, 2)..500.00:DECIMAL(12, 2)), (500.00:DECIMAL(12, 2)..+∞)]:DECIMAL(12, 2)], expr#10=[SEARCH($t8, $t9)], expr#11=[null:BOOLEAN], expr#12=[AND($t10, $t11)], proj#0..7=[{exprs}], I=[$t12]) +EnumerableCalc(expr#0..7=[{inputs}], expr#8=[CAST($t6):DECIMAL(10, 2)], expr#9=[Sarg[(-∞..300.00:DECIMAL(10, 2)), (300.00:DECIMAL(10, 2)..500.00:DECIMAL(10, 2)), (500.00:DECIMAL(10, 2)..+∞)]:DECIMAL(10, 2)], expr#10=[SEARCH($t8, $t9)], expr#11=[null:BOOLEAN], expr#12=[AND($t10, $t11)], proj#0..7=[{exprs}], I=[$t12]) EnumerableTableScan(table=[[scott, EMP]]) !plan @@ -3927,16 +3927,23 @@ select comm, comm in (500, 300, 0) from emp; EnumerableCalc(expr#0..5=[{inputs}], expr#6=[IS NOT NULL($t5)], expr#7=[0:BIGINT], expr#8=[<>($t1, $t7)], expr#9=[AND($t6, $t8)], expr#10=[IS NULL($t3)], expr#11=[<($t2, $t1)], expr#12=[OR($t10, $t11)], expr#13=[null:BOOLEAN], expr#14=[IS NULL($t5)], expr#15=[AND($t12, $t13, $t8, $t14)], expr#16=[OR($t9, $t15)], COMM=[$t0], EXPR$1=[$t16]) EnumerableMergeJoin(condition=[=($3, $4)], joinType=[left]) EnumerableSort(sort0=[$3], dir0=[ASC]) - EnumerableCalc(expr#0..3=[{inputs}], COMM=[$t1], $f0=[$t2], $f1=[$t3], COMM0=[$t1]) + EnumerableCalc(expr#0..3=[{inputs}], expr#4=[CAST($t1):DECIMAL(10, 2)], COMM=[$t1], $f0=[$t2], $f1=[$t3], COMM0=[$t4]) EnumerableNestedLoopJoin(condition=[true], joinType=[inner]) EnumerableCalc(expr#0..7=[{inputs}], EMPNO=[$t0], COMM=[$t6]) EnumerableTableScan(table=[[scott, EMP]]) EnumerableCalc(expr#0=[{inputs}], $f0=[$t0], $f00=[$t0]) EnumerableAggregate(group=[{}], agg#0=[COUNT()]) - EnumerableValues(tuples=[[{ 500.00 }, { 300.00 }, { 0.00 }]]) + EnumerableUnion(all=[true]) + EnumerableValues(tuples=[[{ 500.00 }]]) + EnumerableValues(tuples=[[{ 300.00 }]]) + EnumerableValues(tuples=[[{ 0.00 }]]) EnumerableSort(sort0=[$0], dir0=[ASC]) - EnumerableCalc(expr#0=[{inputs}], expr#1=[true], proj#0..1=[{exprs}]) - EnumerableValues(tuples=[[{ 500.00 }, { 300.00 }, { 0.00 }]]) + EnumerableAggregate(group=[{0}], agg#0=[MIN($1)]) + EnumerableCalc(expr#0=[{inputs}], expr#1=[true], proj#0..1=[{exprs}]) + EnumerableUnion(all=[true]) + EnumerableValues(tuples=[[{ 500.00 }]]) + EnumerableValues(tuples=[[{ 300.00 }]]) + EnumerableValues(tuples=[[{ 0.00 }]]) !plan # Test LHS is nullable and RHS is nullable @@ -3967,15 +3974,24 @@ select comm, comm in (500, 300, 0, null) from emp; EnumerableCalc(expr#0..5=[{inputs}], expr#6=[IS NOT NULL($t5)], expr#7=[0:BIGINT], expr#8=[<>($t1, $t7)], expr#9=[AND($t6, $t8)], expr#10=[IS NULL($t3)], expr#11=[<($t2, $t1)], expr#12=[OR($t10, $t11)], expr#13=[null:BOOLEAN], expr#14=[IS NULL($t5)], expr#15=[AND($t12, $t13, $t8, $t14)], expr#16=[OR($t9, $t15)], COMM=[$t0], EXPR$1=[$t16]) EnumerableMergeJoin(condition=[=($3, $4)], joinType=[left]) EnumerableSort(sort0=[$3], dir0=[ASC]) - EnumerableCalc(expr#0..3=[{inputs}], COMM=[$t1], $f0=[$t2], $f1=[$t3], COMM0=[$t1]) + EnumerableCalc(expr#0..3=[{inputs}], expr#4=[CAST($t1):DECIMAL(12, 2)], COMM=[$t1], $f0=[$t2], $f1=[$t3], COMM0=[$t4]) EnumerableNestedLoopJoin(condition=[true], joinType=[inner]) EnumerableCalc(expr#0..7=[{inputs}], EMPNO=[$t0], COMM=[$t6]) EnumerableTableScan(table=[[scott, EMP]]) EnumerableAggregate(group=[{}], agg#0=[COUNT()], agg#1=[COUNT($0)]) - EnumerableValues(tuples=[[{ 500.00 }, { 300.00 }, { 0.00 }, { null }]]) + EnumerableUnion(all=[true]) + EnumerableValues(tuples=[[{ 500.00 }]]) + EnumerableValues(tuples=[[{ 300.00 }]]) + EnumerableValues(tuples=[[{ 0.00 }]]) + EnumerableValues(tuples=[[{ null }]]) EnumerableSort(sort0=[$0], dir0=[ASC]) - EnumerableCalc(expr#0=[{inputs}], expr#1=[true], proj#0..1=[{exprs}]) - EnumerableValues(tuples=[[{ 500.00 }, { 300.00 }, { 0.00 }, { null }]]) + EnumerableAggregate(group=[{0}], agg#0=[MIN($1)]) + EnumerableCalc(expr#0=[{inputs}], expr#1=[true], proj#0..1=[{exprs}]) + EnumerableUnion(all=[true]) + EnumerableValues(tuples=[[{ 500.00 }]]) + EnumerableValues(tuples=[[{ 300.00 }]]) + EnumerableValues(tuples=[[{ 0.00 }]]) + EnumerableValues(tuples=[[{ null }]]) !plan # Test LHS is (not nullable, not nullable) and RHS is (not nullable, not nullable) @@ -4046,16 +4062,23 @@ select comm, (comm, comm) in ((500, 500), (300, 300), (0, 0)) from emp; EnumerableCalc(expr#0..7=[{inputs}], expr#8=[IS NOT NULL($t7)], expr#9=[0:BIGINT], expr#10=[<>($t1, $t9)], expr#11=[AND($t8, $t10)], expr#12=[IS NULL($t3)], expr#13=[IS NULL($t4)], expr#14=[<($t2, $t1)], expr#15=[OR($t12, $t13, $t14)], expr#16=[null:BOOLEAN], expr#17=[IS NULL($t7)], expr#18=[AND($t15, $t16, $t10, $t17)], expr#19=[OR($t11, $t18)], COMM=[$t0], EXPR$1=[$t19]) EnumerableMergeJoin(condition=[AND(=($3, $5), =($4, $6))], joinType=[left]) EnumerableSort(sort0=[$3], sort1=[$4], dir0=[ASC], dir1=[ASC]) - EnumerableCalc(expr#0..3=[{inputs}], COMM=[$t1], $f0=[$t2], $f1=[$t3], COMM0=[$t1], COMM1=[$t1]) + EnumerableCalc(expr#0..3=[{inputs}], expr#4=[CAST($t1):DECIMAL(10, 2)], COMM=[$t1], $f0=[$t2], $f1=[$t3], COMM0=[$t4], COMM1=[$t4]) EnumerableNestedLoopJoin(condition=[true], joinType=[inner]) EnumerableCalc(expr#0..7=[{inputs}], EMPNO=[$t0], COMM=[$t6]) EnumerableTableScan(table=[[scott, EMP]]) EnumerableCalc(expr#0=[{inputs}], $f0=[$t0], $f00=[$t0]) EnumerableAggregate(group=[{}], agg#0=[COUNT()]) - EnumerableValues(tuples=[[{ 500.00, 500.00 }, { 300.00, 300.00 }, { 0.00, 0.00 }]]) + EnumerableUnion(all=[true]) + EnumerableValues(tuples=[[{ 500.00, 500.00 }]]) + EnumerableValues(tuples=[[{ 300.00, 300.00 }]]) + EnumerableValues(tuples=[[{ 0.00, 0.00 }]]) EnumerableSort(sort0=[$0], sort1=[$1], dir0=[ASC], dir1=[ASC]) - EnumerableCalc(expr#0..1=[{inputs}], expr#2=[true], proj#0..2=[{exprs}]) - EnumerableValues(tuples=[[{ 500.00, 500.00 }, { 300.00, 300.00 }, { 0.00, 0.00 }]]) + EnumerableAggregate(group=[{0, 1}], agg#0=[MIN($2)]) + EnumerableCalc(expr#0..1=[{inputs}], expr#2=[true], proj#0..2=[{exprs}]) + EnumerableUnion(all=[true]) + EnumerableValues(tuples=[[{ 500.00, 500.00 }]]) + EnumerableValues(tuples=[[{ 300.00, 300.00 }]]) + EnumerableValues(tuples=[[{ 0.00, 0.00 }]]) !plan # Test LHS is (nullable, nullable) and RHS is (nullable, nullable) @@ -4086,15 +4109,24 @@ select comm, (comm, comm) in ((500, 500), (300, 300), (0, 0), (null , null)) fro EnumerableCalc(expr#0..7=[{inputs}], expr#8=[IS NOT NULL($t7)], expr#9=[0:BIGINT], expr#10=[<>($t1, $t9)], expr#11=[AND($t8, $t10)], expr#12=[IS NULL($t3)], expr#13=[IS NULL($t4)], expr#14=[<($t2, $t1)], expr#15=[OR($t12, $t13, $t14)], expr#16=[null:BOOLEAN], expr#17=[IS NULL($t7)], expr#18=[AND($t15, $t16, $t10, $t17)], expr#19=[OR($t11, $t18)], COMM=[$t0], EXPR$1=[$t19]) EnumerableMergeJoin(condition=[AND(=($3, $5), =($4, $6))], joinType=[left]) EnumerableSort(sort0=[$3], sort1=[$4], dir0=[ASC], dir1=[ASC]) - EnumerableCalc(expr#0..3=[{inputs}], COMM=[$t1], $f0=[$t2], $f1=[$t3], COMM0=[$t1], COMM1=[$t1]) + EnumerableCalc(expr#0..3=[{inputs}], expr#4=[CAST($t1):DECIMAL(10, 2)], COMM=[$t1], $f0=[$t2], $f1=[$t3], COMM0=[$t4], COMM1=[$t4]) EnumerableNestedLoopJoin(condition=[true], joinType=[inner]) EnumerableCalc(expr#0..7=[{inputs}], EMPNO=[$t0], COMM=[$t6]) EnumerableTableScan(table=[[scott, EMP]]) EnumerableAggregate(group=[{}], agg#0=[COUNT()], agg#1=[COUNT($0, $1)]) - EnumerableValues(tuples=[[{ 500.00, 500.00 }, { 300.00, 300.00 }, { 0.00, 0.00 }, { null, null }]]) + EnumerableUnion(all=[true]) + EnumerableValues(tuples=[[{ 500.00, 500.00 }]]) + EnumerableValues(tuples=[[{ 300.00, 300.00 }]]) + EnumerableValues(tuples=[[{ 0.00, 0.00 }]]) + EnumerableValues(tuples=[[{ null, null }]]) EnumerableSort(sort0=[$0], sort1=[$1], dir0=[ASC], dir1=[ASC]) - EnumerableCalc(expr#0..1=[{inputs}], expr#2=[true], proj#0..2=[{exprs}]) - EnumerableValues(tuples=[[{ 500.00, 500.00 }, { 300.00, 300.00 }, { 0.00, 0.00 }, { null, null }]]) + EnumerableAggregate(group=[{0, 1}], agg#0=[MIN($2)]) + EnumerableCalc(expr#0..1=[{inputs}], expr#2=[true], proj#0..2=[{exprs}]) + EnumerableUnion(all=[true]) + EnumerableValues(tuples=[[{ 500.00, 500.00 }]]) + EnumerableValues(tuples=[[{ 300.00, 300.00 }]]) + EnumerableValues(tuples=[[{ 0.00, 0.00 }]]) + EnumerableValues(tuples=[[{ null, null }]]) !plan # End sub-query.iq diff --git a/druid/src/test/java/org/apache/calcite/test/DruidAdapter2IT.java b/druid/src/test/java/org/apache/calcite/test/DruidAdapter2IT.java index a44add80940..384d98409b6 100644 --- a/druid/src/test/java/org/apache/calcite/test/DruidAdapter2IT.java +++ b/druid/src/test/java/org/apache/calcite/test/DruidAdapter2IT.java @@ -2374,14 +2374,14 @@ private void checkGroupBySingleSortLimit(boolean approx) { "PLAN=EnumerableInterpreter\n" + " DruidQuery(table=[[foodmart, foodmart]], " + "intervals=[[1900-01-09T00:00:00.000Z/2992-01-10T00:00:00.000Z]], filter=[>" - + "($91, 10)], projects=[[$90, IS TRUE(>($91, 10))]], groups=[{}], aggs=[[SUM($0)" + + "($91, 10.0E0)], projects=[[$90, IS TRUE(>($91, 10.0E0))]], groups=[{}], aggs=[[SUM($0)" + "]])"; sql(sql) .explainContains(expectedSubExplain) .queryContains( new DruidChecker("\"queryType\":\"timeseries\"", "\"filter\":{\"type\":\"bound\"," - + "\"dimension\":\"store_cost\",\"lower\":\"10\",\"lowerStrict\":true," + + "\"dimension\":\"store_cost\",\"lower\":\"10.0\",\"lowerStrict\":true," + "\"ordering\":\"numeric\"}")) .returnsUnordered("EXPR$0=25.06"); } @@ -2392,13 +2392,13 @@ private void checkGroupBySingleSortLimit(boolean approx) { String expectedSubExplain = "PLAN=" + "EnumerableCalc(expr#0..1=[{inputs}], EXPR$0=[$t1], product_id=[$t0])\n" + " EnumerableInterpreter\n" - + " DruidQuery(table=[[foodmart, foodmart]], intervals=[[1900-01-09T00:00:00.000Z/2992-01-10T00:00:00.000Z]], filter=[AND(>(CAST($1):INTEGER, 1553), >($91, 5))], projects=[[$1, $90]], groups=[{0}], aggs=[[SUM($1)]])"; + + " DruidQuery(table=[[foodmart, foodmart]], intervals=[[1900-01-09T00:00:00.000Z/2992-01-10T00:00:00.000Z]], filter=[AND(>(CAST($1):INTEGER, 1553), >($91, 5.0E0))], projects=[[$1, $90]], groups=[{0}], aggs=[[SUM($1)]])"; CalciteAssert.AssertQuery q = sql(sql) .explainContains(expectedSubExplain) .queryContains( new DruidChecker("\"queryType\":\"groupBy\"", "{\"type\":\"bound\"," - + "\"dimension\":\"store_cost\",\"lower\":\"5\",\"lowerStrict\":true," + + "\"dimension\":\"store_cost\",\"lower\":\"5.0\",\"lowerStrict\":true," + "\"ordering\":\"numeric\"}")); q.returnsUnordered("EXPR$0=10.16; product_id=1554", "EXPR$0=45.05; product_id=1556", @@ -2413,7 +2413,7 @@ private void checkGroupBySingleSortLimit(boolean approx) { sql(sql) .queryContains( new DruidChecker("\"queryType\":\"groupBy\"", "{\"type\":\"bound\"," - + "\"dimension\":\"store_cost\",\"lower\":\"5\",\"lowerStrict\":true," + + "\"dimension\":\"store_cost\",\"lower\":\"5.0\",\"lowerStrict\":true," + "\"ordering\":\"numeric\"}")) .returnsUnordered("EXPR$0=10.6; product_id=1556", "EXPR$0=10.6; product_id=1556", @@ -3150,7 +3150,7 @@ private void testCountWithApproxDistinct(boolean approx, String sql, + "<= ((floor(\\'store_sales\\') * 25) + 2))'}"; final String likeExpressionFilter = "{'type':'expression','expression':'like(\\'product_id\\'"; final String likeExpressionFilter2 = "1%"; - final String simpleBound = "{'type':'bound','dimension':'store_cost','lower':'1'," + final String simpleBound = "{'type':'bound','dimension':'store_cost','lower':'1.0'," + "'lowerStrict':true,'ordering':'numeric'}"; final String timeSimpleFilter = "{'type':'bound','dimension':'__time','upper':'1997-01-02T00:00:00.000Z'," @@ -3169,7 +3169,7 @@ private void testCountWithApproxDistinct(boolean approx, String sql, final String plan = "PLAN=EnumerableInterpreter\n" + " DruidQuery(table=[[foodmart, foodmart]], intervals=[[1900-01-09T00:00:00.000Z/" + "2992-01-10T00:00:00.000Z]], filter=[AND(<=(/(+(CAST($1):INTEGER, $90), " - + "-($91, 5)), +(*(FLOOR($90), 25), 2)), >($90, 0), LIKE($1, '1%'), >($91, 1), " + + "-($91, 5)), +(*(FLOOR($90), 25), 2)), >($90, 0.0E0), LIKE($1, '1%'), >($91, 1.0E0), " + "<($0, 1997-01-02 00:00:00), =(EXTRACT(FLAG(MONTH), $0), 1), " + "=(EXTRACT(FLAG(DAY), $0), 1), =(+(/(EXTRACT(FLAG(MONTH), $0), 4), 1), 1))], " + "groups=[{}], aggs=[[COUNT()]])"; @@ -3194,7 +3194,7 @@ private void testCountWithApproxDistinct(boolean approx, String sql, + " AND ceil(\"timestamp\" TO SECOND) > CAST('1997-01-01' AS TIMESTAMP) "; final String plan = "PLAN=EnumerableInterpreter\n" + " DruidQuery(table=[[foodmart, foodmart]], intervals=[[1997-01-01T00:00:00.001Z/" - + "1997-01-04T00:00:00.001Z]], filter=[>(CEIL($90), 1)], groups=[{}], aggs=[[COUNT()]])"; + + "1997-01-04T00:00:00.001Z]], filter=[>(CEIL($90), 1.0E0)], groups=[{}], aggs=[[COUNT()]])"; sql(sql) .explainContains(plan) .returnsOrdered("EXPR$0=408"); @@ -3272,7 +3272,7 @@ private void testCountWithApproxDistinct(boolean approx, String sql, sql(sql) .queryContains( new DruidChecker("\"queryType\":\"groupBy\"", "{\"type\":\"bound\"," - + "\"dimension\":\"store_cost\",\"lower\":\"5\",\"lowerStrict\":true," + + "\"dimension\":\"store_cost\",\"lower\":\"5.0\",\"lowerStrict\":true," + "\"ordering\":\"numeric\"}")) .runs(); } @@ -3355,7 +3355,7 @@ private void testCountWithApproxDistinct(boolean approx, String sql, .explainContains("PLAN=EnumerableInterpreter\n" + " BindableSort(sort0=[$2], dir0=[ASC], fetch=[3])\n" + " DruidQuery(table=[[foodmart, foodmart]], intervals=[[1900-01-09T00:00:00.000Z/" - + "2992-01-10T00:00:00.000Z]], filter=[<($90, 20)], projects=[[+(COS($90), 1), SIN($91)," + + "2992-01-10T00:00:00.000Z]], filter=[<($90, 20.0E0)], projects=[[+(COS($90), 1), SIN($91)," + " +(EXTRACT(FLAG(DAY), $0), 1)]])"); } @@ -3371,7 +3371,7 @@ private void testCountWithApproxDistinct(boolean approx, String sql, .explainContains("PLAN=EnumerableInterpreter\n" + " BindableSort(sort0=[$1], dir0=[ASC], fetch=[3])\n" + " DruidQuery(table=[[foodmart, foodmart]], intervals=[[1900-01-09T00:00:00.000Z/" - + "2992-01-10T00:00:00.000Z]], filter=[<($90, 20)], projects=[[+(COS(+($90, $91)), 1), " + + "2992-01-10T00:00:00.000Z]], filter=[<($90, 20.0E0)], projects=[[+(COS(+($90, $91)), 1), " + "+(EXTRACT(FLAG(DAY), $0), 1)]])"); } @@ -3501,7 +3501,7 @@ private void testCountWithApproxDistinct(boolean approx, String sql, final String query = "{'queryType':'timeseries','dataSource':'foodmart','descending':false," + "'granularity':'all','aggregations':[{'type':'filtered','filter':{'type':'or','fields':" + "[{'type':'selector','dimension':'state_province','value':'CA'},{'type':'and','fields':" - + "[{'type':'bound','dimension':'store_sales','lower':'100','lowerStrict':true," + + "[{'type':'bound','dimension':'store_sales','lower':'100.0','lowerStrict':true," + "'ordering':'numeric'},{'type':'not','field':{'type':'selector','dimension':'product_id'," + "'value':'100'}}]}]},'aggregator':{'type':'filtered','filter':{'type':'not'," + "'field':{'type':'selector','dimension':'product_id','value':null}},'aggregator':" @@ -3637,10 +3637,10 @@ private void testCountWithApproxDistinct(boolean approx, String sql, .explainContains("PLAN=EnumerableInterpreter\n" + " DruidQuery(table=[[foodmart, foodmart]], intervals=[[1900-01-09T00:00:00.000Z/" + "2992-01-10T00:00:00.000Z]], projects=[[$1, $90]], groups=[{0}], aggs=[[SUM($1)]], " - + "filter=[>($1, 220)], sort0=[0], dir0=[ASC], fetch=[2])") + + "filter=[>($1, 220.0E0)], sort0=[0], dir0=[ASC], fetch=[2])") .queryContains( new DruidChecker("'having':{'type':'filter','filter':{'type':'bound'," - + "'dimension':'S','lower':'220','lowerStrict':true,'ordering':'numeric'}}")); + + "'dimension':'S','lower':'220.0','lowerStrict':true,'ordering':'numeric'}}")); q.returnsOrdered("P=1; S=236.55", "P=10; S=230.04"); } @@ -3652,7 +3652,7 @@ private void testCountWithApproxDistinct(boolean approx, String sql, .explainContains("PLAN=EnumerableInterpreter\n" + " DruidQuery(table=[[foodmart, foodmart]], intervals=[[1900-01-09T00:00:00.000Z/" + "2992-01-10T00:00:00.000Z]], filter=[>($1, '10')], projects=[[$1, $90]], groups=[{0}]," - + " aggs=[[SUM($1)]], filter=[>($1, 220)], sort0=[0], dir0=[ASC], fetch=[2])\n") + + " aggs=[[SUM($1)]], filter=[>($1, 220.0E0)], sort0=[0], dir0=[ASC], fetch=[2])\n") .queryContains( new DruidChecker("{'queryType':'groupBy','dataSource':'foodmart','granularity':'all'")); q.returnsOrdered("P=100; S=343.2", "P=1000; S=532.62"); diff --git a/druid/src/test/java/org/apache/calcite/test/DruidAdapterIT.java b/druid/src/test/java/org/apache/calcite/test/DruidAdapterIT.java index 9e140f32937..e47fe4376ec 100644 --- a/druid/src/test/java/org/apache/calcite/test/DruidAdapterIT.java +++ b/druid/src/test/java/org/apache/calcite/test/DruidAdapterIT.java @@ -2691,14 +2691,14 @@ private void checkGroupBySingleSortLimit(boolean approx) { + "EnumerableInterpreter\n" + " DruidQuery(table=[[foodmart, foodmart]], " + "intervals=[[1900-01-09T00:00:00.000Z/2992-01-10T00:00:00.000Z]], filter=[>" - + "($91, 10)], projects=[[$90, IS TRUE(>($91, 10))]], groups=[{}], aggs=[[SUM($0)" + + "($91, 10.0E0)], projects=[[$90, IS TRUE(>($91, 10.0E0))]], groups=[{}], aggs=[[SUM($0)" + "]])"; sql(sql) .explainContains(expectedSubExplain) .queryContains( new DruidChecker("\"queryType\":\"timeseries\"", "\"filter\":{\"type\":\"bound\"," - + "\"dimension\":\"store_cost\",\"lower\":\"10\",\"lowerStrict\":true," + + "\"dimension\":\"store_cost\",\"lower\":\"10.0\",\"lowerStrict\":true," + "\"ordering\":\"numeric\"}")) .returnsUnordered("EXPR$0=25.06"); } @@ -2712,14 +2712,14 @@ private void checkGroupBySingleSortLimit(boolean approx) { + "EnumerableCalc(expr#0..1=[{inputs}], EXPR$0=[$t1], product_id=[$t0])\n" + " EnumerableInterpreter\n" + " DruidQuery(table=[[foodmart, foodmart]], intervals=[[1900-01-09T00:00:00" - + ".000Z/2992-01-10T00:00:00.000Z]], filter=[AND(>(CAST($1):INTEGER, 1553), >($91, 5))], " + + ".000Z/2992-01-10T00:00:00.000Z]], filter=[AND(>(CAST($1):INTEGER, 1553), >($91, 5.0E0))], " + "projects=[[$1, $90]], groups=[{0}], aggs=[[SUM($1)]])"; CalciteAssert.AssertQuery q = sql(sql) .explainContains(expectedSubExplain) .queryContains( new DruidChecker("\"queryType\":\"groupBy\"", "{\"type\":\"bound\"," - + "\"dimension\":\"store_cost\",\"lower\":\"5\",\"lowerStrict\":true," + + "\"dimension\":\"store_cost\",\"lower\":\"5.0\",\"lowerStrict\":true," + "\"ordering\":\"numeric\"}")); q.returnsUnordered("EXPR$0=10.16; product_id=1554", "EXPR$0=45.05; product_id=1556", @@ -2736,7 +2736,7 @@ private void checkGroupBySingleSortLimit(boolean approx) { sql(sql) .queryContains( new DruidChecker("\"queryType\":\"groupBy\"", "{\"type\":\"bound\"," - + "\"dimension\":\"store_cost\",\"lower\":\"5\",\"lowerStrict\":true," + + "\"dimension\":\"store_cost\",\"lower\":\"5.0\",\"lowerStrict\":true," + "\"ordering\":\"numeric\"}")) .returnsUnordered("EXPR$0=10.6; product_id=1556", "EXPR$0=10.6; product_id=1556", @@ -3792,7 +3792,7 @@ private void testCountWithApproxDistinct(boolean approx, String sql, String expe + "<= ((floor(\\'store_sales\\') * 25) + 2))'}"; final String likeExpressionFilter = "{'type':'expression','expression':'like(\\'product_id\\'"; final String likeExpressionFilter2 = "1%"; - final String simpleBound = "{'type':'bound','dimension':'store_cost','lower':'1'," + final String simpleBound = "{'type':'bound','dimension':'store_cost','lower':'1.0'," + "'lowerStrict':true,'ordering':'numeric'}"; final String timeSimpleFilter = "{'type':'bound','dimension':'__time','upper':'1997-01-02T00:00:00.000Z'," @@ -3812,12 +3812,12 @@ private void testCountWithApproxDistinct(boolean approx, String sql, String expe // 1. https://issues.apache.org/jira/browse/CALCITE-2590 // 2. https://issues.apache.org/jira/browse/CALCITE-2838 final String booleanAsFilter = "{\"type\":\"bound\",\"dimension\":\"store_sales\"," - + "\"lower\":\"0\",\"lowerStrict\":true,\"ordering\":\"numeric\"}"; + + "\"lower\":\"0.0\",\"lowerStrict\":true,\"ordering\":\"numeric\"}"; final String plan = "PLAN=EnumerableInterpreter\n" + " DruidQuery(table=[[foodmart, foodmart]], " + "intervals=[[1900-01-09T00:00:00.000Z/2992-01-10T00:00:00.000Z]], " + "filter=[AND(<=(/(+(CAST($1):INTEGER, $90), -($91, 5)), +(*(FLOOR($90), 25), 2)), " - + ">($90, 0), LIKE($1, '1%'), >($91, 1), <($0, 1997-01-02 00:00:00), " + + ">($90, 0.0E0), LIKE($1, '1%'), >($91, 1.0E0), <($0, 1997-01-02 00:00:00), " + "=(EXTRACT(FLAG(MONTH), $0), 1), =(EXTRACT(FLAG(DAY), $0), 1), " + "=(+(/(EXTRACT(FLAG(MONTH), $0), 4), 1), 1))], groups=[{}], aggs=[[COUNT()]])"; sql(sql, FOODMART) @@ -3841,7 +3841,7 @@ private void testCountWithApproxDistinct(boolean approx, String sql, String expe + " AND ceil(\"timestamp\" TO SECOND) > CAST('1997-01-01' AS TIMESTAMP) "; final String plan = "PLAN=EnumerableInterpreter\n" + " DruidQuery(table=[[foodmart, foodmart]], intervals=[[1997-01-01T00:00:00.001Z/" - + "1997-01-04T00:00:00.001Z]], filter=[>(CEIL($90), 1)], groups=[{}], aggs=[[COUNT()]])"; + + "1997-01-04T00:00:00.001Z]], filter=[>(CEIL($90), 1.0E0)], groups=[{}], aggs=[[COUNT()]])"; sql(sql, FOODMART) .explainContains(plan) .returnsOrdered("EXPR$0=408"); @@ -3923,7 +3923,7 @@ private void testCountWithApproxDistinct(boolean approx, String sql, String expe sql(sql) .queryContains( new DruidChecker("\"queryType\":\"groupBy\"", "{\"type\":\"bound\"," - + "\"dimension\":\"store_cost\",\"lower\":\"5\",\"lowerStrict\":true," + + "\"dimension\":\"store_cost\",\"lower\":\"5.0\",\"lowerStrict\":true," + "\"ordering\":\"numeric\"}")) .runs(); } @@ -4010,7 +4010,7 @@ private void testCountWithApproxDistinct(boolean approx, String sql, String expe .explainContains("PLAN=EnumerableInterpreter\n" + " BindableSort(sort0=[$2], dir0=[ASC], fetch=[3])\n" + " DruidQuery(table=[[foodmart, foodmart]], intervals=[[1900-01-09T00:00:00.000Z/" - + "2992-01-10T00:00:00.000Z]], filter=[<($90, 20)], projects=[[+(COS($90), 1), SIN($91)," + + "2992-01-10T00:00:00.000Z]], filter=[<($90, 20.0E0)], projects=[[+(COS($90), 1), SIN($91)," + " +(EXTRACT(FLAG(DAY), $0), 1)]])"); } @@ -4026,7 +4026,7 @@ private void testCountWithApproxDistinct(boolean approx, String sql, String expe .explainContains("PLAN=EnumerableInterpreter\n" + " BindableSort(sort0=[$1], dir0=[ASC], fetch=[3])\n" + " DruidQuery(table=[[foodmart, foodmart]], intervals=[[1900-01-09T00:00:00.000Z/" - + "2992-01-10T00:00:00.000Z]], filter=[<($90, 20)], projects=[[+(COS(+($90, $91)), 1), " + + "2992-01-10T00:00:00.000Z]], filter=[<($90, 20.0E0)], projects=[[+(COS(+($90, $91)), 1), " + "+(EXTRACT(FLAG(DAY), $0), 1)]])"); } @@ -4166,7 +4166,7 @@ private void testCountWithApproxDistinct(boolean approx, String sql, String expe final String query = "{'queryType':'timeseries','dataSource':'foodmart','descending':false," + "'granularity':'all','aggregations':[{'type':'filtered','filter':{'type':'or','fields':" + "[{'type':'selector','dimension':'state_province','value':'CA'},{'type':'and','fields':" - + "[{'type':'bound','dimension':'store_sales','lower':'100','lowerStrict':true," + + "[{'type':'bound','dimension':'store_sales','lower':'100.0','lowerStrict':true," + "'ordering':'numeric'},{'type':'not','field':{'type':'selector','dimension':'product_id'," + "'value':'100'}}]}]},'aggregator':{'type':'filtered','filter':{'type':'not'," + "'field':{'type':'selector','dimension':'product_id','value':null}},'aggregator':" @@ -4326,10 +4326,10 @@ private void testCountWithApproxDistinct(boolean approx, String sql, String expe .explainContains("PLAN=EnumerableInterpreter\n" + " DruidQuery(table=[[foodmart, foodmart]], intervals=[[1900-01-09T00:00:00.000Z/" + "2992-01-10T00:00:00.000Z]], projects=[[$1, $90]], groups=[{0}], aggs=[[SUM($1)]], " - + "filter=[>($1, 220)], sort0=[0], dir0=[ASC], fetch=[2])") + + "filter=[>($1, 220.0E0)], sort0=[0], dir0=[ASC], fetch=[2])") .queryContains( new DruidChecker("'having':{'type':'filter','filter':{'type':'bound'," - + "'dimension':'S','lower':'220','lowerStrict':true,'ordering':'numeric'}}")); + + "'dimension':'S','lower':'220.0','lowerStrict':true,'ordering':'numeric'}}")); q.returnsOrdered("P=1; S=236.55", "P=10; S=230.04"); } @@ -4341,7 +4341,7 @@ private void testCountWithApproxDistinct(boolean approx, String sql, String expe .explainContains("PLAN=EnumerableInterpreter\n" + " DruidQuery(table=[[foodmart, foodmart]], intervals=[[1900-01-09T00:00:00.000Z/" + "2992-01-10T00:00:00.000Z]], filter=[>($1, '10')], projects=[[$1, $90]], groups=[{0}]," - + " aggs=[[SUM($1)]], filter=[>($1, 220)], sort0=[0], dir0=[ASC], fetch=[2])\n") + + " aggs=[[SUM($1)]], filter=[>($1, 220.0E0)], sort0=[0], dir0=[ASC], fetch=[2])\n") .queryContains( new DruidChecker("{'queryType':'groupBy','dataSource':'foodmart','granularity':'all'")); q.returnsOrdered("P=100; S=343.2", "P=1000; S=532.62");