-
Notifications
You must be signed in to change notification settings - Fork 2.5k
[CALCITE-7437] Type coercion for quantifier operators is incomplete #4877
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
7289fc0
501d61a
10c0abf
fb46637
ea3e4a0
ee92ed2
c3acc0f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -649,43 +649,115 @@ | |
| * CAST needs to be added. | ||
| * </ul> | ||
| */ | ||
| @Override public boolean quantifyOperationCoercion(SqlCallBinding binding) { | ||
|
Check failure on line 652 in core/src/main/java/org/apache/calcite/sql/validate/implicit/TypeCoercionImpl.java
|
||
| final RelDataType type1 = binding.getOperandType(0); | ||
| final RelDataType collectionType = binding.getOperandType(1); | ||
| final RelDataType type2 = collectionType.getComponentType(); | ||
| requireNonNull(type2, "type2"); | ||
| final SqlCall sqlCall = binding.getCall(); | ||
| final SqlValidatorScope scope = binding.getScope(); | ||
| final RelDataType type2 = binding.getOperandType(1); | ||
| final SqlNode node1 = binding.operand(0); | ||
| final SqlNode node2 = binding.operand(1); | ||
| RelDataType widenType = commonTypeForBinaryComparison(type1, type2); | ||
| if (widenType == null) { | ||
| widenType = getTightestCommonType(type1, type2); | ||
| } | ||
| if (widenType == null) { | ||
| final SqlValidatorScope scope = binding.getScope(); | ||
|
|
||
| // Check column counts match for struct types, consistent with inOperationCoercion. | ||
| if (type1.isStruct() | ||
| && type2.isStruct() | ||
| && type1.getFieldCount() != type2.getFieldCount()) { | ||
| return false; | ||
| } | ||
| final RelDataType leftWidenType = | ||
| binding.getTypeFactory().enforceTypeWithNullability(widenType, type1.isNullable()); | ||
| boolean coercedLeft = | ||
| coerceOperandType(scope, sqlCall, 0, leftWidenType); | ||
| if (coercedLeft) { | ||
| updateInferredType(node1, leftWidenType); | ||
|
|
||
| int colCount = type1.isStruct() ? type1.getFieldCount() : 1; | ||
| RelDataType[] argTypes = new RelDataType[2]; | ||
| argTypes[0] = type1; | ||
| final boolean isSubQuery = node2 instanceof SqlSelect; | ||
| // For subquery, use the row type directly. | ||
| // For collection, use the component type for comparison, not the collection. | ||
| if (isSubQuery) { | ||
| argTypes[1] = type2; | ||
| } else { | ||
| RelDataType componentType = type2.getComponentType(); | ||
| if (componentType == null) { | ||
| return false; | ||
| } | ||
| argTypes[1] = componentType; | ||
| } | ||
| boolean coerced = false; | ||
|
|
||
| // Find the common types for RHS and LHS columns, | ||
| // following the same rules as inOperationCoercion. | ||
| List<RelDataType> widenTypes = new ArrayList<>(); | ||
| for (int i = 0; i < colCount; i++) { | ||
| final int i2 = i; | ||
| List<RelDataType> columnIthTypes = new AbstractList<RelDataType>() { | ||
| @Override public RelDataType get(int index) { | ||
| return argTypes[index].isStruct() | ||
| ? argTypes[index].getFieldList().get(i2).getType() | ||
| : argTypes[index]; | ||
| } | ||
|
|
||
| @Override public int size() { | ||
| return argTypes.length; | ||
| } | ||
| }; | ||
|
|
||
| RelDataType widenType = | ||
| commonTypeForBinaryComparison(columnIthTypes.get(0), columnIthTypes.get(1)); | ||
| if (widenType == null) { | ||
| widenType = getTightestCommonType(columnIthTypes.get(0), columnIthTypes.get(1)); | ||
| } | ||
| if (widenType == null) { | ||
| // Cannot find any common type, return early. | ||
| return false; | ||
| } | ||
| widenTypes.add(widenType); | ||
| } | ||
| assert widenTypes.size() == colCount; | ||
|
|
||
| // Coerce LHS operand. | ||
| if (!type1.isStruct()) { | ||
| coerced = coerceOperandType(scope, binding.getCall(), 0, widenTypes.get(0)) || coerced; | ||
| } | ||
| final RelDataType rightWidenType = | ||
| binding.getTypeFactory().enforceTypeWithNullability(widenType, type2.isNullable()); | ||
| RelDataType collectionWidenType = | ||
| binding.getTypeFactory().createArrayType(rightWidenType, -1); | ||
| collectionWidenType = | ||
| binding | ||
| .getTypeFactory() | ||
| .enforceTypeWithNullability(collectionWidenType, collectionType.isNullable()); | ||
| boolean coercedRight = | ||
| coerceOperandType(scope, sqlCall, 1, collectionWidenType); | ||
| if (coercedRight) { | ||
| updateInferredType(node2, collectionWidenType); | ||
|
|
||
| for (int i = 0; i < widenTypes.size(); i++) { | ||
| RelDataType desired = widenTypes.get(i); | ||
| if (node1.getKind() == SqlKind.ROW) { | ||
| assert node1 instanceof SqlCall; | ||
| if (coerceOperandType(scope, (SqlCall) node1, i, desired)) { | ||
| updateInferredColumnType( | ||
| requireNonNull(scope, "scope"), | ||
| node1, i, widenTypes.get(i)); | ||
| coerced = true; | ||
| } | ||
| } | ||
|
|
||
| // RHS: subquery uses rowTypeCoercion (consistent with inOperationCoercion), | ||
| // collection reconstructs the array type. | ||
| if (isSubQuery) { | ||
| // Use rowTypeCoercion on the subquery output column, | ||
| // consistent with how inOperationCoercion handles the subquery case. | ||
| SqlValidatorScope scope1 = validator.getSelectScope((SqlSelect) node2); | ||
| RelDataType source = validator.getValidatedNodeType(node2); | ||
| RelDataType target = binding.getTypeFactory() | ||
| .createTypeWithNullability(desired, source.isNullable() || desired.isNullable()); | ||
| coerced = rowTypeCoercion(scope1, node2, i, target) || coerced; | ||
| } else { | ||
| // Collection path (e.g. ARRAY, MULTISET): coerce the whole collection | ||
| // operand once, reconstructing the collection type with the widened | ||
| // component type | ||
| RelDataType componentType = argTypes[1]; | ||
| final RelDataType rightWidenType = | ||
| binding.getTypeFactory() | ||
| .enforceTypeWithNullability(desired, componentType.isNullable()); | ||
| RelDataType collectionWidenType = | ||
| binding.getTypeFactory().createArrayType(rightWidenType, -1); | ||
| collectionWidenType = | ||
| binding.getTypeFactory() | ||
| .enforceTypeWithNullability(collectionWidenType, type2.isNullable()); | ||
| if (coerceOperandType(scope, binding.getCall(), 1, collectionWidenType)) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you please make sure there is test coverage for both cases (subquery and collections)? |
||
| updateInferredType(node2, collectionWidenType); | ||
| coerced = true; | ||
| } | ||
| } | ||
| } | ||
| return coercedLeft || coercedRight; | ||
|
|
||
| return coerced; | ||
| } | ||
|
|
||
| @Override public boolean builtinFunctionCoercion( | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.