This is an automated email from the ASF dual-hosted git repository.
danny0405 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/calcite.git
The following commit(s) were added to refs/heads/master by this push:
new 64068d6 [CALCITE-1178] Allow SqlBetweenOperator to compare DATE and
TIMESTAMP
64068d6 is described below
commit 64068d69ceee8450e56c4705d26b7a2f944909b9
Author: yuzhao.cyz <[email protected]>
AuthorDate: Mon Sep 23 13:42:45 2019 +0800
[CALCITE-1178] Allow SqlBetweenOperator to compare DATE and TIMESTAMP
* Add implicit type coercion rules for BETWEEN operator and add test
cases
* For comparison of NUMERIC and CHARACTER, we coerce the CHARACTER to
NUMERIC type which is breaking change.(Before this patch, CHARACTER would
be coerced to INT during sql-to-rel conversion)
* For "DATETIME and CHARACTER" comparison and equality, add coercion rules
during sql
node validation, before this patch, the coercion happens during sql-to-rel
conversion
* Some code refactoring for TypeCoercionConverterTest
---
.../sql/type/ComparableOperandTypeChecker.java | 12 +-
.../validate/implicit/AbstractTypeCoercion.java | 33 +++-
.../sql/validate/implicit/TypeCoercion.java | 3 +
.../sql/validate/implicit/TypeCoercionImpl.java | 189 ++++++++++++--------
.../calcite/test/TypeCoercionConverterTest.java | 85 +++------
.../org/apache/calcite/test/TypeCoercionTest.java | 34 ++--
.../calcite/test/TypeCoercionConverterTest.xml | 192 ++++-----------------
core/src/test/resources/sql/misc.iq | 12 +-
8 files changed, 243 insertions(+), 317 deletions(-)
diff --git
a/core/src/main/java/org/apache/calcite/sql/type/ComparableOperandTypeChecker.java
b/core/src/main/java/org/apache/calcite/sql/type/ComparableOperandTypeChecker.java
index e48920f..659282e 100644
---
a/core/src/main/java/org/apache/calcite/sql/type/ComparableOperandTypeChecker.java
+++
b/core/src/main/java/org/apache/calcite/sql/type/ComparableOperandTypeChecker.java
@@ -59,19 +59,20 @@ public class ComparableOperandTypeChecker extends
SameOperandTypeChecker {
RelDataType type = callBinding.getOperandType(i);
if (!checkType(callBinding, throwOnFailure, type)) {
b = false;
+ break;
}
}
if (b) {
// Coerce type first.
if (callBinding.getValidator().isTypeCoercionEnabled()) {
TypeCoercion typeCoercion =
callBinding.getValidator().getTypeCoercion();
- // For comparable operators, e.g. >, <, =, >=, <=.
- typeCoercion.binaryArithmeticCoercion(callBinding);
+ // For comparison operators, i.e. >, <, =, >=, <=.
+ typeCoercion.binaryComparisonCoercion(callBinding);
}
b = super.checkOperandTypes(callBinding, false);
- if (!b && throwOnFailure) {
- throw callBinding.newValidationSignatureError();
- }
+ }
+ if (!b && throwOnFailure) {
+ throw callBinding.newValidationSignatureError();
}
return b;
}
@@ -104,6 +105,7 @@ public class ComparableOperandTypeChecker extends
SameOperandTypeChecker {
RelDataType type = callBinding.getOperandType(i);
if (type.getComparability().ordinal() < requiredComparability.ordinal())
{
b = false;
+ break;
}
}
if (b) {
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 3e0f4ee..acd0cdf 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
@@ -115,6 +115,25 @@ public abstract class AbstractTypeCoercion implements
TypeCoercion {
}
/**
+ * Coerce all the operands to {@code commonType}.
+ *
+ * @param scope Validator scope
+ * @param call the call
+ * @param commonType common type to coerce to
+ * @return true if any operand is coerced
+ */
+ protected boolean coerceOperandsType(
+ SqlValidatorScope scope,
+ SqlCall call,
+ RelDataType commonType) {
+ boolean coerced = false;
+ for (int i = 0; i < call.operandCount(); i++) {
+ coerced = coerceOperandType(scope, call, i, commonType) || coerced;
+ }
+ return coerced;
+ }
+
+ /**
* Cast column at index {@code index} to target type.
*
* @param scope validator scope for the node list
@@ -419,11 +438,15 @@ public abstract class AbstractTypeCoercion implements
TypeCoercion {
return null;
}
- if (SqlTypeUtil.isString(type1) && SqlTypeUtil.isDatetime(type2)
- || SqlTypeUtil.isDatetime(type1) && SqlTypeUtil.isString(type2)) {
- // Returns null instead of VARCHAR,
- // because calcite will do the cast in SqlToRelConverter.
- 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.isCharacter(type1) && SqlTypeUtil.isDatetime(type2)) {
+ return type2;
+ }
+
+ if (SqlTypeUtil.isDatetime(type1) && SqlTypeUtil.isCharacter(type2)) {
+ return type1;
}
// DATE + TIMESTAMP -> TIMESTAMP
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 7ba068b..e6b6cfe 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
@@ -118,6 +118,9 @@ public interface TypeCoercion {
/** Coerce operand of binary arithmetic expressions to Numeric type.*/
boolean binaryArithmeticCoercion(SqlCallBinding binding);
+ /** Coerce operands in binary comparison expressions. */
+ boolean binaryComparisonCoercion(SqlCallBinding binding);
+
/**
* Coerce CASE WHEN statement branches to one common type.
*
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 a66dd47..fe2f347 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
@@ -35,11 +35,14 @@ import org.apache.calcite.sql.type.SqlTypeUtil;
import org.apache.calcite.sql.validate.SqlUserDefinedTableMacro;
import org.apache.calcite.sql.validate.SqlValidator;
import org.apache.calcite.sql.validate.SqlValidatorScope;
+import org.apache.calcite.util.Util;
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;
/**
* Default implementation of Calcite implicit type cast.
@@ -71,7 +74,7 @@ public class TypeCoercionImpl extends AbstractTypeCoercion {
* @param query query node to update the field type for
* @param columnIndex target column index
* @param targetType target type to cast to
- * @return true if any type coercion actually happens.
+ * @return true if any type coercion actually happens
*/
public boolean rowTypeCoercion(
SqlValidatorScope scope,
@@ -113,28 +116,22 @@ public class TypeCoercionImpl extends
AbstractTypeCoercion {
/**
* Coerce operands in binary arithmetic expressions to NUMERIC types.
*
- * <p>Rules:</p>
- * <ul>
- * <li>For binary arithmetic operators like [+, -, *, /, %]: 1. If the
operand is VARCHAR type,
- * coerce it to data type of the other operand if its data type is
NUMERIC.</li>
- * <li>For EQUALS(=) operator: 1. If operands are BOOLEAN and NUMERIC,
evaluate
- * `1=true` and `0=false` all to be true; 2. If operands are datetime and
string,
- * do nothing because the SqlToRelConverter already makes the type
coercion.</li>
- * <li>For binary comparision [=, >, >=, <, <=]: try to find
the common type,
- * i.e. "1 > '1'" will be converted to "1 > 1".</li>
- * <li>Some single agg functions: coerce string operand to DECIMAL
type.</li>
- * </ul>
+ * <p>For binary arithmetic operators like [+, -, *, /, %]:
+ * If the operand is VARCHAR,
+ * coerce it to data type of the other operand if its data type is NUMERIC;
+ * If the other operand is DECIMAL,
+ * coerce the STRING operand to max precision/scale DECIMAL.
*/
public boolean binaryArithmeticCoercion(SqlCallBinding binding) {
- // Assert that the operator has NUMERIC family operand type checker.
+ // Assume that the operator has NUMERIC family operand type checker.
SqlOperator operator = binding.getOperator();
SqlKind kind = operator.getKind();
boolean coerced = false;
// Binary operator
if (binding.getOperandCount() == 2) {
- RelDataType type1 = binding.getOperandType(0);
- RelDataType type2 = binding.getOperandType(1);
- // Special case for datetime +/- interval
+ final RelDataType type1 = binding.getOperandType(0);
+ final RelDataType type2 = binding.getOperandType(1);
+ // Special case for datetime + interval or datetime - interval
if (kind == SqlKind.PLUS || kind == SqlKind.MINUS) {
if (SqlTypeUtil.isInterval(type1) || SqlTypeUtil.isInterval(type2)) {
return false;
@@ -144,49 +141,6 @@ public class TypeCoercionImpl extends AbstractTypeCoercion
{
if (kind.belongsTo(SqlKind.BINARY_ARITHMETIC)) {
coerced = binaryArithmeticWithStrings(binding, type1, type2);
}
- // EQUALS(=) NOT_EQUALS(<>) operator
- if (kind.belongsTo(SqlKind.BINARY_EQUALITY)) {
- // STRING and datetime
- // BOOLEAN and NUMERIC | BOOLEAN and literal
- coerced = dateTimeStringEquality(binding, type1, type2) || coerced;
- coerced = booleanEquality(binding, type1, type2) || coerced;
- }
- // Binary comparision operator like: = > >= < <=
- if (kind.belongsTo(SqlKind.BINARY_COMPARISON)) {
- RelDataType commonType = commonTypeForBinaryComparison(type1, type2);
- if (null != commonType) {
- coerced = coerceOperandType(binding.getScope(), binding.getCall(),
0, commonType)
- || coerced;
- coerced = coerceOperandType(binding.getScope(), binding.getCall(),
1, commonType)
- || coerced;
- }
- }
- }
- // Single operand agg function, cast string operand to DECIMAL.
- if (binding.getOperandCount() == 1) {
- RelDataType type = validator.deriveType(binding.getScope(),
binding.operand(0));
- boolean isStringType = SqlTypeUtil.isCharacter(type);
- if (operator.getName().equalsIgnoreCase("ABS")
- && isStringType) {
- return coerceOperandType(binding.getScope(), binding.getCall(), 0,
- SqlTypeUtil.getMaxPrecisionScaleDecimal(factory));
- }
- // Better to move these functions to a single method.
- switch (kind) {
- case SUM:
- case SUM0:
- case AVG:
- case STDDEV_POP:
- case STDDEV_SAMP:
- case MINUS_PREFIX:
- case PLUS_PREFIX:
- case VAR_POP:
- case VAR_SAMP:
- if (isStringType) {
- return coerceOperandType(binding.getScope(), binding.getCall(), 0,
- SqlTypeUtil.getMaxPrecisionScaleDecimal(factory));
- }
- }
}
return coerced;
}
@@ -224,6 +178,105 @@ public class TypeCoercionImpl extends
AbstractTypeCoercion {
}
/**
+ * Coerce operands in binary comparison expressions.
+ *
+ * <p>Rules:</p>
+ * <ul>
+ * <li>For EQUALS(=) operator: 1. If operands are BOOLEAN and NUMERIC,
evaluate
+ * `1=true` and `0=false` all to be true; 2. If operands are datetime and
string,
+ * do nothing because the SqlToRelConverter already makes the type
coercion;</li>
+ * <li>For binary comparision [=, >, >=, <, <=]: try to find
the common type,
+ * i.e. "1 > '1'" will be converted to "1 > 1";</li>
+ * <li>For BETWEEN operator, find the common comparison data type of all
the operands,
+ * the common type is deduced from left to right, i.e. for expression "A
between B and C",
+ * finds common comparison type D between A and B
+ * then common comparison type E between D and C as the final common
type.</li>
+ * </ul>
+ */
+ public boolean binaryComparisonCoercion(SqlCallBinding binding) {
+ SqlOperator operator = binding.getOperator();
+ SqlKind kind = operator.getKind();
+ int operandCnt = binding.getOperandCount();
+ boolean coerced = false;
+ // Binary operator
+ if (operandCnt == 2) {
+ final RelDataType type1 = binding.getOperandType(0);
+ final RelDataType type2 = binding.getOperandType(1);
+ // EQUALS(=) NOT_EQUALS(<>) operator
+ if (kind.belongsTo(SqlKind.BINARY_EQUALITY)) {
+ // STRING and datetime
+ // BOOLEAN and NUMERIC | BOOLEAN and literal
+ coerced = dateTimeStringEquality(binding, type1, type2) || coerced;
+ coerced = booleanEquality(binding, type1, type2) || coerced;
+ }
+ // Binary comparision operator like: = > >= < <=
+ if (kind.belongsTo(SqlKind.BINARY_COMPARISON)) {
+ final RelDataType commonType = commonTypeForBinaryComparison(type1,
type2);
+ if (null != commonType) {
+ coerced = coerceOperandsType(binding.getScope(), binding.getCall(),
commonType);
+ }
+ }
+ }
+ // Infix operator like: BETWEEN
+ if (kind == SqlKind.BETWEEN) {
+ final List<RelDataType> operandTypes = Util.range(operandCnt).stream()
+ .map(binding::getOperandType)
+ .collect(Collectors.toList());
+ final RelDataType commonType = commonTypeForComparison(operandTypes);
+ if (null != commonType) {
+ coerced = coerceOperandsType(binding.getScope(), binding.getCall(),
commonType);
+ }
+ }
+ return coerced;
+ }
+
+ /**
+ * Finds the common type for binary comparison
+ * when the size of operands {@code dataTypes} is more than 2.
+ * If there are N(more than 2) operands,
+ * finds the common type between two operands from left to right:
+ *
+ * <p>Rules:</p>
+ * <pre>
+ * type1 type2 type3
+ * | | |
+ * +- type4 -+ |
+ * | |
+ * +--- type5 ---+
+ * </pre>
+ * For operand data types (type1, type2, type3), deduce the common type type4
+ * from type1 and type2, then common type type5 from type4 and type3.
+ */
+ protected RelDataType commonTypeForComparison(List<RelDataType> dataTypes) {
+ assert dataTypes.size() > 2;
+ final RelDataType type1 = dataTypes.get(0);
+ final RelDataType type2 = dataTypes.get(1);
+ // No need to do type coercion if all the data types have the same type
name.
+ boolean allWithSameName = SqlTypeUtil.sameNamedType(type1, type2);
+ for (int i = 2; i < dataTypes.size() && allWithSameName; i++) {
+ allWithSameName = SqlTypeUtil.sameNamedType(dataTypes.get(i - 1),
dataTypes.get(i));
+ }
+ if (allWithSameName) {
+ return null;
+ }
+
+ RelDataType commonType;
+ if (SqlTypeUtil.sameNamedType(type1, type2)) {
+ commonType = factory.leastRestrictive(Arrays.asList(type1, type2));
+ } else {
+ 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));
+ }
+ }
+ return commonType;
+ }
+
+ /**
* Datetime and STRING equality: cast STRING type to datetime type,
SqlToRelConverter already
* make the conversion but we still keep this interface overridable
* so user can have their custom implementation.
@@ -234,15 +287,15 @@ public class TypeCoercionImpl extends
AbstractTypeCoercion {
RelDataType right) {
// REVIEW Danny 2018-05-23 we do not need to coerce type for EQUALS
// because SqlToRelConverter already does this.
- if (binding.getCall().getKind() == SqlKind.NOT_EQUALS) {
- if (SqlTypeUtil.isCharacter(left)
- && SqlTypeUtil.isDatetime(right)) {
- return coerceOperandType(binding.getScope(), binding.getCall(), 0,
right);
- }
- if (SqlTypeUtil.isCharacter(right)
- && SqlTypeUtil.isDatetime(left)) {
- return coerceOperandType(binding.getScope(), binding.getCall(), 1,
left);
- }
+ // REVIEW Danny 2019-09-23, we should unify the coercion rules in
TypeCoercion
+ // instead of SqlToRelConverter.
+ if (SqlTypeUtil.isCharacter(left)
+ && SqlTypeUtil.isDatetime(right)) {
+ return coerceOperandType(binding.getScope(), binding.getCall(), 0,
right);
+ }
+ if (SqlTypeUtil.isCharacter(right)
+ && SqlTypeUtil.isDatetime(left)) {
+ return coerceOperandType(binding.getScope(), binding.getCall(), 1, left);
}
return false;
}
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 3551b60..ed5ca2b 100644
--- a/core/src/test/java/org/apache/calcite/test/TypeCoercionConverterTest.java
+++ b/core/src/test/java/org/apache/calcite/test/TypeCoercionConverterTest.java
@@ -37,47 +37,28 @@ public class TypeCoercionConverterTest extends
SqlToRelTestBase {
}
/** Test case for {@link TypeCoercion#commonTypeForBinaryComparison}. */
- @Test public void testBinaryComparable() {
+ @Test public void testBinaryComparison() {
// for constant cast, there is reduce rule
- checkPlanEquals("select 1<'1' from (values true)");
- }
-
- @Test public void testBinaryComparable1() {
- checkPlanEquals("select 1<='1' from (values true)");
- }
-
- @Test public void testBinaryComparable2() {
- checkPlanEquals("select 1>'1' from (values true)");
- }
-
- @Test public void testBinaryComparable3() {
- checkPlanEquals("select 1>='1' from (values true)");
- }
-
- @Test public void testBinaryComparable4() {
- checkPlanEquals("select 1='1' from (values true)");
- }
-
- @Test public void testBinaryComparable5() {
- checkPlanEquals("select t1_date > t1_timestamp from t1");
- }
-
- @Test public void testBinaryComparable6() {
- checkPlanEquals("select '2' is not distinct from 2 from (values true)");
+ checkPlanEquals("select\n"
+ + "1<'1' as f0,\n"
+ + "1<='1' as f1,\n"
+ + "1>'1' as f2,\n"
+ + "1>='1' as f3,\n"
+ + "1='1' as f4,\n"
+ + "t1_date > t1_timestamp as f5,\n"
+ + "'2' is not distinct from 2 as f6,\n"
+ + "'2019-09-23' between t1_date and t1_timestamp as f7,\n"
+ + "cast('2019-09-23' as date) between t1_date and t1_timestamp as f8\n"
+ + "from t1");
}
/** Test cases for {@link TypeCoercion#inOperationCoercion}. */
@Test public void testInOperation() {
- checkPlanEquals("select 1 in ('1', '2', '3') from (values true)");
- }
-
- @Test public void testInOperation1() {
- checkPlanEquals("select (1, 2) in (select '1', '2' "
- + "from (values (true, true))) from (values true)");
- }
-
- @Test public void testInOperation2() {
- checkPlanEquals("select (1, 2) in (('1', '2'), ('3', '4')) from (values
true)");
+ checkPlanEquals("select\n"
+ + "1 in ('1', '2', '3') as f0,\n"
+ + "(1, 2) in (('1', '2')) as f1,\n"
+ + "(1, 2) in (('1', '2'), ('3', '4')) as f2\n"
+ + "from (values (true, true, true))");
}
/** Test cases for
@@ -87,28 +68,14 @@ public class TypeCoercionConverterTest extends
SqlToRelTestBase {
// Calcite execution runtime, but we still add cast in the plan so other
systems
// using Calcite can rewrite Cast operator implementation.
// for this case, we replace the boolean literal with numeric 1.
- checkPlanEquals("select 1=true from (values true)");
- }
-
- @Test public void testBooleanEquality1() {
- checkPlanEquals("select 1.0=true from (values true)");
- }
-
- @Test public void testBooleanEquality2() {
- checkPlanEquals("select 0.0=true from (values true)");
- }
-
- @Test public void testBooleanEquality3() {
- checkPlanEquals("select 1.23=t1_boolean from t1");
- }
-
- @Test public void testBooleanEquality4() {
- // int boolean
- checkPlanEquals("select t1_smallint=t1_boolean from t1");
- }
-
- @Test public void testBooleanEquality5() {
- checkPlanEquals("select 10000000000=true from (values true)");
+ checkPlanEquals("select\n"
+ + "1=true as f0,\n"
+ + "1.0=true as f1,\n"
+ + "0.0=true=true as f2,\n"
+ + "1.23=t1_boolean as f3,\n"
+ + "t1_smallint=t1_boolean as f4,\n"
+ + "10000000000=true as f5\n"
+ + "from t1");
}
@Test public void testCaseWhen() {
@@ -123,7 +90,7 @@ public class TypeCoercionConverterTest extends
SqlToRelTestBase {
checkPlanEquals("select * from (values(1, '3')) union select * from
(values('2', 4))");
}
- @Test public void testSetOperations() {
+ @Test public void testSetOperation() {
// int decimal smallint double
// char decimal float bigint
// char decimal float double
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 46b72bd..c7075df 100644
--- a/core/src/test/java/org/apache/calcite/test/TypeCoercionTest.java
+++ b/core/src/test/java/org/apache/calcite/test/TypeCoercionTest.java
@@ -536,21 +536,31 @@ public class TypeCoercionTest extends
SqlValidatorTestCase {
/** Test cases for binary comparison expressions. */
@Test public void testBinaryComparisonCoercion() {
- sql("select '2' = 3 from (values true)")
+ checkExpType("'2' = 3", "BOOLEAN NOT NULL");
+ checkExpType("'2' > 3", "BOOLEAN NOT NULL");
+ checkExpType("'2' >= 3", "BOOLEAN NOT NULL");
+ checkExpType("'2' < 3", "BOOLEAN NOT NULL");
+ checkExpType("'2' <= 3", "BOOLEAN NOT NULL");
+ checkExpType("'2' is distinct from 3", "BOOLEAN NOT NULL");
+ checkExpType("'2' is not distinct from 3", "BOOLEAN NOT NULL");
+ // NULL operand
+ checkExpType("'2' = null", "BOOLEAN");
+ checkExpType("'2' > null", "BOOLEAN");
+ checkExpType("'2' >= null", "BOOLEAN");
+ checkExpType("'2' < null", "BOOLEAN");
+ checkExpType("'2' <= null", "BOOLEAN");
+ checkExpType("'2' is distinct from null", "BOOLEAN NOT NULL");
+ checkExpType("'2' is not distinct from null", "BOOLEAN NOT NULL");
+ // BETWEEN operator
+ checkExpType("'2' between 1 and 3", "BOOLEAN NOT NULL");
+ checkExpType("NULL between 1 and 3", "BOOLEAN");
+ sql("select '2019-09-23' between t1_date and t1_timestamp from t1")
.columnType("BOOLEAN NOT NULL");
- sql("select '2' > 3 from (values true)")
+ sql("select t1_date between '2019-09-23' and t1_timestamp from t1")
.columnType("BOOLEAN NOT NULL");
- sql("select '2' >= 3 from (values true)")
+ sql("select cast('2019-09-23' as date) between t1_date and t1_timestamp
from t1")
.columnType("BOOLEAN NOT NULL");
- sql("select '2' < 3 from (values true)")
- .columnType("BOOLEAN NOT NULL");
- sql("select '2' <= 3 from (values true)")
- .columnType("BOOLEAN NOT NULL");
- sql("select '2' is distinct from 3 from (values true)")
- .columnType("BOOLEAN NOT NULL");
- sql("select '2' is not distinct from 3 from (values true)")
- .columnType("BOOLEAN NOT NULL");
- sql("select '2' is not distinct from 3 from (values true)")
+ sql("select t1_date between cast('2019-09-23' as date) and t1_timestamp
from t1")
.columnType("BOOLEAN NOT NULL");
}
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 d0cf220..f697ae2 100644
---
a/core/src/test/resources/org/apache/calcite/test/TypeCoercionConverterTest.xml
+++
b/core/src/test/resources/org/apache/calcite/test/TypeCoercionConverterTest.xml
@@ -18,190 +18,58 @@ limitations under the License.
<Root>
<TestCase name="testInOperation">
<Resource name="sql">
- <![CDATA[select 1 in ('1', '2', '3') from (values true)]]>
+ <![CDATA[select
+1 in ('1', '2', '3') as f0,
+(1, 2) in (('1', '2')) as f1,
+(1, 2) in (('1', '2'), ('3', '4')) as f2
+from (values (true, true, true))"]]>
</Resource>
<Resource name="plan">
<![CDATA[
-LogicalProject(EXPR$0=[true])
- LogicalValues(tuples=[[{ true }]])
-]]>
- </Resource>
- </TestCase>
- <TestCase name="testInOperation1">
- <Resource name="sql">
- <![CDATA[select (1, 2) in (select '1', '2'
-from (values (true, true))) from (values true))]]>
- </Resource>
- <Resource name="plan">
- <![CDATA[
-LogicalProject(EXPR$0=[CAST(OR(AND(IS NOT NULL($7), <>($1, 0)), AND(<($2, $1),
null, <>($1, 0), IS NULL($7)))):BOOLEAN NOT NULL])
- LogicalJoin(condition=[AND(=($3, $5), =($4, $6))], joinType=[left])
- LogicalProject(EXPR$0=[$0], $f0=[$1], $f1=[$2], $f3=[1], $f4=[2])
- LogicalJoin(condition=[true], joinType=[inner])
- LogicalValues(tuples=[[{ true }]])
- LogicalAggregate(group=[{}], agg#0=[COUNT()], agg#1=[COUNT($0, $1)])
- LogicalProject(EXPR$0=[1], EXPR$1=[2], $f2=[true])
- LogicalValues(tuples=[[{ true, true }]])
- LogicalAggregate(group=[{0, 1}], agg#0=[MIN($2)])
- LogicalProject(EXPR$0=[1], EXPR$1=[2], $f2=[true])
- LogicalValues(tuples=[[{ true, true }]])
-]]>
- </Resource>
- </TestCase>
- <TestCase name="testInOperation2">
- <Resource name="sql">
- <![CDATA[select 1 in ('1', '2', '3') from (values true)]]>
- </Resource>
- <Resource name="plan">
- <![CDATA[
-LogicalProject(EXPR$0=[true])
- LogicalValues(tuples=[[{ true }]])
+LogicalProject(F0=[true], F1=[true], F2=[true])
+ LogicalValues(tuples=[[{ true, true, true }]])
]]>
</Resource>
</TestCase>
<TestCase name="testBooleanEquality">
<Resource name="sql">
- <![CDATA[select 1=true from (values true)]]>
- </Resource>
- <Resource name="plan">
- <![CDATA[
-LogicalProject(EXPR$0=[true])
- LogicalValues(tuples=[[{ true }]])
-]]>
- </Resource>
- </TestCase>
- <TestCase name="testBooleanEquality1">
- <Resource name="sql">
- <![CDATA[select 1.0=true from (values true)]]>
- </Resource>
- <Resource name="plan">
- <![CDATA[
-LogicalProject(EXPR$0=[true])
- LogicalValues(tuples=[[{ true }]])
-]]>
- </Resource>
- </TestCase>
- <TestCase name="testBooleanEquality2">
- <Resource name="sql">
- <![CDATA[select 0.0=true from (values true)]]>
- </Resource>
- <Resource name="plan">
- <![CDATA[
-LogicalProject(EXPR$0=[false])
- LogicalValues(tuples=[[{ true }]])
-]]>
- </Resource>
- </TestCase>
- <TestCase name="testBooleanEquality3">
- <Resource name="sql">
- <![CDATA[select 1.23=t1_boolean from t1]]>
+ <![CDATA[select
+1=true as f0,
+1.0=true as f1,
+0.0=true=true as f2,
+1.23=t1_boolean as f3,
+t1_smallint=t1_boolean as f4,
+10000000000=true as f5
+from t1]]>
</Resource>
<Resource name="plan">
<![CDATA[
-LogicalProject(EXPR$0=[NOT($10)])
+LogicalProject(F0=[true], F1=[true], F2=[false], F3=[NOT($10)], F4=[=($1,
CASE($10, 1:SMALLINT, 0:SMALLINT))], F5=[false])
LogicalTableScan(table=[[CATALOG, SALES, T1]])
]]>
</Resource>
</TestCase>
- <TestCase name="testBooleanEquality4">
- <Resource name="sql">
- <![CDATA[select t1_smallint=t1_boolean from t1]]>
- </Resource>
- <Resource name="plan">
- <![CDATA[
-LogicalProject(EXPR$0=[=($1, CASE($10, 1:SMALLINT, 0:SMALLINT))])
- LogicalTableScan(table=[[CATALOG, SALES, T1]])
-]]>
- </Resource>
- </TestCase>
- <TestCase name="testBooleanEquality5">
- <Resource name="sql">
- <![CDATA[select 10000000000=true from (values true)]]>
- </Resource>
- <Resource name="plan">
- <![CDATA[
-LogicalProject(EXPR$0=[false])
- LogicalValues(tuples=[[{ true }]])
-]]>
- </Resource>
- </TestCase>
- <TestCase name="testBinaryComparable">
- <Resource name="sql">
- <![CDATA[select 1<'1' from (values true)]]>
- </Resource>
- <Resource name="plan">
- <![CDATA[
-LogicalProject(EXPR$0=[false])
- LogicalValues(tuples=[[{ true }]])
-]]>
- </Resource>
- </TestCase>
- <TestCase name="testBinaryComparable1">
- <Resource name="sql">
- <![CDATA[select 1<='1' from (values true)]]>
- </Resource>
- <Resource name="plan">
- <![CDATA[
-LogicalProject(EXPR$0=[true])
- LogicalValues(tuples=[[{ true }]])
-]]>
- </Resource>
- </TestCase>
- <TestCase name="testBinaryComparable2">
- <Resource name="sql">
- <![CDATA[select 1>'1' from (values true)]]>
- </Resource>
- <Resource name="plan">
- <![CDATA[
-LogicalProject(EXPR$0=[false])
- LogicalValues(tuples=[[{ true }]])
-]]>
- </Resource>
- </TestCase>
- <TestCase name="testBinaryComparable3">
- <Resource name="sql">
- <![CDATA[select 1>='1' from (values true)]]>
- </Resource>
- <Resource name="plan">
- <![CDATA[
-LogicalProject(EXPR$0=[true])
- LogicalValues(tuples=[[{ true }]])
-]]>
- </Resource>
- </TestCase>
- <TestCase name="testBinaryComparable4">
- <Resource name="sql">
- <![CDATA[select 1='1' from (values true)]]>
- </Resource>
- <Resource name="plan">
- <![CDATA[
-LogicalProject(EXPR$0=[true])
- LogicalValues(tuples=[[{ true }]])
-]]>
- </Resource>
- </TestCase>
- <TestCase name="testBinaryComparable5">
+ <TestCase name="testBinaryComparison">
<Resource name="sql">
- <![CDATA[select t1_date > t1_timestamp from t1]]>
+ <![CDATA["select
+1<'1' as f0,
+1<='1' as f1,
+1>'1' as f2,
+1>='1' as f3,
+1='1' as f4,
+t1_date > t1_timestamp as f5,
+'2' is not distinct from 2 as f6,
+'2019-09-23' between t1_date and t1_timestamp as f7,
+cast('2019-09-23' as date) between t1_date and t1_timestamp as f8
+from t1"]]>
</Resource>
<Resource name="plan">
<![CDATA[
-LogicalProject(EXPR$0=[>(CAST($8):TIMESTAMP(0) NOT NULL, $7)])
+LogicalProject(F0=[false], F1=[true], F2=[false], F3=[true], F4=[true],
F5=[>(CAST($8):TIMESTAMP(0) NOT NULL, $7)], F6=[true], F7=[AND(>=(2019-09-23
00:00:00, CAST($8):TIMESTAMP(0) NOT NULL), <=(2019-09-23 00:00:00, $7))],
F8=[AND(>=(2019-09-23 00:00:00, CAST($8):TIMESTAMP(0) NOT NULL), <=(2019-09-23
00:00:00, $7))])
LogicalTableScan(table=[[CATALOG, SALES, T1]])
]]>
</Resource>
</TestCase>
- <TestCase name="testBinaryComparable6">
- <Resource name="sql">
- <![CDATA[select '2' is not distinct from 2 from (values true)]]>
- </Resource>
- <Resource name="plan">
- <![CDATA[
-LogicalProject(EXPR$0=[true])
- LogicalValues(tuples=[[{ true }]])
-]]>
- </Resource>
- </TestCase>
<TestCase name="testBuiltinFunctionCoercion">
<Resource name="sql">
<![CDATA[select 1||'a' from (values true)]]>
@@ -227,7 +95,7 @@ LogicalUnion(all=[false])
]]>
</Resource>
</TestCase>
- <TestCase name="testSetOperations">
+ <TestCase name="testSetOperation">
<Resource name="sql">
<![CDATA[select t1_int, t1_decimal, t1_smallint, t1_double from t1
union select t2_varchar20, t2_decimal, t2_float, t2_bigint from t2
diff --git a/core/src/test/resources/sql/misc.iq
b/core/src/test/resources/sql/misc.iq
index a7127ac..777eaa4 100644
--- a/core/src/test/resources/sql/misc.iq
+++ b/core/src/test/resources/sql/misc.iq
@@ -1207,14 +1207,14 @@ select * from "scott".emp where empno between '7500'
and '07600';
(2 rows)
!ok
+
+# BETWEEN follows the PostgreSQL style to coerce STRING operand to type of the
other
+# NUMERIC operands, see TypeCoercionImpl#commonTypeForBinaryComparison.
select * from "scott".emp where deptno between '7369' and '7876';
-+-------+-------+-----+-----+----------+-----+------+--------+
-| EMPNO | ENAME | JOB | MGR | HIREDATE | SAL | COMM | DEPTNO |
-+-------+-------+-----+-----+----------+-----+------+--------+
-+-------+-------+-----+-----+----------+-----+------+--------+
-(0 rows)
-!ok
+Caused by: java.lang.NumberFormatException: Value out of range. Value:"7369"
Radix:10
+!error
+
select * from "scott".emp where '7369' between empno and '7876';
+-------+-------+-------+------+------------+--------+------+--------+
| EMPNO | ENAME | JOB | MGR | HIREDATE | SAL | COMM | DEPTNO |