You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ko...@apache.org on 2025/03/06 11:16:47 UTC
(ignite-3) branch main updated: IGNITE-24707 Sql. Fix type coercion for quantify operators (#5350)
This is an automated email from the ASF dual-hosted git repository.
korlov pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/main by this push:
new 92f9847124c IGNITE-24707 Sql. Fix type coercion for quantify operators (#5350)
92f9847124c is described below
commit 92f9847124c301b35894b905a4e99ec2f50d2fd2
Author: korlov42 <ko...@apache.org>
AuthorDate: Thu Mar 6 13:16:42 2025 +0200
IGNITE-24707 Sql. Fix type coercion for quantify operators (#5350)
---
.../sql/engine/prepare/IgniteTypeCoercion.java | 111 ++-
.../planner/datatypes/BaseTypeCoercionTest.java | 4 +-
.../NumericQuantifyOperatorTypeCoercionTest.java | 777 +++++++++++++++++++++
3 files changed, 888 insertions(+), 4 deletions(-)
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/IgniteTypeCoercion.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/IgniteTypeCoercion.java
index 1581d1b7dbc..f98b84d1dc8 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/IgniteTypeCoercion.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/IgniteTypeCoercion.java
@@ -24,9 +24,11 @@ import static org.apache.calcite.util.Static.RESOURCE;
import static org.apache.ignite.internal.sql.engine.util.TypeUtils.typeFamiliesAreCompatible;
import java.nio.charset.Charset;
+import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Set;
import org.apache.calcite.adapter.java.JavaTypeFactory;
import org.apache.calcite.rel.type.DynamicRecordType;
import org.apache.calcite.rel.type.RelDataType;
@@ -187,7 +189,7 @@ public class IgniteTypeCoercion extends TypeCoercionImpl {
@Override
public boolean inOperationCoercion(SqlCallBinding binding) {
SqlOperator operator = binding.getOperator();
- if (operator.getKind() != SqlKind.IN && operator.getKind() != SqlKind.NOT_IN) {
+ if (!operatorIsInOrQuantify(operator)) {
return false;
}
@@ -203,14 +205,119 @@ public class IgniteTypeCoercion extends TypeCoercionImpl {
typeFactory, type2, null
);
- //noinspection SimplifiableIfStatement
if (!typeFamiliesAreCompatible(typeFactory, leftRowType, rightRowType)) {
return false;
}
+ if (operatorIsQuantify(operator)) {
+ return quantifyOperationCoercion(binding);
+ }
+
return super.inOperationCoercion(binding);
}
+ private static boolean operatorIsIn(SqlOperator operator) {
+ return operator.getKind().belongsTo(Set.of(SqlKind.IN, SqlKind.NOT_IN));
+ }
+
+ private static boolean operatorIsQuantify(SqlOperator operator) {
+ return operator.getKind().belongsTo(Set.of(SqlKind.SOME, SqlKind.ALL));
+ }
+
+ private static boolean operatorIsInOrQuantify(SqlOperator operator) {
+ return operatorIsIn(operator) || operatorIsQuantify(operator);
+ }
+
+ private boolean quantifyOperationCoercion(SqlCallBinding binding) {
+ // This method is a copy-paste of org.apache.calcite.sql.validate.implicit.TypeCoercionImpl.inOperationCoercion
+ // with stripped validation of operation kind.
+ assert operatorIsQuantify(binding.getOperator());
+ assert binding.getOperandCount() == 2;
+
+ RelDataType type1 = binding.getOperandType(0);
+ RelDataType type2 = binding.getOperandType(1);
+ SqlNode node1 = binding.operand(0);
+ SqlNode node2 = binding.operand(1);
+ SqlValidatorScope scope = binding.getScope();
+
+ if (type1.isStruct() && type2.isStruct() && type1.getFieldCount() != type2.getFieldCount()) {
+ return false;
+ }
+
+ int colCount = type1.isStruct() ? type1.getFieldCount() : 1;
+ RelDataType[] argTypes = new RelDataType[2];
+ argTypes[0] = type1;
+ argTypes[1] = type2;
+ boolean coerced = false;
+ List<RelDataType> widenTypes = new ArrayList<>();
+ for (int i = 0; i < colCount; i++) {
+ int i2 = i;
+ List<RelDataType> columnIthTypes = new AbstractList<>() {
+ @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) {
+ // Can not find any common type, just return early.
+ return false;
+ }
+ widenTypes.add(widenType);
+ }
+ // Find all the common type for RSH and LSH columns.
+ assert widenTypes.size() == colCount;
+ for (int i = 0; i < widenTypes.size(); i++) {
+ RelDataType desired = widenTypes.get(i);
+ // LSH maybe a row values or single node.
+ 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;
+ }
+ } else {
+ coerced = coerceOperandType(scope, binding.getCall(), 0, desired) || coerced;
+ }
+ // RHS may be a row values expression or sub-query.
+ if (node2 instanceof SqlNodeList) {
+ final SqlNodeList node3 = (SqlNodeList) node2;
+ boolean listCoerced = false;
+ if (type2.isStruct()) {
+ for (SqlNode node : (SqlNodeList) node2) {
+ assert node instanceof SqlCall;
+ listCoerced = coerceOperandType(scope, (SqlCall) node, i, desired) || listCoerced;
+ }
+ if (listCoerced) {
+ updateInferredColumnType(requireNonNull(scope, "scope"), node2, i, desired);
+ }
+ } else {
+ for (int j = 0; j < ((SqlNodeList) node2).size(); j++) {
+ listCoerced = coerceColumnType(scope, node3, j, desired) || listCoerced;
+ }
+ if (listCoerced) {
+ updateInferredType(node2, desired);
+ }
+ }
+ coerced = coerced || listCoerced;
+ } else {
+ // Another sub-query.
+ SqlValidatorScope scope1 = node2 instanceof SqlSelect ? validator.getSelectScope((SqlSelect) node2) : scope;
+ coerced = rowTypeCoercion(scope1, node2, i, desired) || coerced;
+ }
+ }
+ return coerced;
+ }
+
/** {@inheritDoc} */
@Override
public boolean caseWhenCoercion(SqlCallBinding callBinding) {
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/datatypes/BaseTypeCoercionTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/datatypes/BaseTypeCoercionTest.java
index fac10b9f0bb..a801a26abd1 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/datatypes/BaseTypeCoercionTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/datatypes/BaseTypeCoercionTest.java
@@ -257,7 +257,7 @@ public class BaseTypeCoercionTest extends AbstractPlannerTest {
};
}
- private static Matcher<RexCall> ofType(NativeType type) {
+ static Matcher<RexCall> ofType(NativeType type) {
return new BaseMatcher<>() {
RelDataType expectedType;
RelDataType relRowType;
@@ -266,7 +266,7 @@ public class BaseTypeCoercionTest extends AbstractPlannerTest {
public boolean matches(Object item) {
assert item != null;
- RexCall rexCall = (RexCall) item;
+ RexNode rexCall = (RexNode) item;
relRowType = rexCall.getType();
expectedType = native2relationalType(Commons.typeFactory(), type);
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/datatypes/NumericQuantifyOperatorTypeCoercionTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/datatypes/NumericQuantifyOperatorTypeCoercionTest.java
new file mode 100644
index 00000000000..821593f929a
--- /dev/null
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/datatypes/NumericQuantifyOperatorTypeCoercionTest.java
@@ -0,0 +1,777 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.sql.engine.planner.datatypes;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.not;
+import static org.junit.jupiter.api.Assumptions.assumeFalse;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Stream;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.Join;
+import org.apache.calcite.rel.core.Project;
+import org.apache.calcite.rex.RexCall;
+import org.apache.calcite.rex.RexInputRef;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.rex.RexShuttle;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.util.Util;
+import org.apache.ignite.internal.sql.engine.planner.datatypes.utils.NumericPair;
+import org.apache.ignite.internal.sql.engine.planner.datatypes.utils.TypePair;
+import org.apache.ignite.internal.sql.engine.planner.datatypes.utils.Types;
+import org.apache.ignite.internal.sql.engine.rel.IgniteRel;
+import org.apache.ignite.internal.sql.engine.schema.IgniteSchema;
+import org.apache.ignite.internal.type.NativeTypeSpec;
+import org.apache.ignite.internal.type.NativeTypes;
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.Matchers;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+/**
+ * A set of tests to verify behavior of type coercion for quantify operators (ALL, SOME), when operands belongs to the NUMERIC type family.
+ *
+ * <p>This tests aim to help to understand in which cases implicit cast will be added to which operand.
+ */
+public class NumericQuantifyOperatorTypeCoercionTest extends BaseTypeCoercionTest {
+ private static Stream<Arguments> args() {
+ return Stream.of(
+ forTypePair(NumericPair.TINYINT_TINYINT)
+ .firstOpBeSame()
+ .secondOpBeSame(),
+
+ forTypePair(NumericPair.TINYINT_SMALLINT)
+ .firstOpMatches(ofType(NativeTypes.INT16))
+ .secondOpBeSame(),
+
+ forTypePair(NumericPair.TINYINT_INT)
+ .firstOpMatches(ofType(NativeTypes.INT32))
+ .secondOpBeSame(),
+
+ forTypePair(NumericPair.TINYINT_BIGINT)
+ .firstOpMatches(ofType(NativeTypes.INT64))
+ .secondOpBeSame(),
+
+ forTypePair(NumericPair.TINYINT_DECIMAL_1_0)
+ .firstOpMatches(ofType(Types.DECIMAL_3_0))
+ .secondOpMatches(ofType(Types.DECIMAL_3_0)),
+
+ forTypePair(NumericPair.TINYINT_DECIMAL_2_1)
+ .firstOpMatches(ofType(Types.DECIMAL_4_1))
+ .secondOpMatches(ofType(Types.DECIMAL_4_1)),
+
+ forTypePair(NumericPair.TINYINT_DECIMAL_4_3)
+ .firstOpMatches(ofType(Types.DECIMAL_6_3))
+ .secondOpMatches(ofType(Types.DECIMAL_6_3)),
+
+ forTypePair(NumericPair.TINYINT_DECIMAL_2_0)
+ .firstOpMatches(ofType(Types.DECIMAL_3_0))
+ .secondOpMatches(ofType(Types.DECIMAL_3_0)),
+
+ forTypePair(NumericPair.TINYINT_DECIMAL_3_1)
+ .firstOpMatches(ofType(Types.DECIMAL_4_1))
+ .secondOpMatches(ofType(Types.DECIMAL_4_1)),
+
+ forTypePair(NumericPair.TINYINT_DECIMAL_5_3)
+ .firstOpMatches(ofType(Types.DECIMAL_6_3))
+ .secondOpMatches(ofType(Types.DECIMAL_6_3)),
+
+ forTypePair(NumericPair.TINYINT_DECIMAL_5_0)
+ .firstOpMatches(ofType(Types.DECIMAL_5_0))
+ .secondOpMatches(ofType(Types.DECIMAL_5_0)),
+
+ forTypePair(NumericPair.TINYINT_DECIMAL_6_1)
+ .firstOpMatches(ofType(Types.DECIMAL_6_1))
+ .secondOpMatches(ofType(Types.DECIMAL_6_1)),
+
+ forTypePair(NumericPair.TINYINT_DECIMAL_8_3)
+ .firstOpMatches(ofType(Types.DECIMAL_8_3))
+ .secondOpMatches(ofType(Types.DECIMAL_8_3)),
+
+ forTypePair(NumericPair.TINYINT_REAL)
+ .firstOpMatches(ofType(NativeTypes.FLOAT))
+ .secondOpMatches(ofType(NativeTypes.FLOAT)),
+
+ forTypePair(NumericPair.TINYINT_DOUBLE)
+ .firstOpMatches(ofType(NativeTypes.DOUBLE))
+ .secondOpMatches(ofType(NativeTypes.DOUBLE)),
+
+
+ forTypePair(NumericPair.SMALLINT_SMALLINT)
+ .firstOpMatches(ofType(NativeTypes.INT16))
+ .secondOpMatches(ofType(NativeTypes.INT16)),
+
+ forTypePair(NumericPair.SMALLINT_INT)
+ .firstOpMatches(ofType(NativeTypes.INT32))
+ .secondOpMatches(ofType(NativeTypes.INT32)),
+
+ forTypePair(NumericPair.SMALLINT_BIGINT)
+ .firstOpMatches(ofType(NativeTypes.INT64))
+ .secondOpMatches(ofType(NativeTypes.INT64)),
+
+ forTypePair(NumericPair.SMALLINT_DECIMAL_1_0)
+ .firstOpMatches(ofType(Types.DECIMAL_5_0))
+ .secondOpMatches(ofType(Types.DECIMAL_5_0)),
+
+ forTypePair(NumericPair.SMALLINT_DECIMAL_2_1)
+ .firstOpMatches(ofType(Types.DECIMAL_6_1))
+ .secondOpMatches(ofType(Types.DECIMAL_6_1)),
+
+ forTypePair(NumericPair.SMALLINT_DECIMAL_4_3)
+ .firstOpMatches(ofType(Types.DECIMAL_8_3))
+ .secondOpMatches(ofType(Types.DECIMAL_8_3)),
+
+ forTypePair(NumericPair.SMALLINT_DECIMAL_2_0)
+ .firstOpMatches(ofType(Types.DECIMAL_5_0))
+ .secondOpMatches(ofType(Types.DECIMAL_5_0)),
+
+ forTypePair(NumericPair.SMALLINT_DECIMAL_3_1)
+ .firstOpMatches(ofType(Types.DECIMAL_6_1))
+ .secondOpMatches(ofType(Types.DECIMAL_6_1)),
+
+ forTypePair(NumericPair.SMALLINT_DECIMAL_5_3)
+ .firstOpMatches(ofType(Types.DECIMAL_8_3))
+ .secondOpMatches(ofType(Types.DECIMAL_8_3)),
+
+ forTypePair(NumericPair.SMALLINT_DECIMAL_5_0)
+ .firstOpMatches(ofType(Types.DECIMAL_5_0))
+ .secondOpMatches(ofType(Types.DECIMAL_5_0)),
+
+ forTypePair(NumericPair.SMALLINT_DECIMAL_6_1)
+ .firstOpMatches(ofType(Types.DECIMAL_6_1))
+ .secondOpMatches(ofType(Types.DECIMAL_6_1)),
+
+ forTypePair(NumericPair.SMALLINT_DECIMAL_8_3)
+ .firstOpMatches(ofType(Types.DECIMAL_8_3))
+ .secondOpMatches(ofType(Types.DECIMAL_8_3)),
+
+ forTypePair(NumericPair.SMALLINT_REAL)
+ .firstOpMatches(ofType(NativeTypes.FLOAT))
+ .secondOpMatches(ofType(NativeTypes.FLOAT)),
+
+ forTypePair(NumericPair.SMALLINT_DOUBLE)
+ .firstOpMatches(ofType(NativeTypes.DOUBLE))
+ .secondOpMatches(ofType(NativeTypes.DOUBLE)),
+
+
+ forTypePair(NumericPair.INT_INT)
+ .firstOpMatches(ofType(NativeTypes.INT32))
+ .secondOpMatches(ofType(NativeTypes.INT32)),
+
+ forTypePair(NumericPair.INT_BIGINT)
+ .firstOpMatches(ofType(NativeTypes.INT64))
+ .secondOpMatches(ofType(NativeTypes.INT64)),
+
+ forTypePair(NumericPair.INT_DECIMAL_1_0)
+ .firstOpMatches(ofType(Types.DECIMAL_10_0))
+ .secondOpMatches(ofType(Types.DECIMAL_10_0)),
+
+ forTypePair(NumericPair.INT_DECIMAL_2_1)
+ .firstOpMatches(ofType(Types.DECIMAL_11_1))
+ .secondOpMatches(ofType(Types.DECIMAL_11_1)),
+
+ forTypePair(NumericPair.INT_DECIMAL_4_3)
+ .firstOpMatches(ofType(Types.DECIMAL_13_3))
+ .secondOpMatches(ofType(Types.DECIMAL_13_3)),
+
+ forTypePair(NumericPair.INT_DECIMAL_2_0)
+ .firstOpMatches(ofType(Types.DECIMAL_10_0))
+ .secondOpMatches(ofType(Types.DECIMAL_10_0)),
+
+ forTypePair(NumericPair.INT_DECIMAL_3_1)
+ .firstOpMatches(ofType(Types.DECIMAL_11_1))
+ .secondOpMatches(ofType(Types.DECIMAL_11_1)),
+
+ forTypePair(NumericPair.INT_DECIMAL_5_3)
+ .firstOpMatches(ofType(Types.DECIMAL_13_3))
+ .secondOpMatches(ofType(Types.DECIMAL_13_3)),
+
+ forTypePair(NumericPair.INT_DECIMAL_5_0)
+ .firstOpMatches(ofType(Types.DECIMAL_10_0))
+ .secondOpMatches(ofType(Types.DECIMAL_10_0)),
+
+ forTypePair(NumericPair.INT_DECIMAL_6_1)
+ .firstOpMatches(ofType(Types.DECIMAL_11_1))
+ .secondOpMatches(ofType(Types.DECIMAL_11_1)),
+
+ forTypePair(NumericPair.INT_DECIMAL_8_3)
+ .firstOpMatches(ofType(Types.DECIMAL_13_3))
+ .secondOpMatches(ofType(Types.DECIMAL_13_3)),
+
+ forTypePair(NumericPair.INT_REAL)
+ .firstOpMatches(ofType(NativeTypes.FLOAT))
+ .secondOpMatches(ofType(NativeTypes.FLOAT)),
+
+ forTypePair(NumericPair.INT_DOUBLE)
+ .firstOpMatches(ofType(NativeTypes.DOUBLE))
+ .secondOpMatches(ofType(NativeTypes.DOUBLE)),
+
+
+ forTypePair(NumericPair.BIGINT_BIGINT)
+ .firstOpBeSame()
+ .secondOpBeSame(),
+
+ forTypePair(NumericPair.BIGINT_DECIMAL_1_0)
+ .firstOpMatches(ofType(Types.DECIMAL_19_0))
+ .secondOpMatches(ofType(Types.DECIMAL_19_0)),
+
+ forTypePair(NumericPair.BIGINT_DECIMAL_2_1)
+ .firstOpMatches(ofType(Types.DECIMAL_20_1))
+ .secondOpMatches(ofType(Types.DECIMAL_20_1)),
+
+ forTypePair(NumericPair.BIGINT_DECIMAL_4_3)
+ .firstOpMatches(ofType(Types.DECIMAL_22_3))
+ .secondOpMatches(ofType(Types.DECIMAL_22_3)),
+
+ forTypePair(NumericPair.BIGINT_DECIMAL_2_0)
+ .firstOpMatches(ofType(Types.DECIMAL_19_0))
+ .secondOpMatches(ofType(Types.DECIMAL_19_0)),
+
+ forTypePair(NumericPair.BIGINT_DECIMAL_3_1)
+ .firstOpMatches(ofType(Types.DECIMAL_20_1))
+ .secondOpMatches(ofType(Types.DECIMAL_20_1)),
+
+ forTypePair(NumericPair.BIGINT_DECIMAL_5_3)
+ .firstOpMatches(ofType(Types.DECIMAL_22_3))
+ .secondOpMatches(ofType(Types.DECIMAL_22_3)),
+
+ forTypePair(NumericPair.BIGINT_DECIMAL_5_0)
+ .firstOpMatches(ofType(Types.DECIMAL_19_0))
+ .secondOpMatches(ofType(Types.DECIMAL_19_0)),
+
+ forTypePair(NumericPair.BIGINT_DECIMAL_6_1)
+ .firstOpMatches(ofType(Types.DECIMAL_20_1))
+ .secondOpMatches(ofType(Types.DECIMAL_20_1)),
+
+ forTypePair(NumericPair.BIGINT_DECIMAL_8_3)
+ .firstOpMatches(ofType(Types.DECIMAL_22_3))
+ .secondOpMatches(ofType(Types.DECIMAL_22_3)),
+
+ forTypePair(NumericPair.BIGINT_REAL)
+ .firstOpMatches(ofType(NativeTypes.FLOAT))
+ .secondOpMatches(ofType(NativeTypes.FLOAT)),
+
+ forTypePair(NumericPair.BIGINT_DOUBLE)
+ .firstOpMatches(ofType(NativeTypes.DOUBLE))
+ .secondOpBeSame(),
+
+ forTypePair(NumericPair.DECIMAL_1_0_DECIMAL_1_0)
+ .firstOpMatches(ofType(Types.DECIMAL_1_0))
+ .secondOpMatches(ofType(Types.DECIMAL_1_0)),
+
+ forTypePair(NumericPair.DECIMAL_1_0_DECIMAL_2_1)
+ .firstOpMatches(ofType(Types.DECIMAL_2_1))
+ .secondOpMatches(ofType(Types.DECIMAL_2_1)),
+
+ forTypePair(NumericPair.DECIMAL_1_0_DECIMAL_4_3)
+ .firstOpMatches(ofType(Types.DECIMAL_4_3))
+ .secondOpMatches(ofType(Types.DECIMAL_4_3)),
+
+ forTypePair(NumericPair.DECIMAL_1_0_DECIMAL_2_0)
+ .firstOpMatches(ofType(Types.DECIMAL_2_0))
+ .secondOpMatches(ofType(Types.DECIMAL_2_0)),
+
+ forTypePair(NumericPair.DECIMAL_1_0_DECIMAL_3_1)
+ .firstOpMatches(ofType(Types.DECIMAL_3_1))
+ .secondOpMatches(ofType(Types.DECIMAL_3_1)),
+
+ forTypePair(NumericPair.DECIMAL_1_0_DECIMAL_5_3)
+ .firstOpMatches(ofType(Types.DECIMAL_5_3))
+ .secondOpMatches(ofType(Types.DECIMAL_5_3)),
+
+ forTypePair(NumericPair.DECIMAL_1_0_DECIMAL_5_0)
+ .firstOpMatches(ofType(Types.DECIMAL_5_0))
+ .secondOpMatches(ofType(Types.DECIMAL_5_0)),
+
+ forTypePair(NumericPair.DECIMAL_1_0_DECIMAL_6_1)
+ .firstOpMatches(ofType(Types.DECIMAL_6_1))
+ .secondOpMatches(ofType(Types.DECIMAL_6_1)),
+
+ forTypePair(NumericPair.DECIMAL_1_0_DECIMAL_8_3)
+ .firstOpMatches(ofType(Types.DECIMAL_8_3))
+ .secondOpMatches(ofType(Types.DECIMAL_8_3)),
+
+ forTypePair(NumericPair.DECIMAL_1_0_REAL)
+ .firstOpMatches(ofType(NativeTypes.DOUBLE))
+ .secondOpMatches(ofType(NativeTypes.DOUBLE)),
+
+ forTypePair(NumericPair.DECIMAL_1_0_DOUBLE)
+ .firstOpMatches(ofType(NativeTypes.DOUBLE))
+ .secondOpBeSame(),
+
+
+ forTypePair(NumericPair.DECIMAL_2_1_DECIMAL_2_1)
+ .firstOpMatches(ofType(Types.DECIMAL_2_1))
+ .secondOpMatches(ofType(Types.DECIMAL_2_1)),
+
+ forTypePair(NumericPair.DECIMAL_2_1_DECIMAL_4_3)
+ .firstOpMatches(ofType(Types.DECIMAL_4_3))
+ .secondOpMatches(ofType(Types.DECIMAL_4_3)),
+
+ forTypePair(NumericPair.DECIMAL_2_1_DECIMAL_2_0)
+ .firstOpMatches(ofType(Types.DECIMAL_3_1))
+ .secondOpMatches(ofType(Types.DECIMAL_3_1)),
+
+ forTypePair(NumericPair.DECIMAL_2_1_DECIMAL_3_1)
+ .firstOpMatches(ofType(Types.DECIMAL_3_1))
+ .secondOpMatches(ofType(Types.DECIMAL_3_1)),
+
+ forTypePair(NumericPair.DECIMAL_2_1_DECIMAL_5_3)
+ .firstOpMatches(ofType(Types.DECIMAL_5_3))
+ .secondOpMatches(ofType(Types.DECIMAL_5_3)),
+
+ forTypePair(NumericPair.DECIMAL_2_1_DECIMAL_5_0)
+ .firstOpMatches(ofType(Types.DECIMAL_6_1))
+ .secondOpMatches(ofType(Types.DECIMAL_6_1)),
+
+ forTypePair(NumericPair.DECIMAL_2_1_DECIMAL_6_1)
+ .firstOpMatches(ofType(Types.DECIMAL_6_1))
+ .secondOpMatches(ofType(Types.DECIMAL_6_1)),
+
+ forTypePair(NumericPair.DECIMAL_2_1_DECIMAL_8_3)
+ .firstOpMatches(ofType(Types.DECIMAL_8_3))
+ .secondOpMatches(ofType(Types.DECIMAL_8_3)),
+
+ forTypePair(NumericPair.DECIMAL_2_1_REAL)
+ .firstOpMatches(ofType(NativeTypes.DOUBLE))
+ .secondOpMatches(ofType(NativeTypes.DOUBLE)),
+
+ forTypePair(NumericPair.DECIMAL_2_1_DOUBLE)
+ .firstOpMatches(ofType(NativeTypes.DOUBLE))
+ .secondOpBeSame(),
+
+
+ forTypePair(NumericPair.DECIMAL_4_3_DECIMAL_4_3)
+ .firstOpMatches(ofType(Types.DECIMAL_4_3))
+ .secondOpMatches(ofType(Types.DECIMAL_4_3)),
+
+ forTypePair(NumericPair.DECIMAL_4_3_DECIMAL_2_0)
+ .firstOpMatches(ofType(Types.DECIMAL_5_3))
+ .secondOpMatches(ofType(Types.DECIMAL_5_3)),
+
+ forTypePair(NumericPair.DECIMAL_4_3_DECIMAL_3_1)
+ .firstOpMatches(ofType(Types.DECIMAL_5_3))
+ .secondOpMatches(ofType(Types.DECIMAL_5_3)),
+
+ forTypePair(NumericPair.DECIMAL_4_3_DECIMAL_5_3)
+ .firstOpMatches(ofType(Types.DECIMAL_5_3))
+ .secondOpMatches(ofType(Types.DECIMAL_5_3)),
+
+ forTypePair(NumericPair.DECIMAL_4_3_DECIMAL_5_0)
+ .firstOpMatches(ofType(Types.DECIMAL_8_3))
+ .secondOpMatches(ofType(Types.DECIMAL_8_3)),
+
+ forTypePair(NumericPair.DECIMAL_4_3_DECIMAL_6_1)
+ .firstOpMatches(ofType(Types.DECIMAL_8_3))
+ .secondOpMatches(ofType(Types.DECIMAL_8_3)),
+
+ forTypePair(NumericPair.DECIMAL_4_3_DECIMAL_8_3)
+ .firstOpMatches(ofType(Types.DECIMAL_8_3))
+ .secondOpMatches(ofType(Types.DECIMAL_8_3)),
+
+ forTypePair(NumericPair.DECIMAL_4_3_REAL)
+ .firstOpMatches(ofType(NativeTypes.DOUBLE))
+ .secondOpMatches(ofType(NativeTypes.DOUBLE)),
+
+ forTypePair(NumericPair.DECIMAL_4_3_DOUBLE)
+ .firstOpMatches(ofType(NativeTypes.DOUBLE))
+ .secondOpBeSame(),
+
+
+ forTypePair(NumericPair.DECIMAL_2_0_DECIMAL_2_0)
+ .firstOpMatches(ofType(Types.DECIMAL_2_0))
+ .secondOpMatches(ofType(Types.DECIMAL_2_0)),
+
+ forTypePair(NumericPair.DECIMAL_2_0_DECIMAL_3_1)
+ .firstOpMatches(ofType(Types.DECIMAL_3_1))
+ .secondOpMatches(ofType(Types.DECIMAL_3_1)),
+
+ forTypePair(NumericPair.DECIMAL_2_0_DECIMAL_5_3)
+ .firstOpMatches(ofType(Types.DECIMAL_5_3))
+ .secondOpMatches(ofType(Types.DECIMAL_5_3)),
+
+ forTypePair(NumericPair.DECIMAL_2_0_DECIMAL_5_0)
+ .firstOpMatches(ofType(Types.DECIMAL_5_0))
+ .secondOpMatches(ofType(Types.DECIMAL_5_0)),
+
+ forTypePair(NumericPair.DECIMAL_2_0_DECIMAL_6_1)
+ .firstOpMatches(ofType(Types.DECIMAL_6_1))
+ .secondOpMatches(ofType(Types.DECIMAL_6_1)),
+
+ forTypePair(NumericPair.DECIMAL_2_0_DECIMAL_8_3)
+ .firstOpMatches(ofType(Types.DECIMAL_8_3))
+ .secondOpMatches(ofType(Types.DECIMAL_8_3)),
+
+ forTypePair(NumericPair.DECIMAL_2_0_REAL)
+ .firstOpMatches(ofType(NativeTypes.DOUBLE))
+ .secondOpMatches(ofType(NativeTypes.DOUBLE)),
+
+ forTypePair(NumericPair.DECIMAL_2_0_DOUBLE)
+ .firstOpMatches(ofType(NativeTypes.DOUBLE))
+ .secondOpBeSame(),
+
+
+ forTypePair(NumericPair.DECIMAL_3_1_DECIMAL_3_1)
+ .firstOpMatches(ofType(Types.DECIMAL_3_1))
+ .secondOpMatches(ofType(Types.DECIMAL_3_1)),
+
+ forTypePair(NumericPair.DECIMAL_3_1_DECIMAL_5_3)
+ .firstOpMatches(ofType(Types.DECIMAL_5_3))
+ .secondOpMatches(ofType(Types.DECIMAL_5_3)),
+
+ forTypePair(NumericPair.DECIMAL_3_1_DECIMAL_5_0)
+ .firstOpMatches(ofType(Types.DECIMAL_6_1))
+ .secondOpMatches(ofType(Types.DECIMAL_6_1)),
+
+ forTypePair(NumericPair.DECIMAL_3_1_DECIMAL_6_1)
+ .firstOpMatches(ofType(Types.DECIMAL_6_1))
+ .secondOpMatches(ofType(Types.DECIMAL_6_1)),
+
+ forTypePair(NumericPair.DECIMAL_3_1_DECIMAL_8_3)
+ .firstOpMatches(ofType(Types.DECIMAL_8_3))
+ .secondOpMatches(ofType(Types.DECIMAL_8_3)),
+
+ forTypePair(NumericPair.DECIMAL_3_1_REAL)
+ .firstOpMatches(ofType(NativeTypes.DOUBLE))
+ .secondOpMatches(ofType(NativeTypes.DOUBLE)),
+
+ forTypePair(NumericPair.DECIMAL_3_1_DOUBLE)
+ .firstOpMatches(ofType(NativeTypes.DOUBLE))
+ .secondOpBeSame(),
+
+
+ forTypePair(NumericPair.DECIMAL_5_3_DECIMAL_5_3)
+ .firstOpMatches(ofType(Types.DECIMAL_5_3))
+ .secondOpMatches(ofType(Types.DECIMAL_5_3)),
+
+ forTypePair(NumericPair.DECIMAL_5_3_DECIMAL_5_0)
+ .firstOpMatches(ofType(Types.DECIMAL_8_3))
+ .secondOpMatches(ofType(Types.DECIMAL_8_3)),
+
+ forTypePair(NumericPair.DECIMAL_5_3_DECIMAL_6_1)
+ .firstOpMatches(ofType(Types.DECIMAL_8_3))
+ .secondOpMatches(ofType(Types.DECIMAL_8_3)),
+
+ forTypePair(NumericPair.DECIMAL_5_3_DECIMAL_8_3)
+ .firstOpMatches(ofType(Types.DECIMAL_8_3))
+ .secondOpMatches(ofType(Types.DECIMAL_8_3)),
+
+ forTypePair(NumericPair.DECIMAL_5_3_REAL)
+ .firstOpMatches(ofType(NativeTypes.DOUBLE))
+ .secondOpMatches(ofType(NativeTypes.DOUBLE)),
+
+ forTypePair(NumericPair.DECIMAL_5_3_DOUBLE)
+ .firstOpMatches(ofType(NativeTypes.DOUBLE))
+ .secondOpBeSame(),
+
+
+ forTypePair(NumericPair.DECIMAL_5_0_DECIMAL_5_0)
+ .firstOpMatches(ofType(Types.DECIMAL_5_0))
+ .secondOpMatches(ofType(Types.DECIMAL_5_0)),
+
+ forTypePair(NumericPair.DECIMAL_5_0_DECIMAL_6_1)
+ .firstOpMatches(ofType(Types.DECIMAL_6_1))
+ .secondOpMatches(ofType(Types.DECIMAL_6_1)),
+
+ forTypePair(NumericPair.DECIMAL_5_0_DECIMAL_8_3)
+ .firstOpMatches(ofType(Types.DECIMAL_8_3))
+ .secondOpMatches(ofType(Types.DECIMAL_8_3)),
+
+ forTypePair(NumericPair.DECIMAL_5_0_REAL)
+ .firstOpMatches(ofType(NativeTypes.DOUBLE))
+ .secondOpMatches(ofType(NativeTypes.DOUBLE)),
+
+ forTypePair(NumericPair.DECIMAL_5_0_DOUBLE)
+ .firstOpMatches(ofType(NativeTypes.DOUBLE))
+ .secondOpBeSame(),
+
+
+ forTypePair(NumericPair.DECIMAL_6_1_DECIMAL_6_1)
+ .firstOpMatches(ofType(Types.DECIMAL_6_1))
+ .secondOpMatches(ofType(Types.DECIMAL_6_1)),
+
+ forTypePair(NumericPair.DECIMAL_6_1_DECIMAL_8_3)
+ .firstOpMatches(ofType(Types.DECIMAL_8_3))
+ .secondOpMatches(ofType(Types.DECIMAL_8_3)),
+
+ forTypePair(NumericPair.DECIMAL_6_1_REAL)
+ .firstOpMatches(ofType(NativeTypes.DOUBLE))
+ .secondOpMatches(ofType(NativeTypes.DOUBLE)),
+
+ forTypePair(NumericPair.DECIMAL_6_1_DOUBLE)
+ .firstOpMatches(ofType(NativeTypes.DOUBLE))
+ .secondOpBeSame(),
+
+
+ forTypePair(NumericPair.DECIMAL_8_3_DECIMAL_8_3)
+ .firstOpMatches(ofType(Types.DECIMAL_8_3))
+ .secondOpMatches(ofType(Types.DECIMAL_8_3)),
+
+ forTypePair(NumericPair.DECIMAL_8_3_REAL)
+ .firstOpMatches(ofType(NativeTypes.DOUBLE))
+ .secondOpMatches(ofType(NativeTypes.DOUBLE)),
+
+ forTypePair(NumericPair.DECIMAL_8_3_DOUBLE)
+ .firstOpMatches(ofType(NativeTypes.DOUBLE))
+ .secondOpBeSame(),
+
+
+ forTypePair(NumericPair.REAL_REAL)
+ .firstOpMatches(ofType(NativeTypes.FLOAT))
+ .secondOpMatches(ofType(NativeTypes.FLOAT)),
+
+ forTypePair(NumericPair.REAL_DOUBLE)
+ .firstOpMatches(ofType(NativeTypes.DOUBLE))
+ .secondOpMatches(ofType(NativeTypes.DOUBLE)),
+
+
+ forTypePair(NumericPair.DOUBLE_DOUBLE)
+ .firstOpMatches(ofType(NativeTypes.DOUBLE))
+ .secondOpMatches(ofType(NativeTypes.DOUBLE))
+ );
+ }
+
+ @ParameterizedTest
+ @MethodSource("args")
+ public void equalsTo(
+ TypePair typePair,
+ Matcher<RexNode> firstOperandMatcher,
+ Matcher<RexNode> secondOperandMatcher
+ ) throws Exception {
+ // TODO: IGNITE-24706 remove this assumption
+ assumeFalse(typePair.first().spec() == NativeTypeSpec.DECIMAL || typePair.second().spec() == NativeTypeSpec.DECIMAL);
+
+ IgniteSchema schema = createSchemaWithTwoColumnTable(typePair.first(), typePair.second());
+
+ assertPlan("SELECT c1 = SOME(SELECT c2 FROM t) FROM t", schema,
+ equalsToSomeOperatorMatcher(firstOperandMatcher, secondOperandMatcher)::matches, List.of());
+ assertPlan("SELECT c2 = SOME(SELECT c1 FROM t) FROM t", schema,
+ equalsToSomeOperatorMatcher(secondOperandMatcher, firstOperandMatcher)::matches, List.of());
+ assertPlan("SELECT c1 = ALL(SELECT c2 FROM t) FROM t", schema,
+ quantifyOperatorMatcher(firstOperandMatcher, secondOperandMatcher, 0, 3)::matches, List.of());
+ assertPlan("SELECT c2 = ALL(SELECT c1 FROM t) FROM t", schema,
+ quantifyOperatorMatcher(firstOperandMatcher, secondOperandMatcher, 0, 3)::matches, List.of());
+ }
+
+ @ParameterizedTest
+ @MethodSource("args")
+ public void lessThan(
+ TypePair typePair,
+ Matcher<RexNode> firstOperandMatcher,
+ Matcher<RexNode> secondOperandMatcher
+ ) throws Exception {
+ // TODO: IGNITE-24706 remove this assumption
+ assumeFalse(typePair.first().spec() == NativeTypeSpec.DECIMAL || typePair.second().spec() == NativeTypeSpec.DECIMAL);
+
+ IgniteSchema schema = createSchemaWithTwoColumnTable(typePair.first(), typePair.second());
+
+ assertPlan("SELECT c1 < SOME(SELECT c2 FROM t) FROM t", schema,
+ quantifyOperatorMatcher(firstOperandMatcher, secondOperandMatcher, 0, 1)::matches, List.of());
+ assertPlan("SELECT c2 < SOME(SELECT c1 FROM t) FROM t", schema,
+ quantifyOperatorMatcher(secondOperandMatcher, firstOperandMatcher, 0, 1)::matches, List.of());
+ assertPlan("SELECT c1 < ALL(SELECT c2 FROM t) FROM t", schema,
+ quantifyOperatorMatcher(firstOperandMatcher, secondOperandMatcher, 0, 1)::matches, List.of());
+ assertPlan("SELECT c2 < ALL(SELECT c1 FROM t) FROM t", schema,
+ quantifyOperatorMatcher(firstOperandMatcher, secondOperandMatcher, 0, 1)::matches, List.of());
+ }
+
+ @ParameterizedTest
+ @MethodSource("args")
+ public void lessThanOrEqual(
+ TypePair typePair,
+ Matcher<RexNode> firstOperandMatcher,
+ Matcher<RexNode> secondOperandMatcher
+ ) throws Exception {
+ // TODO: IGNITE-24706 remove this assumption
+ assumeFalse(typePair.first().spec() == NativeTypeSpec.DECIMAL || typePair.second().spec() == NativeTypeSpec.DECIMAL);
+
+ IgniteSchema schema = createSchemaWithTwoColumnTable(typePair.first(), typePair.second());
+
+ assertPlan("SELECT c1 <= SOME(SELECT c2 FROM t) FROM t", schema,
+ quantifyOperatorMatcher(firstOperandMatcher, secondOperandMatcher, 0, 1)::matches, List.of());
+ assertPlan("SELECT c2 <= SOME(SELECT c1 FROM t) FROM t", schema,
+ quantifyOperatorMatcher(secondOperandMatcher, firstOperandMatcher, 0, 1)::matches, List.of());
+ assertPlan("SELECT c1 <= ALL(SELECT c2 FROM t) FROM t", schema,
+ quantifyOperatorMatcher(firstOperandMatcher, secondOperandMatcher, 0, 1)::matches, List.of());
+ assertPlan("SELECT c2 <= ALL(SELECT c1 FROM t) FROM t", schema,
+ quantifyOperatorMatcher(firstOperandMatcher, secondOperandMatcher, 0, 1)::matches, List.of());
+ }
+
+ @ParameterizedTest
+ @MethodSource("args")
+ public void greaterThan(
+ TypePair typePair,
+ Matcher<RexNode> firstOperandMatcher,
+ Matcher<RexNode> secondOperandMatcher
+ ) throws Exception {
+ // TODO: IGNITE-24706 remove this assumption
+ assumeFalse(typePair.first().spec() == NativeTypeSpec.DECIMAL || typePair.second().spec() == NativeTypeSpec.DECIMAL);
+
+ IgniteSchema schema = createSchemaWithTwoColumnTable(typePair.first(), typePair.second());
+
+ assertPlan("SELECT c1 > SOME(SELECT c2 FROM t) FROM t", schema,
+ quantifyOperatorMatcher(firstOperandMatcher, secondOperandMatcher, 0, 1)::matches, List.of());
+ assertPlan("SELECT c2 > SOME(SELECT c1 FROM t) FROM t", schema,
+ quantifyOperatorMatcher(secondOperandMatcher, firstOperandMatcher, 0, 1)::matches, List.of());
+ assertPlan("SELECT c1 > ALL(SELECT c2 FROM t) FROM t", schema,
+ quantifyOperatorMatcher(firstOperandMatcher, secondOperandMatcher, 0, 1)::matches, List.of());
+ assertPlan("SELECT c2 > ALL(SELECT c1 FROM t) FROM t", schema,
+ quantifyOperatorMatcher(firstOperandMatcher, secondOperandMatcher, 0, 1)::matches, List.of());
+ }
+
+ @ParameterizedTest
+ @MethodSource("args")
+ public void greaterThanOrEqual(
+ TypePair typePair,
+ Matcher<RexNode> firstOperandMatcher,
+ Matcher<RexNode> secondOperandMatcher
+ ) throws Exception {
+ // TODO: IGNITE-24706 remove this assumption
+ assumeFalse(typePair.first().spec() == NativeTypeSpec.DECIMAL || typePair.second().spec() == NativeTypeSpec.DECIMAL);
+
+ IgniteSchema schema = createSchemaWithTwoColumnTable(typePair.first(), typePair.second());
+
+ assertPlan("SELECT c1 >= SOME(SELECT c2 FROM t) FROM t", schema,
+ quantifyOperatorMatcher(firstOperandMatcher, secondOperandMatcher, 0, 1)::matches, List.of());
+ assertPlan("SELECT c2 >= SOME(SELECT c1 FROM t) FROM t", schema,
+ quantifyOperatorMatcher(secondOperandMatcher, firstOperandMatcher, 0, 1)::matches, List.of());
+ assertPlan("SELECT c1 >= ALL(SELECT c2 FROM t) FROM t", schema,
+ quantifyOperatorMatcher(firstOperandMatcher, secondOperandMatcher, 0, 1)::matches, List.of());
+ assertPlan("SELECT c2 >= ALL(SELECT c1 FROM t) FROM t", schema,
+ quantifyOperatorMatcher(firstOperandMatcher, secondOperandMatcher, 0, 1)::matches, List.of());
+ }
+
+ /**
+ * This test ensures that {@link #args()} doesn't miss any type pair from {@link NumericPair}.
+ */
+ @Test
+ void argsIncludesAllTypePairs() {
+ checkIncludesAllNumericTypePairs(args());
+ }
+
+ private static Matcher<IgniteRel> equalsToSomeOperatorMatcher(
+ Matcher<RexNode> first, Matcher<RexNode> second
+ ) {
+ return new BaseMatcher<>() {
+ @Override
+ public boolean matches(Object actual) {
+ RelNode projectRel = (RelNode) actual;
+ Join joinRel = (Join) projectRel.getInput(0);
+
+ RexNode comparison = joinRel.getCondition();
+
+ assertThat(comparison, instanceOf(RexCall.class));
+
+ RexCall comparisonCall = (RexCall) comparison;
+
+ RexNode leftOperand = comparisonCall.getOperands().get(0);
+ RexNode rightOperand = comparisonCall.getOperands().get(1);
+
+ assertThat(leftOperand, first);
+ assertThat(rightOperand, second);
+
+ return true;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+
+ }
+ };
+ }
+
+ private static Matcher<IgniteRel> quantifyOperatorMatcher(
+ Matcher<RexNode> first, Matcher<RexNode> second, int firstOpIdx, int secondOpIdx
+ ) {
+ return new BaseMatcher<>() {
+ @Override
+ public boolean matches(Object actual) {
+ RexNode expression = ((Project) actual).getProjects().get(0);
+
+ List<RexNode> comparisonToVerify = findComparison(expression, firstOpIdx, secondOpIdx);
+
+ assertThat(comparisonToVerify, not(Matchers.empty()));
+
+ for (RexNode comparison : comparisonToVerify) {
+ assertThat(comparison, instanceOf(RexCall.class));
+
+ RexCall comparisonCall = (RexCall) comparison;
+
+ RexNode leftOperand = comparisonCall.getOperands().get(0);
+ RexNode rightOperand = comparisonCall.getOperands().get(1);
+
+ assertThat(leftOperand, first);
+ assertThat(rightOperand, second);
+ }
+
+ return true;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+
+ }
+ };
+ }
+
+ /** Finds a comparison call comparing input references with provided indexes. */
+ private static List<RexNode> findComparison(RexNode expression, int firstOpIdx, int secondOpIdx) {
+ List<RexNode> result = new ArrayList<>();
+ new RexShuttle() {
+ @Override
+ public RexNode visitCall(RexCall call) {
+ if (call.getKind().belongsTo(SqlKind.BINARY_COMPARISON)) {
+ result.add(call);
+
+ return call;
+ }
+
+ return super.visitCall(call);
+ }
+ }.apply(expression);
+
+ result.removeIf(cmp -> {
+ try {
+ new RexShuttle() {
+ @Override
+ public RexNode visitInputRef(RexInputRef inputRef) {
+ int column = inputRef.getIndex();
+ if (column != firstOpIdx && column != secondOpIdx) {
+ throw Util.FoundOne.NULL;
+ }
+
+ return inputRef;
+ }
+ }.apply(cmp);
+ } catch (Util.FoundOne ignored) {
+ return true;
+ }
+
+ return false;
+ });
+
+ return result;
+ }
+}