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/12/24 14:16:06 UTC
(ignite-3) branch ignite-26636 created (now a469b98baa6)
This is an automated email from the ASF dual-hosted git repository.
korlov pushed a change to branch ignite-26636
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
at a469b98baa6 IGNITE-26636 Introduce basic API for expression execution and predicate implementation
This branch includes the following new commits:
new a469b98baa6 IGNITE-26636 Introduce basic API for expression execution and predicate implementation
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(ignite-3) 01/01: IGNITE-26636 Introduce basic API for expression execution and predicate implementation
Posted by ko...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
korlov pushed a commit to branch ignite-26636
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
commit a469b98baa68bf5dd317cbebbadd5d7da6edda0b
Author: korlov42 <ko...@apache.org>
AuthorDate: Fri Dec 19 13:46:23 2025 +0200
IGNITE-26636 Introduce basic API for expression execution and predicate implementation
---
.../engine/api/expressions/ContextBuilder.java} | 22 +-
.../engine/api/expressions/EvaluationContext.java} | 21 +-
.../ExpressionEvaluationException.java} | 22 +-
.../engine/api/expressions/ExpressionFactory.java | 106 ++
.../expressions/ExpressionParsingException.java} | 24 +-
.../ExpressionValidationException.java} | 32 +-
.../engine/api/expressions/IgnitePredicate.java | 38 +
.../sql/engine/api/expressions}/RowAccessor.java | 2 +-
.../sql/engine/api/expressions}/RowFactory.java | 2 +-
.../engine/api/expressions}/RowFactoryFactory.java | 2 +-
.../internal/lang/SqlExceptionMapperUtil.java | 4 +
.../internal/sql/engine/SqlQueryProcessor.java | 18 +-
.../sql/engine/exec/ExchangeServiceImpl.java | 29 +-
.../internal/sql/engine/exec/ExecutionContext.java | 1 +
.../sql/engine/exec/ExecutionServiceImpl.java | 1 +
.../exec/ProjectedTableRowConverterImpl.java | 1 +
.../internal/sql/engine/exec/RowHandler.java | 1 +
.../internal/sql/engine/exec/ScannableTable.java | 1 +
.../sql/engine/exec/ScannableTableImpl.java | 1 +
.../sql/engine/exec/SqlEvaluationContext.java | 3 +
.../internal/sql/engine/exec/SqlRowHandler.java | 4 +-
.../sql/engine/exec/TableRowConverter.java | 1 +
.../sql/engine/exec/TableRowConverterImpl.java | 1 +
.../sql/engine/exec/UpdatableTableImpl.java | 1 +
.../internal/sql/engine/exec/exp/CodegenUtils.java | 52 +
.../sql/engine/exec/exp/ComparatorImplementor.java | 2 +-
.../engine/exec/exp/JoinPredicateImplementor.java | 14 +-
.../engine/exec/exp/JoinProjectionImplementor.java | 16 +-
.../sql/engine/exec/exp/PredicateImplementor.java | 14 +-
.../sql/engine/exec/exp/ProjectionImplementor.java | 16 +-
.../engine/exec/exp/RowProviderImplementor.java | 16 +-
.../sql/engine/exec/exp/ScalarImplementor.java | 12 +-
.../engine/exec/exp/agg/AccumulatorsFactory.java | 2 +-
.../exec/exp/func/SystemRangeTableFunction.java | 2 +-
.../internal/sql/engine/exec/rel/AbstractNode.java | 9 +-
.../sql/engine/exec/rel/AbstractSetOpNode.java | 2 +-
.../exec/rel/CorrelatedNestedLoopJoinNode.java | 2 +-
.../sql/engine/exec/rel/DataSourceScanNode.java | 2 +-
.../sql/engine/exec/rel/HashAggregateNode.java | 2 +-
.../internal/sql/engine/exec/rel/HashJoinNode.java | 2 +-
.../ignite/internal/sql/engine/exec/rel/Inbox.java | 2 +-
.../sql/engine/exec/rel/IndexScanNode.java | 2 +-
.../sql/engine/exec/rel/IntersectNode.java | 2 +-
.../sql/engine/exec/rel/MergeJoinNode.java | 2 +-
.../internal/sql/engine/exec/rel/MinusNode.java | 2 +-
.../internal/sql/engine/exec/rel/ModifyNode.java | 2 +-
.../sql/engine/exec/rel/NestedLoopJoinNode.java | 2 +-
.../sql/engine/exec/rel/SortAggregateNode.java | 2 +-
.../sql/engine/exec/rel/TableScanNode.java | 2 +-
.../RowBasedScope.java} | 27 +-
.../sql/engine/expressions/RowFieldNamespace.java | 136 +++
.../sql/engine/expressions/RowNamespace.java | 143 +++
.../expressions/SqlExpressionFactoryAdapter.java | 318 ++++++
.../expressions/ToInternalGenericAdapter.java | 84 ++
.../engine/prepare/IgniteSqlToRelConvertor.java | 26 +
.../sql/engine/prepare/KeyValueGetPlan.java | 2 +-
.../internal/sql/engine/sql/IgniteSqlParser.java | 19 +-
.../internal/sql/engine/util/IgniteMethod.java | 4 +-
.../ignite/internal/sql/engine/util/TypeUtils.java | 23 +-
.../sql/engine/exec/DummyScannableTable.java | 1 +
.../exec/IdentityDistributionFunctionSelfTest.java | 1 +
.../sql/engine/exec/PartitionsResolutionTest.java | 1 +
.../exec/ProjectedTableRowConverterSelfTest.java | 1 +
.../sql/engine/exec/TableRowConverterSelfTest.java | 1 +
.../sql/engine/exec/rel/AbstractExecutionTest.java | 2 +-
.../engine/exec/rel/AbstractJoinExecutionTest.java | 2 +-
.../exec/rel/AbstractSetOpExecutionTest.java | 2 +-
.../sql/engine/exec/rel/AsyncRootNodeTest.java | 4 +-
.../sql/engine/exec/rel/BaseAggregateTest.java | 2 +-
.../rel/CorrelatedNestedLoopJoinExecutionTest.java | 2 +-
.../exec/rel/DataSourceScanNodeExecutionTest.java | 4 +-
.../sql/engine/exec/rel/ExchangeExecutionTest.java | 2 +-
.../sql/engine/exec/rel/ExecutionTest.java | 2 +-
.../sql/engine/exec/rel/FilterExecutionTest.java | 2 +-
.../exec/rel/HashAggregateExecutionTest.java | 2 +-
.../rel/HashAggregateSingleGroupExecutionTest.java | 4 +-
.../exec/rel/HashIndexSpoolExecutionTest.java | 2 +-
.../exec/rel/IndexScanNodeExecutionTest.java | 4 +-
.../engine/exec/rel/IntersectExecutionTest.java | 2 +-
.../sql/engine/exec/rel/LimitExecutionTest.java | 2 +-
.../engine/exec/rel/MergeJoinExecutionTest.java | 2 +-
.../engine/exec/rel/ModifyNodeExecutionTest.java | 6 +-
.../sql/engine/exec/rel/ScanNodeExecutionTest.java | 2 +-
.../engine/exec/rel/ScannableTableSelfTest.java | 2 +-
.../exec/rel/SortAggregateExecutionTest.java | 2 +-
.../exec/rel/SortedIndexSpoolExecutionTest.java | 2 +-
.../exec/rel/SystemRangeTableFunctionTest.java | 2 +-
.../exec/rel/TableScanNodeExecutionTest.java | 4 +-
.../engine/exec/rel/TableSpoolExecutionTest.java | 2 +-
.../sql/engine/exec/row/SqlRowHandlerTest.java | 4 +-
.../expressions/AbstractExpressionFactoryTest.java | 64 ++
.../engine/expressions/IgnitePredicateTest.java | 1172 ++++++++++++++++++++
.../sql/engine/framework/ArrayRowHandler.java | 6 +-
.../sql/engine/framework/TestBuilders.java | 2 +-
.../sql/engine/framework/TestClusterTest.java | 2 +-
.../internal/sql/engine/framework/TestNode.java | 2 +-
96 files changed, 2403 insertions(+), 221 deletions(-)
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/RowAccessor.java b/modules/sql-engine-api/src/main/java/org/apache/ignite/internal/sql/engine/api/expressions/ContextBuilder.java
similarity index 55%
copy from modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/RowAccessor.java
copy to modules/sql-engine-api/src/main/java/org/apache/ignite/internal/sql/engine/api/expressions/ContextBuilder.java
index ad3b33e62e3..46e9e14a069 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/RowAccessor.java
+++ b/modules/sql-engine-api/src/main/java/org/apache/ignite/internal/sql/engine/api/expressions/ContextBuilder.java
@@ -15,22 +15,22 @@
* limitations under the License.
*/
-package org.apache.ignite.internal.sql.engine.exec;
+package org.apache.ignite.internal.sql.engine.api.expressions;
-import org.jetbrains.annotations.Nullable;
+import java.util.function.LongSupplier;
/**
- * Class representing a reader for the input row.
+ * Builder interface for constructing {@link EvaluationContext} instances.
*
- * @param <RowT> Type of the supported row.
+ * @param <RowT> the type of row data that will be processed in the evaluation context
*/
-public interface RowAccessor<RowT> {
- /** Reads the specified field of the given row. */
- @Nullable Object get(int field, RowT row);
+public interface ContextBuilder<RowT> {
+ /** Specifies time provider to use in CURRENT_TIMESTAMP function. */
+ ContextBuilder<RowT> timeProvider(LongSupplier timeProvider);
- /** Checks whether the given field is {@code null}. */
- boolean isNull(int field, RowT row);
+ /** Specifies row accessor to read columns of input row, if any. */
+ ContextBuilder<RowT> rowAccessor(RowAccessor<RowT> rowAccessor);
- /** Return columns count contained in the incoming row. */
- int columnsCount(RowT row);
+ /** Returns created context. */
+ EvaluationContext<RowT> build();
}
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/RowAccessor.java b/modules/sql-engine-api/src/main/java/org/apache/ignite/internal/sql/engine/api/expressions/EvaluationContext.java
similarity index 60%
copy from modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/RowAccessor.java
copy to modules/sql-engine-api/src/main/java/org/apache/ignite/internal/sql/engine/api/expressions/EvaluationContext.java
index ad3b33e62e3..480432273ab 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/RowAccessor.java
+++ b/modules/sql-engine-api/src/main/java/org/apache/ignite/internal/sql/engine/api/expressions/EvaluationContext.java
@@ -15,22 +15,15 @@
* limitations under the License.
*/
-package org.apache.ignite.internal.sql.engine.exec;
-
-import org.jetbrains.annotations.Nullable;
+package org.apache.ignite.internal.sql.engine.api.expressions;
/**
- * Class representing a reader for the input row.
+ * Marker interface for context enclosing necessary metadata to evaluate expressions.
+ *
+ * <p>Every expression accepts only contexts created by the same {@link ExpressionFactory}.
*
- * @param <RowT> Type of the supported row.
+ * @see ExpressionFactory
+ * @see ExpressionFactory#contextBuilder()
*/
-public interface RowAccessor<RowT> {
- /** Reads the specified field of the given row. */
- @Nullable Object get(int field, RowT row);
-
- /** Checks whether the given field is {@code null}. */
- boolean isNull(int field, RowT row);
-
- /** Return columns count contained in the incoming row. */
- int columnsCount(RowT row);
+public interface EvaluationContext<RowT> {
}
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/RowAccessor.java b/modules/sql-engine-api/src/main/java/org/apache/ignite/internal/sql/engine/api/expressions/ExpressionEvaluationException.java
similarity index 60%
copy from modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/RowAccessor.java
copy to modules/sql-engine-api/src/main/java/org/apache/ignite/internal/sql/engine/api/expressions/ExpressionEvaluationException.java
index ad3b33e62e3..f80b4a6ea27 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/RowAccessor.java
+++ b/modules/sql-engine-api/src/main/java/org/apache/ignite/internal/sql/engine/api/expressions/ExpressionEvaluationException.java
@@ -15,22 +15,18 @@
* limitations under the License.
*/
-package org.apache.ignite.internal.sql.engine.exec;
-
-import org.jetbrains.annotations.Nullable;
+package org.apache.ignite.internal.sql.engine.api.expressions;
/**
- * Class representing a reader for the input row.
+ * Exception thrown when an error occurs during expression evaluation.
*
- * @param <RowT> Type of the supported row.
+ * <p>Typical examples are "Division by zero" or "Invalid input string for type INTEGER: 'foo'".
*/
-public interface RowAccessor<RowT> {
- /** Reads the specified field of the given row. */
- @Nullable Object get(int field, RowT row);
-
- /** Checks whether the given field is {@code null}. */
- boolean isNull(int field, RowT row);
+public class ExpressionEvaluationException extends Exception {
+ private static final long serialVersionUID = 2040204247361408378L;
- /** Return columns count contained in the incoming row. */
- int columnsCount(RowT row);
+ /** Constructs the exception. */
+ public ExpressionEvaluationException(String message) {
+ super(message, null, true, false);
+ }
}
diff --git a/modules/sql-engine-api/src/main/java/org/apache/ignite/internal/sql/engine/api/expressions/ExpressionFactory.java b/modules/sql-engine-api/src/main/java/org/apache/ignite/internal/sql/engine/api/expressions/ExpressionFactory.java
new file mode 100644
index 00000000000..92031979d3d
--- /dev/null
+++ b/modules/sql-engine-api/src/main/java/org/apache/ignite/internal/sql/engine/api/expressions/ExpressionFactory.java
@@ -0,0 +1,106 @@
+/*
+ * 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.api.expressions;
+
+import org.apache.ignite.internal.type.StructNativeType;
+
+/**
+ * Factory for creating and compiling expressions from string representations.
+ *
+ * <p>This factory provides methods to parse, validate, and compile string-based expressions into executable predicates and other
+ * expression types. Expressions are evaluated against structured row data using an {@link EvaluationContext} created by this factory's
+ * {@link #contextBuilder()} method.
+ *
+ * <h2>Expression Syntax and Normalization</h2>
+ *
+ * <p>Expressions follow SQL-like syntax with support for identifiers, operators, and functions. All identifiers are normalized to
+ * uppercase unless enclosed in double quotes. This means {@code column_name}, {@code COLUMN_NAME}, and {@code CoLuMn_NaMe} are treated as
+ * identical, while {@code "column_name"} preserves exact case.
+ *
+ * <h2>Namespace and Column References</h2>
+ *
+ * <p>Expressions operate within namespaces that correspond to input data sources. For single-row predicates, an implicit "INPUT" namespace
+ * is created, allowing both simple and qualified column references (e.g., {@code col_name} or {@code input.col_name}).
+ *
+ * <h2>Usage Pattern</h2>
+ * <pre>{@code
+ * // Create a predicate
+ * IgnitePredicate predicate = factory.predicate("age > 18 AND status = 'ACTIVE'", rowType);
+ *
+ * // Build evaluation context
+ * EvaluationContext<MyRow> context = factory.<MyRow>contextBuilder()
+ * .timeProvider(() -> System.currentTimeMillis())
+ * .rowAccessor(myRowAccessor)
+ * .build();
+ *
+ * // Evaluate predicate
+ * boolean result = predicate.test(myRow, context);
+ * }</pre>
+ *
+ * <h2>Error Handling</h2>
+ *
+ * <p>Expression creation may fail at two stages:<ul>
+ * <li><strong>Parsing:</strong> If the expression string is syntactically invalid, an
+ * {@link ExpressionParsingException} is thrown.</li>
+ * <li><strong>Validation:</strong> If the expression is syntactically correct but semantically
+ * invalid (e.g., type mismatches, undefined columns), an {@link ExpressionValidationException} is thrown.</li>
+ * </ul>
+ *
+ * @see EvaluationContext
+ * @see ContextBuilder
+ * @see ExpressionParsingException
+ * @see ExpressionValidationException
+ */
+public interface ExpressionFactory {
+ /**
+ * Returns predicate which accepts a single row as input.
+ *
+ * <p>This expression has single namespace with name "INPUT" created implicitly. To refer to any input column, a simple identifier may
+ * be used as well as fully qualified one: <pre>{@code
+ * // Given
+ * StructNativeType inputRowType = NativeTypes.structBuilder()
+ * .addField("INT_COL", NativeTypes.INT32, false)
+ * .build();
+ *
+ * // This is correct
+ * factory.predicate("int_col > 5", inputRowType);
+ *
+ * // This is also correct
+ * factory.predicate("input.int_col > 5", inputRowType)
+ * }</pre>
+ *
+ * <p>The expression provided is subject to normalization. This implies, all identifiers are normalized (i.e. converted to upper case)
+ * unless they wrapped in double quotes. That is, {@code int_col > 5} and {@code INT_COL > 5} are equal expressions.
+ *
+ * @param expression String representation of expression to create.
+ * @param inputRowType A type of the input row.
+ * @return A predicate representing the provided expression.
+ * @throws ExpressionParsingException If the expression string is syntactically invalid.
+ * @throws ExpressionValidationException If the expression is syntactically correct but semantically invalid (e.g., type
+ * mismatches, undefined columns).
+ * @see ExpressionParsingException
+ * @see ExpressionValidationException
+ */
+ IgnitePredicate predicate(
+ String expression,
+ StructNativeType inputRowType
+ ) throws ExpressionParsingException, ExpressionValidationException;
+
+ /** Returns builder to create {@link EvaluationContext context} which should be used with expressions returned by this factory. */
+ <RowT> ContextBuilder<RowT> contextBuilder();
+}
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/RowAccessor.java b/modules/sql-engine-api/src/main/java/org/apache/ignite/internal/sql/engine/api/expressions/ExpressionParsingException.java
similarity index 60%
copy from modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/RowAccessor.java
copy to modules/sql-engine-api/src/main/java/org/apache/ignite/internal/sql/engine/api/expressions/ExpressionParsingException.java
index ad3b33e62e3..63dfd6ce38d 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/RowAccessor.java
+++ b/modules/sql-engine-api/src/main/java/org/apache/ignite/internal/sql/engine/api/expressions/ExpressionParsingException.java
@@ -15,22 +15,14 @@
* limitations under the License.
*/
-package org.apache.ignite.internal.sql.engine.exec;
+package org.apache.ignite.internal.sql.engine.api.expressions;
-import org.jetbrains.annotations.Nullable;
+/** Exception thrown when a given expression is syntactically invalid. */
+public class ExpressionParsingException extends Exception {
+ private static final long serialVersionUID = 2040204247361408378L;
-/**
- * Class representing a reader for the input row.
- *
- * @param <RowT> Type of the supported row.
- */
-public interface RowAccessor<RowT> {
- /** Reads the specified field of the given row. */
- @Nullable Object get(int field, RowT row);
-
- /** Checks whether the given field is {@code null}. */
- boolean isNull(int field, RowT row);
-
- /** Return columns count contained in the incoming row. */
- int columnsCount(RowT row);
+ /** Constructs the exception. */
+ public ExpressionParsingException(String message) {
+ super(message, null, true, false);
+ }
}
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/RowHandler.java b/modules/sql-engine-api/src/main/java/org/apache/ignite/internal/sql/engine/api/expressions/ExpressionValidationException.java
similarity index 51%
copy from modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/RowHandler.java
copy to modules/sql-engine-api/src/main/java/org/apache/ignite/internal/sql/engine/api/expressions/ExpressionValidationException.java
index 040b6a38fc6..10f421148d3 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/RowHandler.java
+++ b/modules/sql-engine-api/src/main/java/org/apache/ignite/internal/sql/engine/api/expressions/ExpressionValidationException.java
@@ -15,27 +15,23 @@
* limitations under the License.
*/
-package org.apache.ignite.internal.sql.engine.exec;
-
-import java.nio.ByteBuffer;
-import org.apache.ignite.internal.schema.BinaryTuple;
+package org.apache.ignite.internal.sql.engine.api.expressions;
/**
- * Universal accessor for rows. It also has factory methods.
+ * Exception thrown when a given expression is semantically invalid (e.g., type mismatches, undefined columns).
+ *
+ * <p>Some examples are:<ul>
+ * <li>Expression is expected to return value of type T1, but given expression returns value of type T2, and no implicit cast from T1
+ * to T2 exists.</li>
+ * <li>There is reference to undeclared column or row alias.</li>
+ * <li>Operator or function argument's type mismatch.</li>
+ * </ul>
*/
-public interface RowHandler<RowT> extends RowAccessor<RowT> {
- /**
- * Assembly row representation as BinaryTuple.
- *
- * @param row Incoming data to be processed.
- * @return {@link BinaryTuple} representation.
- */
- BinaryTuple toBinaryTuple(RowT row);
+public class ExpressionValidationException extends Exception {
+ private static final long serialVersionUID = 2040204247361408378L;
- default ByteBuffer toByteBuffer(RowT row) {
- return toBinaryTuple(row).byteBuffer();
+ /** Constructs the exception. */
+ public ExpressionValidationException(String message) {
+ super(message, null, true, false);
}
-
- /** String representation. */
- String toString(RowT row);
}
diff --git a/modules/sql-engine-api/src/main/java/org/apache/ignite/internal/sql/engine/api/expressions/IgnitePredicate.java b/modules/sql-engine-api/src/main/java/org/apache/ignite/internal/sql/engine/api/expressions/IgnitePredicate.java
new file mode 100644
index 00000000000..7a8bad74242
--- /dev/null
+++ b/modules/sql-engine-api/src/main/java/org/apache/ignite/internal/sql/engine/api/expressions/IgnitePredicate.java
@@ -0,0 +1,38 @@
+/*
+ * 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.api.expressions;
+
+/** Predicate expression which accepts single row as input and evaluates to boolean value. */
+public interface IgnitePredicate {
+ /**
+ * Evaluates the predicate on the given row within the execution context.
+ *
+ * @param context The evaluation context, providing metadata for context-dependent functions, such as CURRENT_TIMESTAMP. Accepts
+ * only contexts created by the same {@link ExpressionFactory}.
+ * @param row The current row to be evaluated.
+ * @param <RowT> The type of the execution row.
+ * @return {@code true} if the row satisfies the predicate, {@code false} otherwise.
+ * @throws ExpressionEvaluationException If an error occurred during expression evaluation.
+ * @see ExpressionFactory
+ * @see ExpressionFactory#contextBuilder()
+ */
+ <RowT> boolean test(
+ EvaluationContext<RowT> context,
+ RowT row
+ ) throws ExpressionEvaluationException;
+}
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/RowAccessor.java b/modules/sql-engine-api/src/main/java/org/apache/ignite/internal/sql/engine/api/expressions/RowAccessor.java
similarity index 95%
copy from modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/RowAccessor.java
copy to modules/sql-engine-api/src/main/java/org/apache/ignite/internal/sql/engine/api/expressions/RowAccessor.java
index ad3b33e62e3..434f0683461 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/RowAccessor.java
+++ b/modules/sql-engine-api/src/main/java/org/apache/ignite/internal/sql/engine/api/expressions/RowAccessor.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-package org.apache.ignite.internal.sql.engine.exec;
+package org.apache.ignite.internal.sql.engine.api.expressions;
import org.jetbrains.annotations.Nullable;
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/RowFactory.java b/modules/sql-engine-api/src/main/java/org/apache/ignite/internal/sql/engine/api/expressions/RowFactory.java
similarity index 98%
rename from modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/RowFactory.java
rename to modules/sql-engine-api/src/main/java/org/apache/ignite/internal/sql/engine/api/expressions/RowFactory.java
index 0026bad4201..b037a49f752 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/RowFactory.java
+++ b/modules/sql-engine-api/src/main/java/org/apache/ignite/internal/sql/engine/api/expressions/RowFactory.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-package org.apache.ignite.internal.sql.engine.exec;
+package org.apache.ignite.internal.sql.engine.api.expressions;
import org.apache.ignite.internal.lang.InternalTuple;
import org.apache.ignite.internal.type.StructNativeType;
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/RowFactoryFactory.java b/modules/sql-engine-api/src/main/java/org/apache/ignite/internal/sql/engine/api/expressions/RowFactoryFactory.java
similarity index 96%
rename from modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/RowFactoryFactory.java
rename to modules/sql-engine-api/src/main/java/org/apache/ignite/internal/sql/engine/api/expressions/RowFactoryFactory.java
index bc91f251ae5..1a53dc0e3cc 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/RowFactoryFactory.java
+++ b/modules/sql-engine-api/src/main/java/org/apache/ignite/internal/sql/engine/api/expressions/RowFactoryFactory.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-package org.apache.ignite.internal.sql.engine.exec;
+package org.apache.ignite.internal.sql.engine.api.expressions;
import org.apache.ignite.internal.type.StructNativeType;
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/lang/SqlExceptionMapperUtil.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/lang/SqlExceptionMapperUtil.java
index c580a84893e..96c1bf7d2b7 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/lang/SqlExceptionMapperUtil.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/lang/SqlExceptionMapperUtil.java
@@ -20,6 +20,7 @@ package org.apache.ignite.internal.lang;
import static org.apache.ignite.internal.lang.IgniteExceptionMapperUtil.mapToPublicException;
import static org.apache.ignite.lang.ErrorGroups.Common.INTERNAL_ERR;
+import org.apache.ignite.internal.sql.engine.api.expressions.ExpressionEvaluationException;
import org.apache.ignite.internal.sql.engine.exec.mapping.MappingException;
import org.apache.ignite.internal.sql.engine.message.UnknownNodeException;
import org.apache.ignite.internal.util.ExceptionUtils;
@@ -59,6 +60,9 @@ public class SqlExceptionMapperUtil {
if (unwrapped instanceof UnknownNodeException) {
return new SqlException(Common.NODE_LEFT_ERR, "Node left the cluster. Node: " + ((UnknownNodeException) unwrapped).nodeName());
}
+ if (unwrapped instanceof ExpressionEvaluationException) {
+ return new SqlException(Sql.RUNTIME_ERR, unwrapped.getMessage());
+ }
Throwable e = mapToPublicException(origin);
if (e instanceof Error) {
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/SqlQueryProcessor.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/SqlQueryProcessor.java
index 1261e55795b..1d1c9e7c833 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/SqlQueryProcessor.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/SqlQueryProcessor.java
@@ -61,6 +61,7 @@ import org.apache.ignite.internal.sql.SqlCommon;
import org.apache.ignite.internal.sql.configuration.distributed.CreateTableDefaultsView;
import org.apache.ignite.internal.sql.configuration.distributed.SqlDistributedConfiguration;
import org.apache.ignite.internal.sql.configuration.local.SqlLocalConfiguration;
+import org.apache.ignite.internal.sql.engine.api.expressions.ExpressionFactory;
import org.apache.ignite.internal.sql.engine.api.kill.CancellableOperationType;
import org.apache.ignite.internal.sql.engine.api.kill.OperationKillHandler;
import org.apache.ignite.internal.sql.engine.exec.ExchangeServiceImpl;
@@ -74,6 +75,7 @@ import org.apache.ignite.internal.sql.engine.exec.QueryTaskExecutorImpl;
import org.apache.ignite.internal.sql.engine.exec.SqlRowHandler;
import org.apache.ignite.internal.sql.engine.exec.TransactionalOperationTracker;
import org.apache.ignite.internal.sql.engine.exec.ddl.DdlCommandHandler;
+import org.apache.ignite.internal.sql.engine.exec.exp.SqlExpressionFactory;
import org.apache.ignite.internal.sql.engine.exec.exp.SqlExpressionFactoryImpl;
import org.apache.ignite.internal.sql.engine.exec.exp.func.TableFunctionRegistryImpl;
import org.apache.ignite.internal.sql.engine.exec.fsm.ExecutionPhase;
@@ -83,6 +85,7 @@ import org.apache.ignite.internal.sql.engine.exec.fsm.QueryInfo;
import org.apache.ignite.internal.sql.engine.exec.kill.KillCommandHandler;
import org.apache.ignite.internal.sql.engine.exec.mapping.ExecutionDistributionProviderImpl;
import org.apache.ignite.internal.sql.engine.exec.mapping.MappingServiceImpl;
+import org.apache.ignite.internal.sql.engine.expressions.SqlExpressionFactoryAdapter;
import org.apache.ignite.internal.sql.engine.message.MessageServiceImpl;
import org.apache.ignite.internal.sql.engine.prepare.PrepareService;
import org.apache.ignite.internal.sql.engine.prepare.PrepareServiceImpl;
@@ -207,6 +210,8 @@ public class SqlQueryProcessor implements QueryProcessor, SystemViewProvider {
private final EventLog eventLog;
+ private final SqlExpressionFactory expressionFactory;
+
/** Constructor. */
public SqlQueryProcessor(
ClusterService clusterSrvc,
@@ -270,6 +275,10 @@ public class SqlQueryProcessor implements QueryProcessor, SystemViewProvider {
CACHE_FACTORY,
SCHEMA_CACHE_SIZE
);
+
+ expressionFactory = new SqlExpressionFactoryImpl(
+ Commons.typeFactory(), COMPILED_EXPRESSIONS_CACHE_SIZE, CACHE_FACTORY
+ );
}
/** {@inheritDoc} */
@@ -386,9 +395,7 @@ public class SqlQueryProcessor implements QueryProcessor, SystemViewProvider {
tableFunctionRegistry,
clockService,
killCommandHandler,
- new SqlExpressionFactoryImpl(
- Commons.typeFactory(), COMPILED_EXPRESSIONS_CACHE_SIZE, CACHE_FACTORY
- ),
+ expressionFactory,
EXECUTION_SERVICE_SHUTDOWN_TIMEOUT
));
@@ -637,6 +644,11 @@ public class SqlQueryProcessor implements QueryProcessor, SystemViewProvider {
return prepareSvc.invalidateCache(tableNames);
}
+ /** Returns an expression factory to create executable expressions. */
+ public ExpressionFactory expressionFactory() {
+ return new SqlExpressionFactoryAdapter(expressionFactory);
+ }
+
/** Completes the provided future when the callback is called. */
public static class PrefetchCallback implements QueryPrefetchCallback {
private final CompletableFuture<Void> prefetchFuture = new CompletableFuture<>();
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ExchangeServiceImpl.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ExchangeServiceImpl.java
index 3b0a493c9dc..f61d335789d 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ExchangeServiceImpl.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ExchangeServiceImpl.java
@@ -25,10 +25,12 @@ import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import org.apache.ignite.internal.hlc.ClockService;
import org.apache.ignite.internal.lang.IgniteInternalException;
+import org.apache.ignite.internal.lang.SqlExceptionMapperUtil;
import org.apache.ignite.internal.logger.IgniteLogger;
import org.apache.ignite.internal.logger.Loggers;
import org.apache.ignite.internal.network.InternalClusterNode;
import org.apache.ignite.internal.partition.replicator.network.replication.BinaryTupleMessage;
+import org.apache.ignite.internal.sql.engine.api.expressions.ExpressionEvaluationException;
import org.apache.ignite.internal.sql.engine.exec.rel.Inbox;
import org.apache.ignite.internal.sql.engine.exec.rel.Outbox;
import org.apache.ignite.internal.sql.engine.message.MessageService;
@@ -156,9 +158,18 @@ public class ExchangeServiceImpl implements ExchangeService {
outbox.onRequest(node.name(), msg.amountOfBatches());
}
} catch (Throwable e) {
- outbox.onError(e);
+ Throwable toUse;
+ //noinspection InstanceofCatchParameter
+ if (e instanceof ExpressionEvaluationException) {
+ toUse = SqlExceptionMapperUtil.mapToPublicSqlException(e);
+ } else {
+ toUse = e;
+ }
+
+ outbox.onError(toUse);
- throw new IgniteInternalException(INTERNAL_ERR, "Unexpected exception", e);
+ //noinspection ThrowInsideCatchBlockWhichIgnoresCaughtException
+ throw new IgniteInternalException(INTERNAL_ERR, "Unexpected exception", toUse);
}
};
@@ -177,13 +188,21 @@ public class ExchangeServiceImpl implements ExchangeService {
try {
inbox.onBatchReceived(node.name(), msg.batchId(), msg.last(), msg.rows());
} catch (Throwable e) {
- inbox.onError(e);
+ Throwable toUse;
+ //noinspection InstanceofCatchParameter
+ if (e instanceof ExpressionEvaluationException) {
+ toUse = SqlExceptionMapperUtil.mapToPublicSqlException(e);
+ } else {
+ toUse = e;
+ }
+
+ inbox.onError(toUse);
- if (e instanceof IgniteException) {
+ if (toUse instanceof IgniteException) {
return;
}
- LOG.warn("Unexpected exception", e);
+ LOG.warn("Unexpected exception", toUse);
}
} else if (LOG.isDebugEnabled()) {
LOG.debug("Stale batch message received: [nodeName={}, executionId={}, fragmentId={}, exchangeId={}, batchId={}]",
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ExecutionContext.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ExecutionContext.java
index 349685e0e48..5716c1fbaa4 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ExecutionContext.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ExecutionContext.java
@@ -41,6 +41,7 @@ import org.apache.ignite.internal.lang.RunnableX;
import org.apache.ignite.internal.logger.IgniteLogger;
import org.apache.ignite.internal.logger.Loggers;
import org.apache.ignite.internal.network.InternalClusterNode;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.exp.SqlExpressionFactory;
import org.apache.ignite.internal.sql.engine.exec.mapping.ColocationGroup;
import org.apache.ignite.internal.sql.engine.exec.mapping.FragmentDescription;
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ExecutionServiceImpl.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ExecutionServiceImpl.java
index e1222e7f103..fa858ed8c4d 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ExecutionServiceImpl.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ExecutionServiceImpl.java
@@ -85,6 +85,7 @@ import org.apache.ignite.internal.sql.engine.SchemaAwareConverter;
import org.apache.ignite.internal.sql.engine.SqlOperationContext;
import org.apache.ignite.internal.sql.engine.SqlQueryProcessor.PrefetchCallback;
import org.apache.ignite.internal.sql.engine.SqlQueryType;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.AsyncDataCursor.CancellationReason;
import org.apache.ignite.internal.sql.engine.exec.ddl.DdlCommandHandler;
import org.apache.ignite.internal.sql.engine.exec.exp.SqlExpressionFactory;
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ProjectedTableRowConverterImpl.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ProjectedTableRowConverterImpl.java
index a7c95bb9d66..7234448c56c 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ProjectedTableRowConverterImpl.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ProjectedTableRowConverterImpl.java
@@ -24,6 +24,7 @@ import org.apache.ignite.internal.schema.BinaryTuple;
import org.apache.ignite.internal.schema.InternalTupleEx;
import org.apache.ignite.internal.schema.SchemaDescriptor;
import org.apache.ignite.internal.schema.SchemaRegistry;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory;
import org.apache.ignite.internal.sql.engine.util.ExtendedProjectedTuple;
import org.apache.ignite.internal.sql.engine.util.ProjectedTuple;
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/RowHandler.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/RowHandler.java
index 040b6a38fc6..59fae8e206e 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/RowHandler.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/RowHandler.java
@@ -19,6 +19,7 @@ package org.apache.ignite.internal.sql.engine.exec;
import java.nio.ByteBuffer;
import org.apache.ignite.internal.schema.BinaryTuple;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowAccessor;
/**
* Universal accessor for rows. It also has factory methods.
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ScannableTable.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ScannableTable.java
index 98717570bdb..4f385ebf49d 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ScannableTable.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ScannableTable.java
@@ -20,6 +20,7 @@ package org.apache.ignite.internal.sql.engine.exec;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Flow.Publisher;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.exp.RangeCondition;
import org.apache.ignite.internal.tx.InternalTransaction;
import org.jetbrains.annotations.Nullable;
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ScannableTableImpl.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ScannableTableImpl.java
index f369bc62e7f..3e9605a20dd 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ScannableTableImpl.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ScannableTableImpl.java
@@ -32,6 +32,7 @@ import org.apache.ignite.internal.schema.BinaryRow;
import org.apache.ignite.internal.schema.BinaryRowEx;
import org.apache.ignite.internal.schema.BinaryTuple;
import org.apache.ignite.internal.schema.BinaryTuplePrefix;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.exp.RangeCondition;
import org.apache.ignite.internal.table.IndexScanCriteria;
import org.apache.ignite.internal.table.InternalTable;
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/SqlEvaluationContext.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/SqlEvaluationContext.java
index 858797c3575..bd7aa510c17 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/SqlEvaluationContext.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/SqlEvaluationContext.java
@@ -18,6 +18,9 @@
package org.apache.ignite.internal.sql.engine.exec;
import org.apache.calcite.DataContext;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowAccessor;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactoryFactory;
/**
* Provides the contextual environment required for evaluating SQL expressions.
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/SqlRowHandler.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/SqlRowHandler.java
index 3c907a84e87..aac8ff4588f 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/SqlRowHandler.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/SqlRowHandler.java
@@ -34,7 +34,9 @@ import org.apache.ignite.internal.binarytuple.BinaryTupleBuilder;
import org.apache.ignite.internal.lang.IgniteStringBuilder;
import org.apache.ignite.internal.lang.InternalTuple;
import org.apache.ignite.internal.schema.BinaryTuple;
-import org.apache.ignite.internal.sql.engine.exec.RowFactory.RowBuilder;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory.RowBuilder;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.SqlRowHandler.RowWrapper;
import org.apache.ignite.internal.sql.engine.util.TypeUtils;
import org.apache.ignite.internal.type.DecimalNativeType;
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/TableRowConverter.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/TableRowConverter.java
index 334a70dc3f7..12c373b141a 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/TableRowConverter.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/TableRowConverter.java
@@ -19,6 +19,7 @@ package org.apache.ignite.internal.sql.engine.exec;
import org.apache.ignite.internal.schema.BinaryRow;
import org.apache.ignite.internal.schema.BinaryRowEx;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory;
/**
* Converts rows between storage and execution engine representation.
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/TableRowConverterImpl.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/TableRowConverterImpl.java
index 5d18356ed2c..01a9676ba94 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/TableRowConverterImpl.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/TableRowConverterImpl.java
@@ -23,6 +23,7 @@ import org.apache.ignite.internal.schema.BinaryRowEx;
import org.apache.ignite.internal.schema.BinaryTuple;
import org.apache.ignite.internal.schema.SchemaDescriptor;
import org.apache.ignite.internal.schema.SchemaRegistry;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory;
/**
* Converts rows to execution engine representation.
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/UpdatableTableImpl.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/UpdatableTableImpl.java
index a8b9c053521..2cea6475592 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/UpdatableTableImpl.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/UpdatableTableImpl.java
@@ -48,6 +48,7 @@ import org.apache.ignite.internal.replicator.message.ReplicaMessagesFactory;
import org.apache.ignite.internal.replicator.message.ReplicaRequest;
import org.apache.ignite.internal.schema.BinaryRow;
import org.apache.ignite.internal.schema.BinaryRowEx;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.mapping.ColocationGroup;
import org.apache.ignite.internal.sql.engine.prepare.IgniteSqlValidatorErrorMessages;
import org.apache.ignite.internal.sql.engine.schema.ColumnDescriptor;
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/CodegenUtils.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/CodegenUtils.java
new file mode 100644
index 00000000000..aac09babf7a
--- /dev/null
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/CodegenUtils.java
@@ -0,0 +1,52 @@
+/*
+ * 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.exec.exp;
+
+import org.apache.calcite.linq4j.tree.BlockBuilder;
+import org.apache.calcite.linq4j.tree.BlockStatement;
+import org.apache.calcite.linq4j.tree.Expression;
+import org.apache.calcite.linq4j.tree.Expressions;
+import org.apache.calcite.linq4j.tree.ParameterExpression;
+import org.apache.calcite.linq4j.tree.Statement;
+import org.apache.ignite.internal.sql.engine.api.expressions.ExpressionEvaluationException;
+
+/** Utility class providing helper methods for code generation. */
+final class CodegenUtils {
+ /**
+ * Wraps a statement in a try-catch block that converts any thrown exceptions to {@link ExpressionEvaluationException}.
+ *
+ * <p>This method creates a try-catch wrapper around the provided statement. If any {@link Exception} is thrown during the execution of
+ * the statement, it will be caught and re-thrown as an {@link ExpressionEvaluationException} with the original exception's message.
+ *
+ * @param statement The statement to be wrapped in exception handling logic.
+ * @return A {@link BlockStatement} containing the wrapped statement with exception conversion logic.
+ */
+ static BlockStatement wrapWithConversionToEvaluationException(Statement statement) {
+ ParameterExpression ex = Expressions.parameter(0, Exception.class, "e");
+ Expression evaluationException = Expressions.new_(ExpressionEvaluationException.class, Expressions.call(ex, "getMessage"));
+ BlockBuilder tryCatchBlock = new BlockBuilder();
+
+ tryCatchBlock.add(Expressions.tryCatch(statement, Expressions.catch_(ex, Expressions.throw_(evaluationException))));
+
+ return tryCatchBlock.toBlock();
+ }
+
+ private CodegenUtils() {
+ throw new AssertionError("Should not be called");
+ }
+}
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/ComparatorImplementor.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/ComparatorImplementor.java
index 850960515bb..2e35f44d4db 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/ComparatorImplementor.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/ComparatorImplementor.java
@@ -24,7 +24,7 @@ import org.apache.calcite.rel.RelCollation;
import org.apache.calcite.rel.RelFieldCollation;
import org.apache.calcite.rel.RelFieldCollation.Direction;
import org.apache.calcite.util.ImmutableBitSet;
-import org.apache.ignite.internal.sql.engine.exec.RowAccessor;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowAccessor;
import org.apache.ignite.internal.sql.engine.exec.SqlEvaluationContext;
import org.jetbrains.annotations.Nullable;
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/JoinPredicateImplementor.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/JoinPredicateImplementor.java
index 5e329ba24ca..b923210f6f2 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/JoinPredicateImplementor.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/JoinPredicateImplementor.java
@@ -17,6 +17,7 @@
package org.apache.ignite.internal.sql.engine.exec.exp;
+import static org.apache.ignite.internal.sql.engine.exec.exp.CodegenUtils.wrapWithConversionToEvaluationException;
import static org.apache.ignite.internal.sql.engine.exec.exp.SqlExpressionFactoryImpl.digest;
import static org.apache.ignite.internal.sql.engine.util.Commons.cast;
@@ -26,6 +27,7 @@ import org.apache.calcite.DataContext;
import org.apache.calcite.adapter.java.JavaTypeFactory;
import org.apache.calcite.linq4j.function.Function1;
import org.apache.calcite.linq4j.tree.BlockBuilder;
+import org.apache.calcite.linq4j.tree.BlockStatement;
import org.apache.calcite.linq4j.tree.Expression;
import org.apache.calcite.linq4j.tree.Expressions;
import org.apache.calcite.linq4j.tree.MethodDeclaration;
@@ -41,8 +43,6 @@ import org.apache.ignite.internal.sql.engine.exec.exp.RexToLixTranslator.InputGe
import org.apache.ignite.internal.sql.engine.util.Commons;
import org.apache.ignite.internal.sql.engine.util.IgniteMethod;
import org.apache.ignite.internal.sql.engine.util.cache.Cache;
-import org.apache.ignite.lang.ErrorGroups.Sql;
-import org.apache.ignite.sql.SqlException;
/** Implementor which implements {@link SqlJoinPredicate}. */
class JoinPredicateImplementor {
@@ -108,17 +108,13 @@ class JoinPredicateImplementor {
builder.add(condition);
- ParameterExpression ex = Expressions.parameter(0, Exception.class, "e");
- Expression sqlException = Expressions.new_(SqlException.class, Expressions.constant(Sql.RUNTIME_ERR), ex);
- BlockBuilder tryCatchBlock = new BlockBuilder();
-
- tryCatchBlock.add(Expressions.tryCatch(builder.toBlock(), Expressions.catch_(ex, Expressions.throw_(sqlException))));
+ BlockStatement methodBody = wrapWithConversionToEvaluationException(builder.toBlock());
List<ParameterExpression> params = List.of(ctx, left, right);
MethodDeclaration declaration = Expressions.methodDecl(
- Modifier.PUBLIC, boolean.class, "test",
- params, tryCatchBlock.toBlock());
+ Modifier.PUBLIC, boolean.class, "test", params, methodBody
+ );
Class<SqlJoinPredicate> clazz = cast(SqlJoinPredicate.class);
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/JoinProjectionImplementor.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/JoinProjectionImplementor.java
index 53f4d7b056e..1060c59b9b5 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/JoinProjectionImplementor.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/JoinProjectionImplementor.java
@@ -17,6 +17,7 @@
package org.apache.ignite.internal.sql.engine.exec.exp;
+import static org.apache.ignite.internal.sql.engine.exec.exp.CodegenUtils.wrapWithConversionToEvaluationException;
import static org.apache.ignite.internal.sql.engine.exec.exp.SqlExpressionFactoryImpl.digest;
import static org.apache.ignite.internal.sql.engine.util.Commons.cast;
import static org.apache.ignite.internal.sql.engine.util.TypeUtils.structuredTypeFromRelTypeList;
@@ -27,6 +28,7 @@ import org.apache.calcite.DataContext;
import org.apache.calcite.adapter.java.JavaTypeFactory;
import org.apache.calcite.linq4j.function.Function1;
import org.apache.calcite.linq4j.tree.BlockBuilder;
+import org.apache.calcite.linq4j.tree.BlockStatement;
import org.apache.calcite.linq4j.tree.Expression;
import org.apache.calcite.linq4j.tree.Expressions;
import org.apache.calcite.linq4j.tree.MethodDeclaration;
@@ -38,15 +40,13 @@ import org.apache.calcite.rex.RexProgram;
import org.apache.calcite.rex.RexProgramBuilder;
import org.apache.calcite.rex.RexUtil;
import org.apache.calcite.sql.validate.SqlConformance;
-import org.apache.ignite.internal.sql.engine.exec.RowFactory.RowBuilder;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory.RowBuilder;
import org.apache.ignite.internal.sql.engine.exec.SqlEvaluationContext;
import org.apache.ignite.internal.sql.engine.exec.exp.RexToLixTranslator.InputGetter;
import org.apache.ignite.internal.sql.engine.util.Commons;
import org.apache.ignite.internal.sql.engine.util.IgniteMethod;
import org.apache.ignite.internal.sql.engine.util.cache.Cache;
import org.apache.ignite.internal.type.StructNativeType;
-import org.apache.ignite.lang.ErrorGroups.Sql;
-import org.apache.ignite.sql.SqlException;
/** Implementor which implements {@link SqlJoinProjection}. */
class JoinProjectionImplementor {
@@ -123,17 +123,13 @@ class JoinProjectionImplementor {
builder.add(Expressions.statement(addRowField));
}
- ParameterExpression ex = Expressions.parameter(0, Exception.class, "e");
- Expression sqlException = Expressions.new_(SqlException.class, Expressions.constant(Sql.RUNTIME_ERR), ex);
- BlockBuilder tryCatchBlock = new BlockBuilder();
-
- tryCatchBlock.add(Expressions.tryCatch(builder.toBlock(), Expressions.catch_(ex, Expressions.throw_(sqlException))));
+ BlockStatement methodBody = wrapWithConversionToEvaluationException(builder.toBlock());
List<ParameterExpression> params = List.of(ctx, left, right, outBuilder);
MethodDeclaration declaration = Expressions.methodDecl(
- Modifier.PUBLIC, void.class, "project",
- params, tryCatchBlock.toBlock());
+ Modifier.PUBLIC, void.class, "project", params, methodBody
+ );
Class<SqlJoinProjectionExt> clazz = cast(SqlJoinProjectionExt.class);
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/PredicateImplementor.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/PredicateImplementor.java
index ab69935a4be..463c4700009 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/PredicateImplementor.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/PredicateImplementor.java
@@ -17,6 +17,7 @@
package org.apache.ignite.internal.sql.engine.exec.exp;
+import static org.apache.ignite.internal.sql.engine.exec.exp.CodegenUtils.wrapWithConversionToEvaluationException;
import static org.apache.ignite.internal.sql.engine.exec.exp.SqlExpressionFactoryImpl.digest;
import static org.apache.ignite.internal.sql.engine.util.Commons.cast;
@@ -26,6 +27,7 @@ import org.apache.calcite.DataContext;
import org.apache.calcite.adapter.java.JavaTypeFactory;
import org.apache.calcite.linq4j.function.Function1;
import org.apache.calcite.linq4j.tree.BlockBuilder;
+import org.apache.calcite.linq4j.tree.BlockStatement;
import org.apache.calcite.linq4j.tree.Expression;
import org.apache.calcite.linq4j.tree.Expressions;
import org.apache.calcite.linq4j.tree.MethodDeclaration;
@@ -41,8 +43,6 @@ import org.apache.ignite.internal.sql.engine.exec.exp.RexToLixTranslator.InputGe
import org.apache.ignite.internal.sql.engine.util.Commons;
import org.apache.ignite.internal.sql.engine.util.IgniteMethod;
import org.apache.ignite.internal.sql.engine.util.cache.Cache;
-import org.apache.ignite.lang.ErrorGroups.Sql;
-import org.apache.ignite.sql.SqlException;
/** Implementor which implements {@link SqlPredicate}. */
class PredicateImplementor {
@@ -106,17 +106,13 @@ class PredicateImplementor {
builder.add(condition);
- ParameterExpression ex = Expressions.parameter(0, Exception.class, "e");
- Expression sqlException = Expressions.new_(SqlException.class, Expressions.constant(Sql.RUNTIME_ERR), ex);
- BlockBuilder tryCatchBlock = new BlockBuilder();
-
- tryCatchBlock.add(Expressions.tryCatch(builder.toBlock(), Expressions.catch_(ex, Expressions.throw_(sqlException))));
+ BlockStatement methodBody = wrapWithConversionToEvaluationException(builder.toBlock());
List<ParameterExpression> params = List.of(ctx, row);
MethodDeclaration declaration = Expressions.methodDecl(
- Modifier.PUBLIC, boolean.class, "test",
- params, tryCatchBlock.toBlock());
+ Modifier.PUBLIC, boolean.class, "test", params, methodBody
+ );
Class<SqlPredicate> clazz = cast(SqlPredicate.class);
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/ProjectionImplementor.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/ProjectionImplementor.java
index 14abf44590a..029e601f784 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/ProjectionImplementor.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/ProjectionImplementor.java
@@ -17,6 +17,7 @@
package org.apache.ignite.internal.sql.engine.exec.exp;
+import static org.apache.ignite.internal.sql.engine.exec.exp.CodegenUtils.wrapWithConversionToEvaluationException;
import static org.apache.ignite.internal.sql.engine.exec.exp.SqlExpressionFactoryImpl.digest;
import static org.apache.ignite.internal.sql.engine.util.Commons.cast;
import static org.apache.ignite.internal.sql.engine.util.TypeUtils.structuredTypeFromRelTypeList;
@@ -27,6 +28,7 @@ import org.apache.calcite.DataContext;
import org.apache.calcite.adapter.java.JavaTypeFactory;
import org.apache.calcite.linq4j.function.Function1;
import org.apache.calcite.linq4j.tree.BlockBuilder;
+import org.apache.calcite.linq4j.tree.BlockStatement;
import org.apache.calcite.linq4j.tree.Expression;
import org.apache.calcite.linq4j.tree.Expressions;
import org.apache.calcite.linq4j.tree.MethodDeclaration;
@@ -38,15 +40,13 @@ import org.apache.calcite.rex.RexProgram;
import org.apache.calcite.rex.RexProgramBuilder;
import org.apache.calcite.rex.RexUtil;
import org.apache.calcite.sql.validate.SqlConformance;
-import org.apache.ignite.internal.sql.engine.exec.RowFactory.RowBuilder;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory.RowBuilder;
import org.apache.ignite.internal.sql.engine.exec.SqlEvaluationContext;
import org.apache.ignite.internal.sql.engine.exec.exp.RexToLixTranslator.InputGetter;
import org.apache.ignite.internal.sql.engine.util.Commons;
import org.apache.ignite.internal.sql.engine.util.IgniteMethod;
import org.apache.ignite.internal.sql.engine.util.cache.Cache;
import org.apache.ignite.internal.type.StructNativeType;
-import org.apache.ignite.lang.ErrorGroups.Sql;
-import org.apache.ignite.sql.SqlException;
/** Implementor which implements {@link SqlProjection}. */
class ProjectionImplementor {
@@ -121,17 +121,13 @@ class ProjectionImplementor {
builder.add(Expressions.statement(addRowField));
}
- ParameterExpression ex = Expressions.parameter(0, Exception.class, "e");
- Expression sqlException = Expressions.new_(SqlException.class, Expressions.constant(Sql.RUNTIME_ERR), ex);
- BlockBuilder tryCatchBlock = new BlockBuilder();
-
- tryCatchBlock.add(Expressions.tryCatch(builder.toBlock(), Expressions.catch_(ex, Expressions.throw_(sqlException))));
+ BlockStatement methodBody = wrapWithConversionToEvaluationException(builder.toBlock());
List<ParameterExpression> params = List.of(ctx, row, outBuilder);
MethodDeclaration declaration = Expressions.methodDecl(
- Modifier.PUBLIC, void.class, "project",
- params, tryCatchBlock.toBlock());
+ Modifier.PUBLIC, void.class, "project", params, methodBody
+ );
Class<SqlProjectionExt> clazz = cast(SqlProjectionExt.class);
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/RowProviderImplementor.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/RowProviderImplementor.java
index f550976c908..47c7f723039 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/RowProviderImplementor.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/RowProviderImplementor.java
@@ -17,6 +17,7 @@
package org.apache.ignite.internal.sql.engine.exec.exp;
+import static org.apache.ignite.internal.sql.engine.exec.exp.CodegenUtils.wrapWithConversionToEvaluationException;
import static org.apache.ignite.internal.sql.engine.exec.exp.SqlExpressionFactoryImpl.digest;
import static org.apache.ignite.internal.sql.engine.util.Commons.cast;
@@ -27,6 +28,7 @@ import org.apache.calcite.DataContext;
import org.apache.calcite.adapter.java.JavaTypeFactory;
import org.apache.calcite.linq4j.function.Function1;
import org.apache.calcite.linq4j.tree.BlockBuilder;
+import org.apache.calcite.linq4j.tree.BlockStatement;
import org.apache.calcite.linq4j.tree.Expression;
import org.apache.calcite.linq4j.tree.Expressions;
import org.apache.calcite.linq4j.tree.MethodDeclaration;
@@ -40,7 +42,7 @@ import org.apache.calcite.rex.RexProgram;
import org.apache.calcite.rex.RexProgramBuilder;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.sql.validate.SqlConformance;
-import org.apache.ignite.internal.sql.engine.exec.RowFactory.RowBuilder;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory.RowBuilder;
import org.apache.ignite.internal.sql.engine.exec.SqlEvaluationContext;
import org.apache.ignite.internal.sql.engine.exec.exp.RexToLixTranslator.InputGetter;
import org.apache.ignite.internal.sql.engine.util.Commons;
@@ -50,8 +52,6 @@ import org.apache.ignite.internal.sql.engine.util.RexUtils;
import org.apache.ignite.internal.sql.engine.util.TypeUtils;
import org.apache.ignite.internal.sql.engine.util.cache.Cache;
import org.apache.ignite.internal.type.StructNativeType;
-import org.apache.ignite.lang.ErrorGroups.Sql;
-import org.apache.ignite.sql.SqlException;
/** Implementor which implements {@link SqlRowProvider}. */
class RowProviderImplementor {
@@ -145,17 +145,13 @@ class RowProviderImplementor {
builder.add(Expressions.statement(addRowField));
}
- ParameterExpression ex = Expressions.parameter(0, Exception.class, "e");
- Expression sqlException = Expressions.new_(SqlException.class, Expressions.constant(Sql.RUNTIME_ERR), ex);
- BlockBuilder tryCatchBlock = new BlockBuilder();
-
- tryCatchBlock.add(Expressions.tryCatch(builder.toBlock(), Expressions.catch_(ex, Expressions.throw_(sqlException))));
+ BlockStatement methodBody = wrapWithConversionToEvaluationException(builder.toBlock());
List<ParameterExpression> params = List.of(ctx, outBuilder);
MethodDeclaration declaration = Expressions.methodDecl(
- Modifier.PUBLIC, void.class, "get",
- params, tryCatchBlock.toBlock());
+ Modifier.PUBLIC, void.class, "get", params, methodBody
+ );
Class<SqlRowProviderExt> clazz = cast(SqlRowProviderExt.class);
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/ScalarImplementor.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/ScalarImplementor.java
index 36b61a627e2..8ef8cd33c8b 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/ScalarImplementor.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/ScalarImplementor.java
@@ -17,6 +17,7 @@
package org.apache.ignite.internal.sql.engine.exec.exp;
+import static org.apache.ignite.internal.sql.engine.exec.exp.CodegenUtils.wrapWithConversionToEvaluationException;
import static org.apache.ignite.internal.sql.engine.exec.exp.SqlExpressionFactoryImpl.digest;
import static org.apache.ignite.internal.sql.engine.util.Commons.cast;
@@ -25,6 +26,7 @@ import java.util.List;
import org.apache.calcite.adapter.java.JavaTypeFactory;
import org.apache.calcite.linq4j.function.Function1;
import org.apache.calcite.linq4j.tree.BlockBuilder;
+import org.apache.calcite.linq4j.tree.BlockStatement;
import org.apache.calcite.linq4j.tree.Expression;
import org.apache.calcite.linq4j.tree.Expressions;
import org.apache.calcite.linq4j.tree.MethodDeclaration;
@@ -45,8 +47,6 @@ import org.apache.ignite.internal.sql.engine.util.IgniteMethod;
import org.apache.ignite.internal.sql.engine.util.Primitives;
import org.apache.ignite.internal.sql.engine.util.RexUtils;
import org.apache.ignite.internal.sql.engine.util.cache.Cache;
-import org.apache.ignite.lang.ErrorGroups.Sql;
-import org.apache.ignite.sql.SqlException;
/** Implementor which implements {@link SqlScalar}. */
class ScalarImplementor {
@@ -122,16 +122,12 @@ class ScalarImplementor {
builder.add(projects.get(0));
- ParameterExpression ex = Expressions.parameter(0, Exception.class, "e");
- Expression sqlException = Expressions.new_(SqlException.class, Expressions.constant(Sql.RUNTIME_ERR), ex);
- BlockBuilder tryCatchBlock = new BlockBuilder();
-
- tryCatchBlock.add(Expressions.tryCatch(builder.toBlock(), Expressions.catch_(ex, Expressions.throw_(sqlException))));
+ BlockStatement methodBody = wrapWithConversionToEvaluationException(builder.toBlock());
List<ParameterExpression> params = List.of(ctx);
MethodDeclaration declaration = Expressions.methodDecl(
- Modifier.PUBLIC, Object.class, "get", params, tryCatchBlock.toBlock()
+ Modifier.PUBLIC, Object.class, "get", params, methodBody
);
Class<SqlScalar<T>> clazz = cast(SqlScalar.class);
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/agg/AccumulatorsFactory.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/agg/AccumulatorsFactory.java
index 55d21d14165..954bd7a7690 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/agg/AccumulatorsFactory.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/agg/AccumulatorsFactory.java
@@ -48,7 +48,7 @@ import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.sql.type.SqlTypeUtil;
import org.apache.calcite.sql.validate.SqlConformanceEnum;
import org.apache.calcite.util.Pair;
-import org.apache.ignite.internal.sql.engine.exec.RowAccessor;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowAccessor;
import org.apache.ignite.internal.sql.engine.exec.SqlEvaluationContext;
import org.apache.ignite.internal.sql.engine.exec.exp.RexToLixTranslator;
import org.apache.ignite.internal.sql.engine.type.IgniteTypeFactory;
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/func/SystemRangeTableFunction.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/func/SystemRangeTableFunction.java
index e598acbd4bb..06f265d96f3 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/func/SystemRangeTableFunction.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/func/SystemRangeTableFunction.java
@@ -20,8 +20,8 @@ package org.apache.ignite.internal.sql.engine.exec.exp.func;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.function.Supplier;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
-import org.apache.ignite.internal.sql.engine.exec.RowFactory;
import org.apache.ignite.internal.sql.engine.sql.fun.IgniteSqlOperatorTable;
import org.apache.ignite.internal.type.NativeTypes;
import org.apache.ignite.internal.type.StructNativeType;
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/AbstractNode.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/AbstractNode.java
index 240d32c9aab..e810e1c20dd 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/AbstractNode.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/AbstractNode.java
@@ -24,9 +24,12 @@ import java.util.List;
import org.apache.ignite.internal.lang.Debuggable;
import org.apache.ignite.internal.lang.IgniteStringBuilder;
import org.apache.ignite.internal.lang.RunnableX;
+import org.apache.ignite.internal.sql.engine.api.expressions.ExpressionEvaluationException;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
import org.apache.ignite.internal.sql.engine.util.Commons;
import org.apache.ignite.internal.util.IgniteUtils;
+import org.apache.ignite.lang.ErrorGroups.Sql;
+import org.apache.ignite.sql.SqlException;
import org.jetbrains.annotations.TestOnly;
/**
@@ -131,7 +134,11 @@ public abstract class AbstractNode<RowT> implements Node<RowT> {
checkState();
- task.run();
+ try {
+ task.run();
+ } catch (ExpressionEvaluationException evaluationException) {
+ throw new SqlException(Sql.RUNTIME_ERR, evaluationException);
+ }
}, this::onError);
}
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/AbstractSetOpNode.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/AbstractSetOpNode.java
index 3e02012fcc2..c436f4fd999 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/AbstractSetOpNode.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/AbstractSetOpNode.java
@@ -27,8 +27,8 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.ignite.internal.lang.IgniteStringBuilder;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
-import org.apache.ignite.internal.sql.engine.exec.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.RowHandler;
import org.apache.ignite.internal.sql.engine.exec.exp.agg.AggregateType;
import org.apache.ignite.internal.sql.engine.exec.exp.agg.GroupKey;
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/CorrelatedNestedLoopJoinNode.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/CorrelatedNestedLoopJoinNode.java
index 970690d6193..ecc5bbabe81 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/CorrelatedNestedLoopJoinNode.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/CorrelatedNestedLoopJoinNode.java
@@ -29,8 +29,8 @@ import java.util.function.BiPredicate;
import org.apache.calcite.rel.core.CorrelationId;
import org.apache.calcite.rel.core.JoinRelType;
import org.apache.ignite.internal.lang.IgniteStringBuilder;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
-import org.apache.ignite.internal.sql.engine.exec.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.exp.SqlJoinProjection;
/**
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/DataSourceScanNode.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/DataSourceScanNode.java
index da86d403363..bb5f8a8c0d0 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/DataSourceScanNode.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/DataSourceScanNode.java
@@ -23,8 +23,8 @@ import java.util.function.Predicate;
import org.apache.calcite.util.ImmutableIntList;
import org.apache.ignite.internal.lang.InternalTuple;
import org.apache.ignite.internal.schema.BinaryTupleSchema;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
-import org.apache.ignite.internal.sql.engine.exec.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.ScannableDataSource;
import org.apache.ignite.internal.sql.engine.util.FieldDeserializingProjectedTuple;
import org.apache.ignite.internal.util.subscription.TransformingPublisher;
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/HashAggregateNode.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/HashAggregateNode.java
index 01452b61719..0373336600b 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/HashAggregateNode.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/HashAggregateNode.java
@@ -32,8 +32,8 @@ import java.util.Map;
import java.util.Set;
import org.apache.calcite.util.ImmutableBitSet;
import org.apache.ignite.internal.lang.IgniteStringBuilder;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
-import org.apache.ignite.internal.sql.engine.exec.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.RowHandler;
import org.apache.ignite.internal.sql.engine.exec.exp.agg.AccumulatorWrapper;
import org.apache.ignite.internal.sql.engine.exec.exp.agg.AccumulatorsState;
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/HashJoinNode.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/HashJoinNode.java
index 34bbfcdf770..394a63c832a 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/HashJoinNode.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/HashJoinNode.java
@@ -32,8 +32,8 @@ import java.util.function.BiPredicate;
import org.apache.calcite.rel.core.JoinInfo;
import org.apache.calcite.rel.core.JoinRelType;
import org.apache.calcite.rel.type.RelDataType;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
-import org.apache.ignite.internal.sql.engine.exec.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.RowHandler;
import org.apache.ignite.internal.sql.engine.exec.exp.SqlJoinProjection;
import org.apache.ignite.internal.type.StructNativeType;
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/Inbox.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/Inbox.java
index 6801ad6b7a9..5c2534de4f1 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/Inbox.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/Inbox.java
@@ -36,10 +36,10 @@ import org.apache.ignite.internal.lang.IgniteStringBuilder;
import org.apache.ignite.internal.network.InternalClusterNode;
import org.apache.ignite.internal.partition.replicator.network.replication.BinaryTupleMessage;
import org.apache.ignite.internal.sql.engine.NodeLeftException;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.ExchangeService;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
import org.apache.ignite.internal.sql.engine.exec.MailboxRegistry;
-import org.apache.ignite.internal.sql.engine.exec.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.SharedState;
import org.apache.ignite.internal.sql.engine.exec.rel.Inbox.RemoteSource.State;
import org.apache.ignite.internal.util.ExceptionUtils;
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/IndexScanNode.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/IndexScanNode.java
index de7ad6c43f3..38d4097b5e3 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/IndexScanNode.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/IndexScanNode.java
@@ -27,10 +27,10 @@ import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.apache.calcite.rel.RelFieldCollation;
import org.apache.calcite.util.ImmutableIntList;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
import org.apache.ignite.internal.sql.engine.exec.PartitionProvider;
import org.apache.ignite.internal.sql.engine.exec.PartitionWithConsistencyToken;
-import org.apache.ignite.internal.sql.engine.exec.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.ScannableTable;
import org.apache.ignite.internal.sql.engine.exec.exp.RangeCondition;
import org.apache.ignite.internal.sql.engine.exec.exp.RangeIterable;
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/IntersectNode.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/IntersectNode.java
index f33c3a6c2c4..abdf05eb7c6 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/IntersectNode.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/IntersectNode.java
@@ -17,8 +17,8 @@
package org.apache.ignite.internal.sql.engine.exec.rel;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
-import org.apache.ignite.internal.sql.engine.exec.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.exp.agg.AggregateType;
import org.apache.ignite.internal.sql.engine.exec.exp.agg.GroupKey;
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/MergeJoinNode.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/MergeJoinNode.java
index c93c2bbae6c..3e0c40c9b00 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/MergeJoinNode.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/MergeJoinNode.java
@@ -28,8 +28,8 @@ import java.util.List;
import org.apache.calcite.rel.core.JoinRelType;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.ignite.internal.lang.IgniteStringBuilder;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
-import org.apache.ignite.internal.sql.engine.exec.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.exp.SqlJoinProjection;
import org.apache.ignite.internal.type.StructNativeType;
import org.jetbrains.annotations.Nullable;
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/MinusNode.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/MinusNode.java
index 188ca4b5f35..e4c4f8bd767 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/MinusNode.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/MinusNode.java
@@ -17,8 +17,8 @@
package org.apache.ignite.internal.sql.engine.exec.rel;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
-import org.apache.ignite.internal.sql.engine.exec.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.exp.agg.AggregateType;
import org.apache.ignite.internal.sql.engine.exec.exp.agg.GroupKey;
import org.apache.ignite.internal.sql.engine.rel.set.IgniteMapMinus;
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/ModifyNode.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/ModifyNode.java
index e62abdfa7ae..1a0412ffea0 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/ModifyNode.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/ModifyNode.java
@@ -29,8 +29,8 @@ import org.apache.calcite.rel.core.TableModify;
import org.apache.calcite.rel.core.TableModify.Operation;
import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.ignite.internal.lang.IgniteStringBuilder;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
-import org.apache.ignite.internal.sql.engine.exec.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.RowHandler;
import org.apache.ignite.internal.sql.engine.exec.UpdatableTable;
import org.apache.ignite.internal.sql.engine.exec.mapping.ColocationGroup;
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/NestedLoopJoinNode.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/NestedLoopJoinNode.java
index a9cb93fe6ba..b2f49ada589 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/NestedLoopJoinNode.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/NestedLoopJoinNode.java
@@ -26,8 +26,8 @@ import java.util.PrimitiveIterator;
import java.util.function.BiPredicate;
import org.apache.calcite.rel.core.JoinRelType;
import org.apache.calcite.rel.type.RelDataType;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
-import org.apache.ignite.internal.sql.engine.exec.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.exp.SqlJoinProjection;
import org.apache.ignite.internal.type.StructNativeType;
import org.jetbrains.annotations.Nullable;
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/SortAggregateNode.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/SortAggregateNode.java
index 99e4f24b9a1..6ea047b2d08 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/SortAggregateNode.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/SortAggregateNode.java
@@ -32,8 +32,8 @@ import java.util.Objects;
import java.util.Set;
import org.apache.calcite.util.ImmutableBitSet;
import org.apache.ignite.internal.lang.IgniteStringBuilder;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
-import org.apache.ignite.internal.sql.engine.exec.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.RowHandler;
import org.apache.ignite.internal.sql.engine.exec.exp.agg.AccumulatorWrapper;
import org.apache.ignite.internal.sql.engine.exec.exp.agg.AccumulatorsState;
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/TableScanNode.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/TableScanNode.java
index ad562fdb20e..25e6500ef79 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/TableScanNode.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/rel/TableScanNode.java
@@ -23,10 +23,10 @@ import java.util.concurrent.Flow.Publisher;
import java.util.function.Function;
import java.util.function.Predicate;
import org.apache.calcite.util.ImmutableIntList;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
import org.apache.ignite.internal.sql.engine.exec.PartitionProvider;
import org.apache.ignite.internal.sql.engine.exec.PartitionWithConsistencyToken;
-import org.apache.ignite.internal.sql.engine.exec.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.ScannableTable;
import org.apache.ignite.internal.util.SubscriptionUtils;
import org.apache.ignite.internal.util.TransformingIterator;
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/RowAccessor.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/expressions/RowBasedScope.java
similarity index 60%
rename from modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/RowAccessor.java
rename to modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/expressions/RowBasedScope.java
index ad3b33e62e3..2d4d45b58e8 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/RowAccessor.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/expressions/RowBasedScope.java
@@ -15,22 +15,19 @@
* limitations under the License.
*/
-package org.apache.ignite.internal.sql.engine.exec;
+package org.apache.ignite.internal.sql.engine.expressions;
-import org.jetbrains.annotations.Nullable;
+import org.apache.calcite.sql.SqlNode;
+import org.apache.calcite.sql.validate.ListScope;
+import org.apache.calcite.sql.validate.SqlValidatorScope;
-/**
- * Class representing a reader for the input row.
- *
- * @param <RowT> Type of the supported row.
- */
-public interface RowAccessor<RowT> {
- /** Reads the specified field of the given row. */
- @Nullable Object get(int field, RowT row);
-
- /** Checks whether the given field is {@code null}. */
- boolean isNull(int field, RowT row);
+class RowBasedScope extends ListScope {
+ RowBasedScope(SqlValidatorScope parent) {
+ super(parent);
+ }
- /** Return columns count contained in the incoming row. */
- int columnsCount(RowT row);
+ @Override
+ public SqlNode getNode() {
+ throw new AssertionError("Should not be called");
+ }
}
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/expressions/RowFieldNamespace.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/expressions/RowFieldNamespace.java
new file mode 100644
index 00000000000..c11d9225e19
--- /dev/null
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/expressions/RowFieldNamespace.java
@@ -0,0 +1,136 @@
+/*
+ * 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.expressions;
+
+import java.util.List;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeField;
+import org.apache.calcite.sql.SqlNode;
+import org.apache.calcite.sql.validate.SqlModality;
+import org.apache.calcite.sql.validate.SqlMonotonicity;
+import org.apache.calcite.sql.validate.SqlValidator;
+import org.apache.calcite.sql.validate.SqlValidatorNamespace;
+import org.apache.calcite.sql.validate.SqlValidatorTable;
+import org.apache.calcite.util.Pair;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+class RowFieldNamespace implements SqlValidatorNamespace {
+ private final SqlValidator validator;
+ private final RelDataType type;
+
+ RowFieldNamespace(SqlValidator validator, RelDataType type) {
+ this.validator = validator;
+ this.type = type;
+ }
+
+ @Override
+ public SqlValidator getValidator() {
+ return validator;
+ }
+
+ @Override
+ public @Nullable SqlValidatorTable getTable() {
+ return null;
+ }
+
+ @Override
+ public RelDataType getRowType() {
+ return type;
+ }
+
+ @Override
+ public RelDataType getType() {
+ return type;
+ }
+
+ @Override
+ public void setType(RelDataType type) {
+ // no-op
+ }
+
+ @Override
+ public RelDataType getRowTypeSansSystemColumns() {
+ return type;
+ }
+
+ @Override
+ public void validate(RelDataType targetRowType) {
+ // valid
+ }
+
+ @Override
+ public @Nullable SqlNode getNode() {
+ return null;
+ }
+
+ @Override
+ public @Nullable SqlNode getEnclosingNode() {
+ return null;
+ }
+
+ @Override
+ public @Nullable SqlValidatorNamespace lookupChild(String name) {
+ return null;
+ }
+
+ @Override
+ public @Nullable RelDataTypeField field(String name) {
+ RelDataType rowType = getRowType();
+
+ return validator.getCatalogReader().nameMatcher().field(rowType, name);
+ }
+
+ @Override
+ public List<Pair<SqlNode, SqlMonotonicity>> getMonotonicExprs() {
+ return List.of();
+ }
+
+ @Override
+ public SqlMonotonicity getMonotonicity(String columnName) {
+ return SqlMonotonicity.NOT_MONOTONIC;
+ }
+
+ @Override
+ public void makeNullable() {
+
+ }
+
+ @Override
+ public <T> @Nullable T unwrap(Class<T> clazz) {
+ if (clazz.isInstance(this)) {
+ return clazz.cast(this);
+ }
+
+ return null;
+ }
+
+ @Override
+ public boolean isWrapperFor(Class<?> clazz) {
+ return clazz.isInstance(this);
+ }
+
+ @Override
+ public SqlValidatorNamespace resolve() {
+ return this;
+ }
+
+ @Override
+ public boolean supportsModality(SqlModality modality) {
+ return true;
+ }
+}
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/expressions/RowNamespace.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/expressions/RowNamespace.java
new file mode 100644
index 00000000000..61cceb59aeb
--- /dev/null
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/expressions/RowNamespace.java
@@ -0,0 +1,143 @@
+/*
+ * 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.expressions;
+
+import java.util.List;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeField;
+import org.apache.calcite.sql.SqlNode;
+import org.apache.calcite.sql.validate.SqlModality;
+import org.apache.calcite.sql.validate.SqlMonotonicity;
+import org.apache.calcite.sql.validate.SqlNameMatcher;
+import org.apache.calcite.sql.validate.SqlValidator;
+import org.apache.calcite.sql.validate.SqlValidatorNamespace;
+import org.apache.calcite.sql.validate.SqlValidatorTable;
+import org.apache.calcite.util.Pair;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+class RowNamespace implements SqlValidatorNamespace {
+ private final SqlValidator validator;
+ private final RelDataType type;
+
+ RowNamespace(SqlValidator validator, RelDataType type) {
+ this.validator = validator;
+ this.type = type;
+ }
+
+ @Override
+ public SqlValidator getValidator() {
+ return validator;
+ }
+
+ @Override
+ public @Nullable SqlValidatorTable getTable() {
+ return null;
+ }
+
+ @Override
+ public RelDataType getRowType() {
+ return type;
+ }
+
+ @Override
+ public RelDataType getType() {
+ return type;
+ }
+
+ @Override
+ public void setType(RelDataType type) {
+ // no-op
+ }
+
+ @Override
+ public RelDataType getRowTypeSansSystemColumns() {
+ return type;
+ }
+
+ @Override
+ public void validate(RelDataType targetRowType) {
+ // valid
+ }
+
+ @Override
+ public @Nullable SqlNode getNode() {
+ return null;
+ }
+
+ @Override
+ public @Nullable SqlNode getEnclosingNode() {
+ return null;
+ }
+
+ @Override
+ public @Nullable SqlValidatorNamespace lookupChild(String name) {
+ SqlNameMatcher nameMatcher = validator.getCatalogReader().nameMatcher();
+ RelDataTypeField field = nameMatcher.field(type, name);
+ if (field == null) {
+ return null;
+ }
+
+ return new RowFieldNamespace(validator, field.getType());
+ }
+
+ @Override
+ public @Nullable RelDataTypeField field(String name) {
+ RelDataType rowType = getRowType();
+
+ return validator.getCatalogReader().nameMatcher().field(rowType, name);
+ }
+
+ @Override
+ public List<Pair<SqlNode, SqlMonotonicity>> getMonotonicExprs() {
+ return List.of();
+ }
+
+ @Override
+ public SqlMonotonicity getMonotonicity(String columnName) {
+ return SqlMonotonicity.NOT_MONOTONIC;
+ }
+
+ @Override
+ public void makeNullable() {
+
+ }
+
+ @Override
+ public <T> @Nullable T unwrap(Class<T> clazz) {
+ if (clazz.isInstance(this)) {
+ return clazz.cast(this);
+ }
+
+ return null;
+ }
+
+ @Override
+ public boolean isWrapperFor(Class<?> clazz) {
+ return clazz.isInstance(this);
+ }
+
+ @Override
+ public SqlValidatorNamespace resolve() {
+ return this;
+ }
+
+ @Override
+ public boolean supportsModality(SqlModality modality) {
+ return true;
+ }
+}
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/expressions/SqlExpressionFactoryAdapter.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/expressions/SqlExpressionFactoryAdapter.java
new file mode 100644
index 00000000000..193dda1d275
--- /dev/null
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/expressions/SqlExpressionFactoryAdapter.java
@@ -0,0 +1,318 @@
+/*
+ * 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.expressions;
+
+import static java.util.Objects.requireNonNull;
+import static org.apache.ignite.internal.sql.engine.sql.IgniteSqlParser.PARSER_CONFIG;
+
+import java.util.Set;
+import java.util.function.LongSupplier;
+import org.apache.calcite.adapter.java.JavaTypeFactory;
+import org.apache.calcite.linq4j.QueryProvider;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.runtime.CalciteContextException;
+import org.apache.calcite.schema.SchemaPlus;
+import org.apache.calcite.sql.SqlCall;
+import org.apache.calcite.sql.SqlIdentifier;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.SqlNode;
+import org.apache.calcite.sql.parser.SqlParseException;
+import org.apache.calcite.sql.parser.SqlParser;
+import org.apache.calcite.sql.parser.SqlParserPos;
+import org.apache.calcite.sql.type.SqlTypeUtil;
+import org.apache.calcite.sql.util.SqlShuttle;
+import org.apache.calcite.sql.validate.SqlValidator;
+import org.apache.calcite.util.SourceStringReader;
+import org.apache.calcite.util.Static;
+import org.apache.calcite.util.Util.FoundOne;
+import org.apache.ignite.internal.sql.engine.api.expressions.ContextBuilder;
+import org.apache.ignite.internal.sql.engine.api.expressions.EvaluationContext;
+import org.apache.ignite.internal.sql.engine.api.expressions.ExpressionEvaluationException;
+import org.apache.ignite.internal.sql.engine.api.expressions.ExpressionFactory;
+import org.apache.ignite.internal.sql.engine.api.expressions.ExpressionParsingException;
+import org.apache.ignite.internal.sql.engine.api.expressions.ExpressionValidationException;
+import org.apache.ignite.internal.sql.engine.api.expressions.IgnitePredicate;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowAccessor;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactoryFactory;
+import org.apache.ignite.internal.sql.engine.exec.SqlEvaluationContext;
+import org.apache.ignite.internal.sql.engine.exec.exp.SqlExpressionFactory;
+import org.apache.ignite.internal.sql.engine.exec.exp.SqlPredicate;
+import org.apache.ignite.internal.sql.engine.prepare.IgnitePlanner;
+import org.apache.ignite.internal.sql.engine.prepare.PlanningContext;
+import org.apache.ignite.internal.sql.engine.sql.IgniteSqlParser;
+import org.apache.ignite.internal.sql.engine.util.Commons;
+import org.apache.ignite.internal.sql.engine.util.TypeUtils;
+import org.apache.ignite.internal.type.StructNativeType;
+import org.apache.ignite.internal.util.ExceptionUtils;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Implementation of {@link ExpressionFactory} interface which parses and validates the given expression but delegates actual implementation
+ * to the instance of {@link SqlExpressionFactory}.
+ */
+public class SqlExpressionFactoryAdapter implements ExpressionFactory {
+ private static final String SINGLE_INPUT_ROW_NAMESPACE_NAME = "INPUT";
+
+ private final SqlExpressionFactory factory;
+
+ /** Constructs tye adapter. */
+ public SqlExpressionFactoryAdapter(
+ SqlExpressionFactory factory
+ ) {
+ this.factory = factory;
+ }
+
+ @Override
+ public <RowT> ContextBuilder<RowT> contextBuilder() {
+ return new ContextBuilderImpl<>();
+ }
+
+ @Override
+ public IgnitePredicate predicate(
+ String expression,
+ StructNativeType inputRowType
+ ) throws ExpressionParsingException, ExpressionValidationException {
+ SqlNode expressionAst = parse(expression);
+
+ try {
+ // Reject subqueries earlier so we can implement lightweight validation
+ // without necessity to register all the namespaces.
+ expressionAst.accept(RejectSubQueriesValidator.INSTANCE);
+
+ // Usage of system context-dependent functions can be validated here as well.
+ expressionAst.accept(RejectContextDependentFunctionValidator.INSTANCE);
+ } catch (FoundOne one) {
+ String message = (String) one.getNode();
+
+ assert message != null;
+
+ throw new ExpressionValidationException(message);
+ }
+
+ try (IgnitePlanner planner = createPlanner()) {
+ SqlValidator validator = planner.validator();
+
+ RowBasedScope scope = new RowBasedScope(validator.getEmptyScope());
+
+ RelDataType relDataType = TypeUtils.native2relationalType(planner.getTypeFactory(), inputRowType);
+ scope.addChild(new RowNamespace(validator, relDataType), SINGLE_INPUT_ROW_NAMESPACE_NAME, false);
+
+ try {
+ expressionAst.validateExpr(validator, scope);
+
+ try {
+ // Aggregate functions are not resolved until validation, hence we cannot reject
+ // such expressions until syntax tree is validated.
+ expressionAst.accept(RejectAggregatesValidator.INSTANCE);
+ } catch (FoundOne one) {
+ String message = (String) one.getNode();
+
+ assert message != null;
+
+ throw new ExpressionValidationException(message);
+ }
+
+ RelDataType resultType = validator.deriveType(scope, expressionAst);
+
+ if (!SqlTypeUtil.isBoolean(resultType)) {
+ throw new ExpressionValidationException("Expected BOOLEAN expression but " + resultType + " was provided.");
+ }
+ } catch (CalciteContextException ex) {
+ String message = ex.getMessage();
+ if (message == null) {
+ message = "Unable to validate expression.";
+ }
+
+ throw new ExpressionValidationException(message);
+ }
+
+ RexNode rexNode = planner.sqlToRelConverter().convertExpressionExt(expressionAst, scope, relDataType);
+
+ SqlPredicate predicate = factory.predicate(
+ rexNode,
+ relDataType
+ );
+
+ return new IgnitePredicate() {
+ @Override
+ public <RowT> boolean test(EvaluationContext<RowT> context, RowT row) {
+ return predicate.test(Commons.cast(context), row);
+ }
+ };
+ }
+ }
+
+ private static SqlNode parse(String sql) throws ExpressionParsingException {
+ try (SourceStringReader reader = new SourceStringReader(sql)) {
+ SqlParser parser = SqlParser.create(reader, PARSER_CONFIG);
+
+ return parser.parseExpression();
+ } catch (SqlParseException e) {
+ String message = IgniteSqlParser.normalizeMessage(e);
+
+ throw new ExpressionParsingException(message);
+ }
+ }
+
+ private static IgnitePlanner createPlanner() {
+ return PlanningContext.builder().catalogVersion(-1).build().planner();
+ }
+
+ private static class ContextBuilderImpl<RowT> implements ContextBuilder<RowT> {
+ private LongSupplier timeProvider = System::currentTimeMillis;
+ private RowAccessor<RowT> rowAccessor;
+
+ @Override
+ public ContextBuilder<RowT> timeProvider(LongSupplier timeProvider) {
+ this.timeProvider = requireNonNull(timeProvider, "timeProvider");
+ return this;
+ }
+
+ @Override
+ public ContextBuilder<RowT> rowAccessor(RowAccessor<RowT> rowAccessor) {
+ this.rowAccessor = requireNonNull(rowAccessor, "rowAccessor");
+ return this;
+ }
+
+ @Override
+ public EvaluationContext<RowT> build() {
+ return new ContextImpl<>(
+ timeProvider,
+ new ToInternalGenericAdapter<>(rowAccessor)
+ );
+ }
+ }
+
+ private static class ContextImpl<RowT> implements EvaluationContext<RowT>, SqlEvaluationContext<RowT> {
+ private final LongSupplier timeProvider;
+ private final RowAccessor<RowT> rowAccessor;
+
+ private ContextImpl(
+ LongSupplier timeProvider,
+ RowAccessor<RowT> rowAccessor
+ ) {
+ this.timeProvider = timeProvider;
+ this.rowAccessor = rowAccessor;
+ }
+
+ @Override
+ public RowAccessor<RowT> rowAccessor() {
+ return rowAccessor;
+ }
+
+ @Override
+ public RowFactoryFactory<RowT> rowFactoryFactory() {
+ throw new AssertionError("Should not get here");
+ }
+
+ @Override
+ public @Nullable RowT correlatedVariable(int id) {
+ return null;
+ }
+
+ @Override
+ public @Nullable SchemaPlus getRootSchema() {
+ return null;
+ }
+
+ @Override
+ public JavaTypeFactory getTypeFactory() {
+ throw new AssertionError("Should not get here");
+ }
+
+ @Override
+ public QueryProvider getQueryProvider() {
+ throw new AssertionError("Should not get here");
+ }
+
+ @Override
+ public @Nullable Object get(String name) {
+ if (Variable.CURRENT_TIMESTAMP.camelName.equals(name)) {
+ return timeProvider.getAsLong();
+ }
+
+ ExceptionUtils.sneakyThrow(new ExpressionEvaluationException("Unexpected context variable requested: " + name));
+
+ throw new AssertionError("Should not get here");
+ }
+ }
+
+ private static final class RejectSubQueriesValidator extends SqlShuttle {
+ private static final RejectSubQueriesValidator INSTANCE = new RejectSubQueriesValidator();
+
+ @Override
+ public @Nullable SqlNode visit(SqlCall call) {
+ if (call.getKind().belongsTo(SqlKind.TOP_LEVEL)) {
+ String message = getMessagePrefix(call)
+ + ": Subqueries are not supported in given context.";
+ throw new FoundOne(message);
+ }
+
+ return super.visit(call);
+ }
+
+ }
+
+ private static final class RejectAggregatesValidator extends SqlShuttle {
+ private static final RejectAggregatesValidator INSTANCE = new RejectAggregatesValidator();
+
+ @Override
+ public @Nullable SqlNode visit(SqlCall call) {
+ if (call.getKind().belongsTo(SqlKind.AGGREGATE)) {
+ String message = getMessagePrefix(call)
+ + ": Aggregates are not supported in given context.";
+ throw new FoundOne(message);
+ }
+
+ return super.visit(call);
+ }
+
+ }
+
+ private static final class RejectContextDependentFunctionValidator extends SqlShuttle {
+ private static final RejectContextDependentFunctionValidator INSTANCE = new RejectContextDependentFunctionValidator();
+
+ private static final Set<String> UNSUPPORTED_FUNCTIONS = Set.of(
+ // These functions require timezone being provided.
+ "CURRENT_DATE",
+ "LOCALTIME",
+ "LOCALTIMESTAMP",
+
+ // This function requires username being provided.
+ "CURRENT_USER"
+ );
+
+ @Override
+ public @Nullable SqlNode visit(SqlIdentifier id) {
+ if (id.isSimple() && UNSUPPORTED_FUNCTIONS.contains(id.getSimple())) {
+ String message = getMessagePrefix(id)
+ + ": " + id.getSimple() + " is not supported in given context.";
+ throw new FoundOne(message);
+ }
+
+ return super.visit(id);
+ }
+ }
+
+ private static String getMessagePrefix(SqlNode n) {
+ SqlParserPos pos = n.getParserPosition();
+ return Static.RESOURCE.validatorContext(pos.getLineNum(), pos.getColumnNum(), pos.getEndLineNum(),
+ pos.getEndColumnNum()).str();
+ }
+}
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/expressions/ToInternalGenericAdapter.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/expressions/ToInternalGenericAdapter.java
new file mode 100644
index 00000000000..2851ad77791
--- /dev/null
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/expressions/ToInternalGenericAdapter.java
@@ -0,0 +1,84 @@
+/*
+ * 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.expressions;
+
+import static org.apache.ignite.internal.sql.engine.util.TypeUtils.toInternal;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.Period;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowAccessor;
+import org.apache.ignite.sql.ColumnType;
+import org.jetbrains.annotations.Nullable;
+
+/** Generic adapter which converts values to internal format based on the actual class of returned values. */
+class ToInternalGenericAdapter<RowT> implements RowAccessor<RowT> {
+ private final RowAccessor<RowT> delegate;
+
+ ToInternalGenericAdapter(RowAccessor<RowT> delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public @Nullable Object get(int field, RowT row) {
+ Object value = delegate.get(field, row);
+
+ if (value instanceof byte[]) {
+ return toInternal(value, ColumnType.BYTE_ARRAY);
+ }
+
+ if (value instanceof LocalDate) {
+ return toInternal(value, ColumnType.DATE);
+ }
+
+ if (value instanceof LocalTime) {
+ return toInternal(value, ColumnType.TIME);
+ }
+
+ if (value instanceof LocalDateTime) {
+ return toInternal(value, ColumnType.DATETIME);
+ }
+
+ if (value instanceof Instant) {
+ return toInternal(value, ColumnType.TIMESTAMP);
+ }
+
+ if (value instanceof Duration) {
+ return toInternal(value, ColumnType.DURATION);
+ }
+
+ if (value instanceof Period) {
+ return toInternal(value, ColumnType.PERIOD);
+ }
+
+ return value;
+ }
+
+ @Override
+ public boolean isNull(int field, RowT row) {
+ return delegate.isNull(field, row);
+ }
+
+ @Override
+ public int columnsCount(RowT row) {
+ return delegate.columnsCount(row);
+ }
+}
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/IgniteSqlToRelConvertor.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/IgniteSqlToRelConvertor.java
index d64c508775b..500416c8b3c 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/IgniteSqlToRelConvertor.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/IgniteSqlToRelConvertor.java
@@ -60,6 +60,7 @@ import org.apache.calcite.tools.RelBuilder;
import org.apache.calcite.util.Pair;
import org.apache.calcite.util.Util;
import org.apache.ignite.internal.sql.engine.schema.IgniteDataSource;
+import org.apache.ignite.internal.util.ExceptionUtils;
import org.jetbrains.annotations.Nullable;
/** Converts a SQL parse tree into a relational algebra operators. */
@@ -139,6 +140,31 @@ public class IgniteSqlToRelConvertor extends SqlToRelConverter implements Initia
throw new UnsupportedOperationException("Not implemented yet");
}
+ /**
+ * Converts given AST representing expression to a {@link RexNode relational expression tree}.
+ *
+ * @param node The AST root to convert.
+ * @param scope The scope to use to resolve column reference, if any.
+ * @param inputRowType The input row type. Should be provided if expression contains any column references.
+ * @return A converted expression tree.
+ */
+ public RexNode convertExpressionExt(SqlNode node, SqlValidatorScope scope, @Nullable RelDataType inputRowType) {
+ //
+ Blackboard bb = createBlackboard(scope, null, false);
+
+ if (inputRowType != null) {
+ bb.setRoot(relBuilder.values(inputRowType).build(), true);
+ }
+
+ try {
+ REPLACE_SUB_QUERIES.invoke(this, bb, node, RelOptUtil.Logic.TRUE_FALSE_UNKNOWN);
+ } catch (Throwable throwable) {
+ ExceptionUtils.sneakyThrow(throwable);
+ }
+
+ return bb.convertExpression(node);
+ }
+
@Override public RelNode convertValues(SqlCall values, RelDataType targetRowType) {
// TODO https://issues.apache.org/jira/browse/IGNITE-27396
// FIX: Original convertValuesImpl adds additional type casts that are not correct
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/KeyValueGetPlan.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/KeyValueGetPlan.java
index cff5ebae41e..e7d4318cfaa 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/KeyValueGetPlan.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/KeyValueGetPlan.java
@@ -34,12 +34,12 @@ import org.apache.ignite.internal.sql.engine.InternalSqlRow;
import org.apache.ignite.internal.sql.engine.InternalSqlRowImpl;
import org.apache.ignite.internal.sql.engine.SchemaAwareConverter;
import org.apache.ignite.internal.sql.engine.SqlQueryType;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.AsyncDataCursor;
import org.apache.ignite.internal.sql.engine.exec.ExecutablePlan;
import org.apache.ignite.internal.sql.engine.exec.ExecutableTable;
import org.apache.ignite.internal.sql.engine.exec.ExecutableTableRegistry;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
-import org.apache.ignite.internal.sql.engine.exec.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.RowHandler;
import org.apache.ignite.internal.sql.engine.exec.ScannableTable;
import org.apache.ignite.internal.sql.engine.exec.exp.SqlPredicate;
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/IgniteSqlParser.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/IgniteSqlParser.java
index 263acd3f23f..1a15835c734 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/IgniteSqlParser.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/IgniteSqlParser.java
@@ -181,6 +181,22 @@ public final class IgniteSqlParser {
* @return An instance of SqlException.
*/
private static SqlException convertException(SqlParseException ex) {
+ String message = normalizeMessage(ex);
+
+ return new SqlException(STMT_PARSE_ERR, "Failed to parse query: " + message, ex);
+ }
+
+ /**
+ * Cut out part of the message from original exception with suggested options.
+ *
+ * <p>The reason to do that is currently grammar of the parser is not aligned with Ignite's capabilities. As a result, the message may
+ * contain misleading options. Another problem is that message sometimes may contain almost every keyword, bloating the message up to
+ * hundreds of lines (given that every keyword is on a new line).
+ *
+ * @param ex The original exception.
+ * @return A normalized message.
+ */
+ public static String normalizeMessage(SqlParseException ex) {
Throwable cause = ex.getCause();
String message;
@@ -252,8 +268,7 @@ public final class IgniteSqlParser {
ex.getPos().getColumnNum()
);
}
-
- return new SqlException(STMT_PARSE_ERR, "Failed to parse query: " + message, ex);
+ return message;
}
private static final class InternalIgniteSqlParser extends IgniteSqlParserImpl {
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/IgniteMethod.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/IgniteMethod.java
index e30a8e426cc..a569adb45c7 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/IgniteMethod.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/IgniteMethod.java
@@ -31,8 +31,8 @@ import org.apache.calcite.linq4j.tree.Types;
import org.apache.calcite.runtime.SqlFunctions;
import org.apache.calcite.sql.SqlIntervalQualifier;
import org.apache.calcite.sql.parser.SqlParserUtil;
-import org.apache.ignite.internal.sql.engine.exec.RowAccessor;
-import org.apache.ignite.internal.sql.engine.exec.RowFactory.RowBuilder;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowAccessor;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory.RowBuilder;
import org.apache.ignite.internal.sql.engine.exec.RowHandler;
import org.apache.ignite.internal.sql.engine.exec.SqlEvaluationContext;
import org.apache.ignite.internal.sql.engine.exec.exp.IgniteSqlFunctions;
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/TypeUtils.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/TypeUtils.java
index 29f995c12fe..1040a7bde82 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/TypeUtils.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/TypeUtils.java
@@ -52,9 +52,9 @@ import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.sql.type.SqlTypeName.Limit;
import org.apache.calcite.sql.type.SqlTypeUtil;
import org.apache.ignite.internal.sql.engine.SchemaAwareConverter;
-import org.apache.ignite.internal.sql.engine.exec.RowFactory;
-import org.apache.ignite.internal.sql.engine.exec.RowFactory.RowBuilder;
-import org.apache.ignite.internal.sql.engine.exec.RowFactoryFactory;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory.RowBuilder;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.RowHandler;
import org.apache.ignite.internal.sql.engine.prepare.ParameterType;
import org.apache.ignite.internal.sql.engine.type.IgniteTypeFactory;
@@ -523,6 +523,23 @@ public class TypeUtils {
var dt = (TemporalNativeType) nativeType;
return factory.createSqlType(SqlTypeName.TIMESTAMP, dt.precision());
+ case NULL:
+ return factory.createSqlType(SqlTypeName.NULL);
+ case STRUCT:
+ assert nativeType instanceof StructNativeType;
+
+ var st = (StructNativeType) nativeType;
+
+ RelDataTypeFactory.Builder builder = new RelDataTypeFactory.Builder(factory);
+
+ for (StructNativeType.Field field : st.fields()) {
+ builder.add(
+ field.name(),
+ native2relationalType(factory, field.type(), field.nullable())
+ );
+ }
+
+ return builder.build();
default:
throw new IllegalStateException("Unexpected native type " + nativeType);
}
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/DummyScannableTable.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/DummyScannableTable.java
index 3745b8b78bc..b2f0f01e44f 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/DummyScannableTable.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/DummyScannableTable.java
@@ -20,6 +20,7 @@ package org.apache.ignite.internal.sql.engine.exec;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Flow.Publisher;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.exp.RangeCondition;
import org.apache.ignite.internal.tx.InternalTransaction;
import org.apache.ignite.internal.util.SubscriptionUtils;
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/IdentityDistributionFunctionSelfTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/IdentityDistributionFunctionSelfTest.java
index 40ea06a331b..fc61990ecc8 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/IdentityDistributionFunctionSelfTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/IdentityDistributionFunctionSelfTest.java
@@ -27,6 +27,7 @@ import it.unimi.dsi.fastutil.longs.LongList;
import java.util.Collection;
import java.util.List;
import org.apache.calcite.rel.RelDistribution.Type;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.mapping.ColocationGroup;
import org.apache.ignite.internal.sql.engine.framework.ArrayRowHandler;
import org.apache.ignite.internal.sql.engine.trait.Destination;
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/PartitionsResolutionTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/PartitionsResolutionTest.java
index 05549d808d0..49f6414c049 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/PartitionsResolutionTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/PartitionsResolutionTest.java
@@ -24,6 +24,7 @@ import java.util.List;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactory.Builder;
import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.SqlRowHandler.RowWrapper;
import org.apache.ignite.internal.sql.engine.planner.AbstractPlannerTest.TestTableDescriptor;
import org.apache.ignite.internal.sql.engine.schema.ColumnDescriptor;
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/ProjectedTableRowConverterSelfTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/ProjectedTableRowConverterSelfTest.java
index c6dd0c82847..244e551d4f7 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/ProjectedTableRowConverterSelfTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/ProjectedTableRowConverterSelfTest.java
@@ -31,6 +31,7 @@ import org.apache.ignite.internal.schema.BinaryRowImpl;
import org.apache.ignite.internal.schema.Column;
import org.apache.ignite.internal.schema.SchemaDescriptor;
import org.apache.ignite.internal.schema.SchemaRegistry;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.SqlRowHandler.RowWrapper;
import org.apache.ignite.internal.testframework.BaseIgniteAbstractTest;
import org.apache.ignite.internal.type.NativeType;
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/TableRowConverterSelfTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/TableRowConverterSelfTest.java
index 5af0faf98a2..7ccbe58a4fb 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/TableRowConverterSelfTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/TableRowConverterSelfTest.java
@@ -35,6 +35,7 @@ import org.apache.ignite.internal.schema.BinaryTuple;
import org.apache.ignite.internal.schema.Column;
import org.apache.ignite.internal.schema.SchemaDescriptor;
import org.apache.ignite.internal.schema.SchemaRegistry;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.SqlRowHandler.RowWrapper;
import org.apache.ignite.internal.sql.engine.schema.TableDescriptor;
import org.apache.ignite.internal.testframework.BaseIgniteAbstractTest;
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/AbstractExecutionTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/AbstractExecutionTest.java
index f6dc9e16a0b..78bdb8b1db7 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/AbstractExecutionTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/AbstractExecutionTest.java
@@ -54,10 +54,10 @@ import org.apache.ignite.internal.schema.BinaryRowConverter;
import org.apache.ignite.internal.schema.BinaryTuple;
import org.apache.ignite.internal.schema.BinaryTupleSchema;
import org.apache.ignite.internal.sql.SqlCommon;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
import org.apache.ignite.internal.sql.engine.exec.ExecutionId;
import org.apache.ignite.internal.sql.engine.exec.QueryTaskExecutorImpl;
-import org.apache.ignite.internal.sql.engine.exec.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.RowHandler;
import org.apache.ignite.internal.sql.engine.exec.SqlEvaluationContext;
import org.apache.ignite.internal.sql.engine.exec.TxAttributes;
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/AbstractJoinExecutionTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/AbstractJoinExecutionTest.java
index 708ad85831b..93525d533d1 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/AbstractJoinExecutionTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/AbstractJoinExecutionTest.java
@@ -42,8 +42,8 @@ import org.apache.calcite.rel.core.JoinInfo;
import org.apache.calcite.rel.core.JoinRelType;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.util.ImmutableIntList;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
-import org.apache.ignite.internal.sql.engine.exec.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.RowHandler;
import org.apache.ignite.internal.sql.engine.exec.TestDownstream;
import org.apache.ignite.internal.sql.engine.exec.exp.SqlComparator;
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/AbstractSetOpExecutionTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/AbstractSetOpExecutionTest.java
index 99ef0b5b40d..1192e1c2a44 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/AbstractSetOpExecutionTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/AbstractSetOpExecutionTest.java
@@ -34,8 +34,8 @@ import java.util.stream.IntStream;
import org.apache.calcite.rel.RelCollation;
import org.apache.calcite.rel.RelCollations;
import org.apache.calcite.util.ImmutableIntList;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
-import org.apache.ignite.internal.sql.engine.exec.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.RowHandler;
import org.apache.ignite.internal.sql.engine.exec.exp.SqlComparator;
import org.apache.ignite.internal.sql.engine.exec.exp.agg.AggregateType;
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/AsyncRootNodeTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/AsyncRootNodeTest.java
index 63f5953b130..45109a745b6 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/AsyncRootNodeTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/AsyncRootNodeTest.java
@@ -40,9 +40,9 @@ import org.apache.ignite.internal.lang.InternalTuple;
import org.apache.ignite.internal.schema.BinaryTuple;
import org.apache.ignite.internal.schema.BinaryTupleSchema;
import org.apache.ignite.internal.schema.BinaryTupleSchema.Element;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
-import org.apache.ignite.internal.sql.engine.exec.RowFactory;
-import org.apache.ignite.internal.sql.engine.exec.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.RowHandler;
import org.apache.ignite.internal.sql.engine.exec.ScannableDataSource;
import org.apache.ignite.internal.sql.engine.exec.SqlRowHandler;
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/BaseAggregateTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/BaseAggregateTest.java
index 525de8f8e71..664e6c07ea6 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/BaseAggregateTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/BaseAggregateTest.java
@@ -44,8 +44,8 @@ import org.apache.calcite.sql.fun.SqlStdOperatorTable;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.util.ImmutableBitSet;
import org.apache.calcite.util.ImmutableIntList;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
-import org.apache.ignite.internal.sql.engine.exec.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.RowHandler;
import org.apache.ignite.internal.sql.engine.exec.exp.agg.AccumulatorWrapper;
import org.apache.ignite.internal.sql.engine.exec.exp.agg.Accumulators;
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/CorrelatedNestedLoopJoinExecutionTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/CorrelatedNestedLoopJoinExecutionTest.java
index d1a4106384c..bae4ea2971a 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/CorrelatedNestedLoopJoinExecutionTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/CorrelatedNestedLoopJoinExecutionTest.java
@@ -35,8 +35,8 @@ import java.util.stream.StreamSupport;
import org.apache.calcite.rel.core.CorrelationId;
import org.apache.calcite.rel.core.JoinRelType;
import org.apache.calcite.rel.type.RelDataType;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
-import org.apache.ignite.internal.sql.engine.exec.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.RowHandler;
import org.apache.ignite.internal.sql.engine.exec.exp.func.IterableTableFunction;
import org.apache.ignite.internal.sql.engine.framework.ArrayRowHandler;
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/DataSourceScanNodeExecutionTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/DataSourceScanNodeExecutionTest.java
index 2f6447582c6..1bc6ea63ebd 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/DataSourceScanNodeExecutionTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/DataSourceScanNodeExecutionTest.java
@@ -41,9 +41,9 @@ import org.apache.calcite.util.ImmutableIntList;
import org.apache.ignite.internal.lang.InternalTuple;
import org.apache.ignite.internal.schema.BinaryTupleSchema;
import org.apache.ignite.internal.schema.BinaryTupleSchema.Element;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
-import org.apache.ignite.internal.sql.engine.exec.RowFactory;
-import org.apache.ignite.internal.sql.engine.exec.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.RowHandler;
import org.apache.ignite.internal.sql.engine.exec.ScannableDataSource;
import org.apache.ignite.internal.sql.engine.exec.SqlRowHandler;
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/ExchangeExecutionTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/ExchangeExecutionTest.java
index 3f8ab4dbf85..403422e9d72 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/ExchangeExecutionTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/ExchangeExecutionTest.java
@@ -52,6 +52,7 @@ import org.apache.ignite.internal.metrics.NoOpMetricManager;
import org.apache.ignite.internal.network.ClusterNodeImpl;
import org.apache.ignite.internal.network.ClusterService;
import org.apache.ignite.internal.network.InternalClusterNode;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.ExchangeService;
import org.apache.ignite.internal.sql.engine.exec.ExchangeServiceImpl;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
@@ -59,7 +60,6 @@ import org.apache.ignite.internal.sql.engine.exec.MailboxRegistry;
import org.apache.ignite.internal.sql.engine.exec.MailboxRegistryImpl;
import org.apache.ignite.internal.sql.engine.exec.QueryTaskExecutor;
import org.apache.ignite.internal.sql.engine.exec.QueryTaskExecutorImpl;
-import org.apache.ignite.internal.sql.engine.exec.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.RowHandler;
import org.apache.ignite.internal.sql.engine.exec.mapping.FragmentDescription;
import org.apache.ignite.internal.sql.engine.framework.ArrayRowHandler;
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/ExecutionTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/ExecutionTest.java
index 1a1548aa7ad..8394a75b869 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/ExecutionTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/ExecutionTest.java
@@ -34,8 +34,8 @@ import java.util.List;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.ignite.internal.lang.IgniteStringBuilder;
import org.apache.ignite.internal.lang.RunnableX;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
-import org.apache.ignite.internal.sql.engine.exec.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.RowHandler;
import org.apache.ignite.internal.sql.engine.framework.ArrayRowHandler;
import org.apache.ignite.internal.sql.engine.type.IgniteTypeFactory;
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/FilterExecutionTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/FilterExecutionTest.java
index 0d4c38ff111..00714b402dc 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/FilterExecutionTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/FilterExecutionTest.java
@@ -27,8 +27,8 @@ import java.util.function.Predicate;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
-import org.apache.ignite.internal.sql.engine.exec.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.RowHandler;
import org.apache.ignite.internal.sql.engine.framework.ArrayRowHandler;
import org.junit.jupiter.api.Test;
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/HashAggregateExecutionTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/HashAggregateExecutionTest.java
index 157975c218b..020070842b8 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/HashAggregateExecutionTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/HashAggregateExecutionTest.java
@@ -32,8 +32,8 @@ import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.util.ImmutableBitSet;
import org.apache.calcite.util.ImmutableIntList;
import org.apache.calcite.util.mapping.Mapping;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
-import org.apache.ignite.internal.sql.engine.exec.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.exp.SqlComparator;
import org.apache.ignite.internal.sql.engine.rel.agg.MapReduceAggregates;
import org.apache.ignite.internal.sql.engine.rel.agg.MapReduceAggregates.MapReduceAgg;
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/HashAggregateSingleGroupExecutionTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/HashAggregateSingleGroupExecutionTest.java
index 077201b1f37..dde90c874b6 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/HashAggregateSingleGroupExecutionTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/HashAggregateSingleGroupExecutionTest.java
@@ -34,9 +34,9 @@ import org.apache.calcite.sql.SqlAggFunction;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.util.ImmutableBitSet;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
-import org.apache.ignite.internal.sql.engine.exec.RowFactory;
-import org.apache.ignite.internal.sql.engine.exec.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.RowHandler;
import org.apache.ignite.internal.sql.engine.exec.exp.agg.AccumulatorWrapper;
import org.apache.ignite.internal.sql.engine.exec.exp.agg.AggregateType;
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/HashIndexSpoolExecutionTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/HashIndexSpoolExecutionTest.java
index 8c4bef54949..5c6b5ddd969 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/HashIndexSpoolExecutionTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/HashIndexSpoolExecutionTest.java
@@ -27,8 +27,8 @@ import java.util.List;
import java.util.function.Predicate;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.util.ImmutableBitSet;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
-import org.apache.ignite.internal.sql.engine.exec.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.RowHandler;
import org.apache.ignite.internal.sql.engine.framework.ArrayRowHandler;
import org.apache.ignite.internal.sql.engine.type.IgniteTypeFactory;
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/IndexScanNodeExecutionTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/IndexScanNodeExecutionTest.java
index 04735d48319..eb97e9dfc7a 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/IndexScanNodeExecutionTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/IndexScanNodeExecutionTest.java
@@ -42,11 +42,11 @@ import org.apache.calcite.rel.RelFieldCollation;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactory.Builder;
import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
import org.apache.ignite.internal.sql.engine.exec.PartitionProvider;
import org.apache.ignite.internal.sql.engine.exec.PartitionWithConsistencyToken;
-import org.apache.ignite.internal.sql.engine.exec.RowFactory;
-import org.apache.ignite.internal.sql.engine.exec.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.RowHandler;
import org.apache.ignite.internal.sql.engine.exec.ScannableTable;
import org.apache.ignite.internal.sql.engine.exec.exp.RangeCondition;
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/IntersectExecutionTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/IntersectExecutionTest.java
index b69215ffc88..9b0abdad0bc 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/IntersectExecutionTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/IntersectExecutionTest.java
@@ -19,8 +19,8 @@ package org.apache.ignite.internal.sql.engine.exec.rel;
import java.util.Arrays;
import java.util.List;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
-import org.apache.ignite.internal.sql.engine.exec.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.exp.agg.AggregateType;
import org.apache.ignite.internal.type.NativeTypes;
import org.apache.ignite.internal.type.StructNativeType;
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/LimitExecutionTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/LimitExecutionTest.java
index 9a7ba28e2ab..2ded4199d11 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/LimitExecutionTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/LimitExecutionTest.java
@@ -26,8 +26,8 @@ import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
-import org.apache.ignite.internal.sql.engine.exec.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.RowHandler;
import org.apache.ignite.internal.sql.engine.framework.ArrayRowHandler;
import org.junit.jupiter.api.Test;
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/MergeJoinExecutionTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/MergeJoinExecutionTest.java
index 0896a8847fc..5c8ceb7f7db 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/MergeJoinExecutionTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/MergeJoinExecutionTest.java
@@ -43,8 +43,8 @@ import org.apache.calcite.rel.RelFieldCollation.NullDirection;
import org.apache.calcite.rel.core.JoinRelType;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.util.ImmutableBitSet;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
-import org.apache.ignite.internal.sql.engine.exec.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.RowHandler;
import org.apache.ignite.internal.sql.engine.exec.exp.SqlComparator;
import org.apache.ignite.internal.sql.engine.exec.exp.SqlExpressionFactoryImpl;
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/ModifyNodeExecutionTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/ModifyNodeExecutionTest.java
index fb61017b633..cb2bdc0aa83 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/ModifyNodeExecutionTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/ModifyNodeExecutionTest.java
@@ -42,10 +42,10 @@ import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Stream;
import org.apache.calcite.rel.core.TableModify.Operation;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory.RowBuilder;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
-import org.apache.ignite.internal.sql.engine.exec.RowFactory;
-import org.apache.ignite.internal.sql.engine.exec.RowFactory.RowBuilder;
-import org.apache.ignite.internal.sql.engine.exec.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.RowHandler;
import org.apache.ignite.internal.sql.engine.exec.SqlRowHandler;
import org.apache.ignite.internal.sql.engine.exec.SqlRowHandler.RowWrapper;
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/ScanNodeExecutionTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/ScanNodeExecutionTest.java
index db06b339347..a7f640c60d3 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/ScanNodeExecutionTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/ScanNodeExecutionTest.java
@@ -31,8 +31,8 @@ import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import org.apache.ignite.internal.sql.engine.QueryCancelledException;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
-import org.apache.ignite.internal.sql.engine.exec.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.RowHandler;
import org.apache.ignite.internal.sql.engine.exec.exp.func.TableFunction;
import org.apache.ignite.internal.sql.engine.exec.exp.func.TableFunctionInstance;
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/ScannableTableSelfTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/ScannableTableSelfTest.java
index b3b19dced52..7b33c59ac0b 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/ScannableTableSelfTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/ScannableTableSelfTest.java
@@ -61,9 +61,9 @@ import org.apache.ignite.internal.schema.BinaryRow;
import org.apache.ignite.internal.schema.BinaryRowEx;
import org.apache.ignite.internal.schema.BinaryTuple;
import org.apache.ignite.internal.schema.BinaryTuplePrefix;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
import org.apache.ignite.internal.sql.engine.exec.PartitionWithConsistencyToken;
-import org.apache.ignite.internal.sql.engine.exec.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.ScannableTable;
import org.apache.ignite.internal.sql.engine.exec.ScannableTableImpl;
import org.apache.ignite.internal.sql.engine.exec.TableRowConverter;
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/SortAggregateExecutionTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/SortAggregateExecutionTest.java
index f274b81a455..f37afc7745a 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/SortAggregateExecutionTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/SortAggregateExecutionTest.java
@@ -34,8 +34,8 @@ import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.util.ImmutableBitSet;
import org.apache.calcite.util.ImmutableIntList;
import org.apache.calcite.util.mapping.Mapping;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
-import org.apache.ignite.internal.sql.engine.exec.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.exp.SqlComparator;
import org.apache.ignite.internal.sql.engine.rel.agg.MapReduceAggregates;
import org.apache.ignite.internal.sql.engine.rel.agg.MapReduceAggregates.MapReduceAgg;
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/SortedIndexSpoolExecutionTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/SortedIndexSpoolExecutionTest.java
index 651d82101de..a0b19ada4fd 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/SortedIndexSpoolExecutionTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/SortedIndexSpoolExecutionTest.java
@@ -26,8 +26,8 @@ import java.util.function.Predicate;
import org.apache.calcite.rel.RelCollations;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.util.ImmutableIntList;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
-import org.apache.ignite.internal.sql.engine.exec.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.RowHandler;
import org.apache.ignite.internal.sql.engine.framework.ArrayRowHandler;
import org.apache.ignite.internal.sql.engine.type.IgniteTypeFactory;
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/SystemRangeTableFunctionTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/SystemRangeTableFunctionTest.java
index a799f60ca47..de9ef8fa6aa 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/SystemRangeTableFunctionTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/SystemRangeTableFunctionTest.java
@@ -27,8 +27,8 @@ import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.apache.calcite.rex.RexBuilder;
import org.apache.calcite.rex.RexCall;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
-import org.apache.ignite.internal.sql.engine.exec.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.RowHandler;
import org.apache.ignite.internal.sql.engine.exec.exp.func.SystemRangeTableFunction;
import org.apache.ignite.internal.sql.engine.exec.exp.func.TableFunction;
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/TableScanNodeExecutionTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/TableScanNodeExecutionTest.java
index 6c99c9d1ca8..780b457a9f0 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/TableScanNodeExecutionTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/TableScanNodeExecutionTest.java
@@ -67,11 +67,11 @@ import org.apache.ignite.internal.replicator.ReplicaService;
import org.apache.ignite.internal.schema.BinaryRow;
import org.apache.ignite.internal.schema.BinaryRowEx;
import org.apache.ignite.internal.sql.SqlCommon;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
import org.apache.ignite.internal.sql.engine.exec.PartitionProvider;
import org.apache.ignite.internal.sql.engine.exec.PartitionWithConsistencyToken;
-import org.apache.ignite.internal.sql.engine.exec.RowFactory;
-import org.apache.ignite.internal.sql.engine.exec.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.RowHandler;
import org.apache.ignite.internal.sql.engine.exec.ScannableTable;
import org.apache.ignite.internal.sql.engine.exec.ScannableTableImpl;
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/TableSpoolExecutionTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/TableSpoolExecutionTest.java
index 2ef9f53d06f..39cb4d01bc0 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/TableSpoolExecutionTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/TableSpoolExecutionTest.java
@@ -27,8 +27,8 @@ import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiFunction;
import java.util.stream.IntStream;
import org.apache.calcite.rel.type.RelDataType;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
-import org.apache.ignite.internal.sql.engine.exec.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.RowHandler;
import org.apache.ignite.internal.sql.engine.framework.ArrayRowHandler;
import org.apache.ignite.internal.sql.engine.type.IgniteTypeFactory;
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/row/SqlRowHandlerTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/row/SqlRowHandlerTest.java
index e1e5a94d62f..00f95f49bb4 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/row/SqlRowHandlerTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/row/SqlRowHandlerTest.java
@@ -36,8 +36,8 @@ import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Stream;
import org.apache.ignite.internal.schema.BinaryTuple;
-import org.apache.ignite.internal.sql.engine.exec.RowFactory;
-import org.apache.ignite.internal.sql.engine.exec.RowFactory.RowBuilder;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory.RowBuilder;
import org.apache.ignite.internal.sql.engine.exec.SqlRowHandler;
import org.apache.ignite.internal.sql.engine.exec.SqlRowHandler.RowWrapper;
import org.apache.ignite.internal.sql.engine.util.SqlTestUtils;
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/expressions/AbstractExpressionFactoryTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/expressions/AbstractExpressionFactoryTest.java
new file mode 100644
index 00000000000..e234c05dff8
--- /dev/null
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/expressions/AbstractExpressionFactoryTest.java
@@ -0,0 +1,64 @@
+/*
+ * 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.expressions;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.empty;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.Method;
+import java.util.EnumSet;
+import org.apache.ignite.sql.ColumnType;
+import org.junit.jupiter.api.Test;
+
+abstract class AbstractExpressionFactoryTest {
+ @Test
+ void ensureAllMarshallableEntryTypesAreCovered() {
+ EnumSet<ColumnType> typesToExclude = EnumSet.of(
+ ColumnType.STRUCT, ColumnType.DURATION, ColumnType.PERIOD // are not fully supported yet.
+ );
+
+ EnumSet<ColumnType> missedTypes = EnumSet.complementOf(typesToExclude);
+
+ for (Method method : this.getClass().getDeclaredMethods()) {
+ if (!method.isAnnotationPresent(TestFor.class)) {
+ continue;
+ }
+
+ TestFor type = method.getAnnotation(TestFor.class);
+
+ missedTypes.remove(type.value());
+ }
+
+ assertThat("All column types must be covered. "
+ + "Please add new test method for every missed column type using following template:\n\n"
+ + " @TestForType(ColumnType.MISSED_TYPE)\n"
+ + " void missedTypeTest() throws Exception {\n"
+ + " // do test\n"
+ + " }\n", missedTypes, empty());
+ }
+
+ @Target(ElementType.METHOD)
+ @Retention(RetentionPolicy.RUNTIME)
+ @interface TestFor {
+ ColumnType value();
+ }
+}
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/expressions/IgnitePredicateTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/expressions/IgnitePredicateTest.java
new file mode 100644
index 00000000000..7067ca0ff7a
--- /dev/null
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/expressions/IgnitePredicateTest.java
@@ -0,0 +1,1172 @@
+/*
+ * 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.expressions;
+
+import static org.apache.ignite.internal.testframework.IgniteTestUtils.assertThrows;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+import java.math.BigDecimal;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.util.UUID;
+import org.apache.ignite.internal.sql.engine.api.expressions.EvaluationContext;
+import org.apache.ignite.internal.sql.engine.api.expressions.ExpressionEvaluationException;
+import org.apache.ignite.internal.sql.engine.api.expressions.ExpressionFactory;
+import org.apache.ignite.internal.sql.engine.api.expressions.ExpressionParsingException;
+import org.apache.ignite.internal.sql.engine.api.expressions.ExpressionValidationException;
+import org.apache.ignite.internal.sql.engine.api.expressions.IgnitePredicate;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowAccessor;
+import org.apache.ignite.internal.sql.engine.exec.exp.SqlExpressionFactoryImpl;
+import org.apache.ignite.internal.sql.engine.util.Commons;
+import org.apache.ignite.internal.sql.engine.util.EmptyCacheFactory;
+import org.apache.ignite.internal.type.NativeType;
+import org.apache.ignite.internal.type.NativeTypes;
+import org.apache.ignite.internal.type.StructNativeType;
+import org.apache.ignite.sql.ColumnType;
+import org.jetbrains.annotations.Nullable;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
+
+@SuppressWarnings("ThrowableNotThrown")
+class IgnitePredicateTest extends AbstractExpressionFactoryTest {
+ private final ExpressionFactory factory = new SqlExpressionFactoryAdapter(
+ new SqlExpressionFactoryImpl(
+ Commons.typeFactory(), 0, EmptyCacheFactory.INSTANCE
+ )
+ );
+
+ @Test
+ @TestFor(ColumnType.BOOLEAN)
+ void booleanTest() throws Exception {
+ EvaluationContext<Object[]> context = context();
+ StructNativeType inputType = singleColumnType(NativeTypes.BOOLEAN);
+
+ {
+ IgnitePredicate predicate = factory.predicate("val", inputType);
+
+ assertThat(predicate.test(context, row(true)), is(true));
+ assertThat(predicate.test(context, row(false)), is(false));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val = true", inputType);
+
+ assertThat(predicate.test(context, row(true)), is(true));
+ assertThat(predicate.test(context, row(false)), is(false));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val <> true", inputType);
+
+ assertThat(predicate.test(context, row(true)), is(false));
+ assertThat(predicate.test(context, row(false)), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val IS NULL", inputType);
+
+ assertThat(predicate.test(context, row(true)), is(false));
+ assertThat(predicate.test(context, row(false)), is(false));
+ assertThat(predicate.test(context, row(null)), is(true));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val IS NOT NULL", inputType);
+
+ assertThat(predicate.test(context, row(true)), is(true));
+ assertThat(predicate.test(context, row(false)), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+ }
+
+ @Test
+ @TestFor(ColumnType.INT8)
+ void byteTest() throws Exception {
+ EvaluationContext<Object[]> context = context();
+ StructNativeType inputType = singleColumnType(NativeTypes.INT8);
+
+ {
+ IgnitePredicate predicate = factory.predicate("val > 0", inputType);
+
+ assertThat(predicate.test(context, row((byte) -100)), is(false));
+ assertThat(predicate.test(context, row((byte) 0)), is(false));
+ assertThat(predicate.test(context, row((byte) 100)), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val <> 0", inputType);
+
+ assertThat(predicate.test(context, row((byte) -100)), is(true));
+ assertThat(predicate.test(context, row((byte) 0)), is(false));
+ assertThat(predicate.test(context, row((byte) 100)), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val <= 0", inputType);
+
+ assertThat(predicate.test(context, row((byte) -100)), is(true));
+ assertThat(predicate.test(context, row((byte) 0)), is(true));
+ assertThat(predicate.test(context, row((byte) 100)), is(false));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val IS NULL", inputType);
+
+ assertThat(predicate.test(context, row((byte) -100)), is(false));
+ assertThat(predicate.test(context, row((byte) 0)), is(false));
+ assertThat(predicate.test(context, row((byte) 100)), is(false));
+ assertThat(predicate.test(context, row(null)), is(true));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val IS NOT NULL", inputType);
+
+ assertThat(predicate.test(context, row((byte) -100)), is(true));
+ assertThat(predicate.test(context, row((byte) 0)), is(true));
+ assertThat(predicate.test(context, row((byte) 100)), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val < 0.1", inputType);
+
+ assertThat(predicate.test(context, row((byte) -100)), is(true));
+ assertThat(predicate.test(context, row((byte) 0)), is(true));
+ assertThat(predicate.test(context, row((byte) 100)), is(false));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val > 0.1", inputType);
+
+ assertThat(predicate.test(context, row((byte) -100)), is(false));
+ assertThat(predicate.test(context, row((byte) 0)), is(false));
+ assertThat(predicate.test(context, row((byte) 100)), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+ }
+
+ @Test
+ @TestFor(ColumnType.INT16)
+ void shortTest() throws Exception {
+ EvaluationContext<Object[]> context = context();
+ StructNativeType inputType = singleColumnType(NativeTypes.INT16);
+
+ {
+ IgnitePredicate predicate = factory.predicate("val > 0", inputType);
+
+ assertThat(predicate.test(context, row((short) -100)), is(false));
+ assertThat(predicate.test(context, row((short) 0)), is(false));
+ assertThat(predicate.test(context, row((short) 100)), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val <> 0", inputType);
+
+ assertThat(predicate.test(context, row((short) -100)), is(true));
+ assertThat(predicate.test(context, row((short) 0)), is(false));
+ assertThat(predicate.test(context, row((short) 100)), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val <= 0", inputType);
+
+ assertThat(predicate.test(context, row((short) -100)), is(true));
+ assertThat(predicate.test(context, row((short) 0)), is(true));
+ assertThat(predicate.test(context, row((short) 100)), is(false));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val IS NULL", inputType);
+
+ assertThat(predicate.test(context, row((short) -100)), is(false));
+ assertThat(predicate.test(context, row((short) 0)), is(false));
+ assertThat(predicate.test(context, row((short) 100)), is(false));
+ assertThat(predicate.test(context, row(null)), is(true));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val IS NOT NULL", inputType);
+
+ assertThat(predicate.test(context, row((short) -100)), is(true));
+ assertThat(predicate.test(context, row((short) 0)), is(true));
+ assertThat(predicate.test(context, row((short) 100)), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val < 0.1", inputType);
+
+ assertThat(predicate.test(context, row((short) -100)), is(true));
+ assertThat(predicate.test(context, row((short) 0)), is(true));
+ assertThat(predicate.test(context, row((short) 100)), is(false));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val > 0.1", inputType);
+
+ assertThat(predicate.test(context, row((short) -100)), is(false));
+ assertThat(predicate.test(context, row((short) 0)), is(false));
+ assertThat(predicate.test(context, row((short) 100)), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+ }
+
+ @Test
+ @TestFor(ColumnType.INT32)
+ void intTest() throws Exception {
+ EvaluationContext<Object[]> context = context();
+ StructNativeType inputType = singleColumnType(NativeTypes.INT32);
+
+ {
+ IgnitePredicate predicate = factory.predicate("val > 0", inputType);
+
+ assertThat(predicate.test(context, row(-100)), is(false));
+ assertThat(predicate.test(context, row(0)), is(false));
+ assertThat(predicate.test(context, row(100)), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val <> 0", inputType);
+
+ assertThat(predicate.test(context, row(-100)), is(true));
+ assertThat(predicate.test(context, row(0)), is(false));
+ assertThat(predicate.test(context, row(100)), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val <= 0", inputType);
+
+ assertThat(predicate.test(context, row(-100)), is(true));
+ assertThat(predicate.test(context, row(0)), is(true));
+ assertThat(predicate.test(context, row(100)), is(false));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val IS NULL", inputType);
+
+ assertThat(predicate.test(context, row(-100)), is(false));
+ assertThat(predicate.test(context, row(0)), is(false));
+ assertThat(predicate.test(context, row(100)), is(false));
+ assertThat(predicate.test(context, row(null)), is(true));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val IS NOT NULL", inputType);
+
+ assertThat(predicate.test(context, row(-100)), is(true));
+ assertThat(predicate.test(context, row(0)), is(true));
+ assertThat(predicate.test(context, row(100)), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val < 0.1", inputType);
+
+ assertThat(predicate.test(context, row(-100)), is(true));
+ assertThat(predicate.test(context, row(0)), is(true));
+ assertThat(predicate.test(context, row(100)), is(false));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val > 0.1", inputType);
+
+ assertThat(predicate.test(context, row(-100)), is(false));
+ assertThat(predicate.test(context, row(0)), is(false));
+ assertThat(predicate.test(context, row(100)), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+ }
+
+ @Test
+ @TestFor(ColumnType.INT64)
+ void longTest() throws Exception {
+ EvaluationContext<Object[]> context = context();
+ StructNativeType inputType = singleColumnType(NativeTypes.INT64);
+
+ {
+ IgnitePredicate predicate = factory.predicate("val > 0", inputType);
+
+ assertThat(predicate.test(context, row(-100L)), is(false));
+ assertThat(predicate.test(context, row(0L)), is(false));
+ assertThat(predicate.test(context, row(100L)), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val <> 0", inputType);
+
+ assertThat(predicate.test(context, row(-100L)), is(true));
+ assertThat(predicate.test(context, row(0L)), is(false));
+ assertThat(predicate.test(context, row(100L)), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val <= 0", inputType);
+
+ assertThat(predicate.test(context, row(-100L)), is(true));
+ assertThat(predicate.test(context, row(0L)), is(true));
+ assertThat(predicate.test(context, row(100L)), is(false));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val IS NULL", inputType);
+
+ assertThat(predicate.test(context, row(-100L)), is(false));
+ assertThat(predicate.test(context, row(0L)), is(false));
+ assertThat(predicate.test(context, row(100L)), is(false));
+ assertThat(predicate.test(context, row(null)), is(true));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val IS NOT NULL", inputType);
+
+ assertThat(predicate.test(context, row(-100L)), is(true));
+ assertThat(predicate.test(context, row(0L)), is(true));
+ assertThat(predicate.test(context, row(100L)), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val < 0.1", inputType);
+
+ assertThat(predicate.test(context, row(-100L)), is(true));
+ assertThat(predicate.test(context, row(0L)), is(true));
+ assertThat(predicate.test(context, row(100L)), is(false));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val > 0.1", inputType);
+
+ assertThat(predicate.test(context, row(-100L)), is(false));
+ assertThat(predicate.test(context, row(0L)), is(false));
+ assertThat(predicate.test(context, row(100L)), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+ }
+
+ @Test
+ @TestFor(ColumnType.FLOAT)
+ void floatTest() throws Exception {
+ EvaluationContext<Object[]> context = context();
+ StructNativeType inputType = singleColumnType(NativeTypes.FLOAT);
+
+ {
+ IgnitePredicate predicate = factory.predicate("val > 0", inputType);
+
+ assertThat(predicate.test(context, row(-100.5f)), is(false));
+ assertThat(predicate.test(context, row(0.0f)), is(false));
+ assertThat(predicate.test(context, row(100.5f)), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val <> 0", inputType);
+
+ assertThat(predicate.test(context, row(-100.5f)), is(true));
+ assertThat(predicate.test(context, row(0.0f)), is(false));
+ assertThat(predicate.test(context, row(100.5f)), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val <= 0", inputType);
+
+ assertThat(predicate.test(context, row(-100.5f)), is(true));
+ assertThat(predicate.test(context, row(0.0f)), is(true));
+ assertThat(predicate.test(context, row(100.5f)), is(false));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val IS NULL", inputType);
+
+ assertThat(predicate.test(context, row(-100.5f)), is(false));
+ assertThat(predicate.test(context, row(0.0f)), is(false));
+ assertThat(predicate.test(context, row(100.5f)), is(false));
+ assertThat(predicate.test(context, row(null)), is(true));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val IS NOT NULL", inputType);
+
+ assertThat(predicate.test(context, row(-100.5f)), is(true));
+ assertThat(predicate.test(context, row(0.0f)), is(true));
+ assertThat(predicate.test(context, row(100.5f)), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val < 0.1", inputType);
+
+ assertThat(predicate.test(context, row(-100.5f)), is(true));
+ assertThat(predicate.test(context, row(0.0f)), is(true));
+ assertThat(predicate.test(context, row(100.5f)), is(false));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val > 0.1", inputType);
+
+ assertThat(predicate.test(context, row(-100.5f)), is(false));
+ assertThat(predicate.test(context, row(0.0f)), is(false));
+ assertThat(predicate.test(context, row(100.5f)), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+ }
+
+ @Test
+ @TestFor(ColumnType.DOUBLE)
+ void doubleTest() throws Exception {
+ EvaluationContext<Object[]> context = context();
+ StructNativeType inputType = singleColumnType(NativeTypes.DOUBLE);
+
+ {
+ IgnitePredicate predicate = factory.predicate("val > 0", inputType);
+
+ assertThat(predicate.test(context, row(-100.5)), is(false));
+ assertThat(predicate.test(context, row(0.0)), is(false));
+ assertThat(predicate.test(context, row(100.5)), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val <> 0", inputType);
+
+ assertThat(predicate.test(context, row(-100.5)), is(true));
+ assertThat(predicate.test(context, row(0.0)), is(false));
+ assertThat(predicate.test(context, row(100.5)), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val <= 0", inputType);
+
+ assertThat(predicate.test(context, row(-100.5)), is(true));
+ assertThat(predicate.test(context, row(0.0)), is(true));
+ assertThat(predicate.test(context, row(100.5)), is(false));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val IS NULL", inputType);
+
+ assertThat(predicate.test(context, row(-100.5)), is(false));
+ assertThat(predicate.test(context, row(0.0)), is(false));
+ assertThat(predicate.test(context, row(100.5)), is(false));
+ assertThat(predicate.test(context, row(null)), is(true));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val IS NOT NULL", inputType);
+
+ assertThat(predicate.test(context, row(-100.5)), is(true));
+ assertThat(predicate.test(context, row(0.0)), is(true));
+ assertThat(predicate.test(context, row(100.5)), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val < 0.1", inputType);
+
+ assertThat(predicate.test(context, row(-100.5)), is(true));
+ assertThat(predicate.test(context, row(0.0)), is(true));
+ assertThat(predicate.test(context, row(100.5)), is(false));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val > 0.1", inputType);
+
+ assertThat(predicate.test(context, row(-100.5)), is(false));
+ assertThat(predicate.test(context, row(0.0)), is(false));
+ assertThat(predicate.test(context, row(100.5)), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+ }
+
+ @Test
+ @TestFor(ColumnType.DECIMAL)
+ void decimalTest() throws Exception {
+ EvaluationContext<Object[]> context = context();
+ StructNativeType inputType = singleColumnType(NativeTypes.decimalOf(19, 3));
+
+ {
+ IgnitePredicate predicate = factory.predicate("val > 0", inputType);
+
+ assertThat(predicate.test(context, row(new BigDecimal("-100.5"))), is(false));
+ assertThat(predicate.test(context, row(BigDecimal.ZERO)), is(false));
+ assertThat(predicate.test(context, row(new BigDecimal("100.5"))), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val <> 0", inputType);
+
+ assertThat(predicate.test(context, row(new BigDecimal("-100.5"))), is(true));
+ assertThat(predicate.test(context, row(BigDecimal.ZERO)), is(false));
+ assertThat(predicate.test(context, row(new BigDecimal("100.5"))), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val <= 0", inputType);
+
+ assertThat(predicate.test(context, row(new BigDecimal("-100.5"))), is(true));
+ assertThat(predicate.test(context, row(BigDecimal.ZERO)), is(true));
+ assertThat(predicate.test(context, row(new BigDecimal("100.5"))), is(false));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val IS NULL", inputType);
+
+ assertThat(predicate.test(context, row(new BigDecimal("-100.5"))), is(false));
+ assertThat(predicate.test(context, row(BigDecimal.ZERO)), is(false));
+ assertThat(predicate.test(context, row(new BigDecimal("100.5"))), is(false));
+ assertThat(predicate.test(context, row(null)), is(true));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val IS NOT NULL", inputType);
+
+ assertThat(predicate.test(context, row(new BigDecimal("-100.5"))), is(true));
+ assertThat(predicate.test(context, row(BigDecimal.ZERO)), is(true));
+ assertThat(predicate.test(context, row(new BigDecimal("100.5"))), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val < 0.1", inputType);
+
+ assertThat(predicate.test(context, row(new BigDecimal("-100.5"))), is(true));
+ assertThat(predicate.test(context, row(BigDecimal.ZERO)), is(true));
+ assertThat(predicate.test(context, row(new BigDecimal("100.5"))), is(false));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val > 0.1", inputType);
+
+ assertThat(predicate.test(context, row(new BigDecimal("-100.5"))), is(false));
+ assertThat(predicate.test(context, row(BigDecimal.ZERO)), is(false));
+ assertThat(predicate.test(context, row(new BigDecimal("100.5"))), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+ }
+
+ @Test
+ @TestFor(ColumnType.DATE)
+ void dateTest() throws Exception {
+ EvaluationContext<Object[]> context = context();
+ StructNativeType inputType = singleColumnType(NativeTypes.DATE);
+
+ LocalDate past = LocalDate.of(2020, 1, 1);
+ LocalDate present = LocalDate.of(2023, 6, 15);
+ LocalDate future = LocalDate.of(2025, 12, 31);
+
+ {
+ IgnitePredicate predicate = factory.predicate("val > DATE '2023-06-15'", inputType);
+
+ assertThat(predicate.test(context, row(past)), is(false));
+ assertThat(predicate.test(context, row(present)), is(false));
+ assertThat(predicate.test(context, row(future)), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val <> DATE '2023-06-15'", inputType);
+
+ assertThat(predicate.test(context, row(past)), is(true));
+ assertThat(predicate.test(context, row(present)), is(false));
+ assertThat(predicate.test(context, row(future)), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val <= DATE '2023-06-15'", inputType);
+
+ assertThat(predicate.test(context, row(past)), is(true));
+ assertThat(predicate.test(context, row(present)), is(true));
+ assertThat(predicate.test(context, row(future)), is(false));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val IS NULL", inputType);
+
+ assertThat(predicate.test(context, row(past)), is(false));
+ assertThat(predicate.test(context, row(present)), is(false));
+ assertThat(predicate.test(context, row(future)), is(false));
+ assertThat(predicate.test(context, row(null)), is(true));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val IS NOT NULL", inputType);
+
+ assertThat(predicate.test(context, row(past)), is(true));
+ assertThat(predicate.test(context, row(present)), is(true));
+ assertThat(predicate.test(context, row(future)), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+ }
+
+ @Test
+ @TestFor(ColumnType.TIME)
+ void timeTest() throws Exception {
+ EvaluationContext<Object[]> context = context();
+ StructNativeType inputType = singleColumnType(NativeTypes.time(0));
+
+ LocalTime early = LocalTime.of(8, 0, 0);
+ LocalTime noon = LocalTime.of(12, 0, 0);
+ LocalTime late = LocalTime.of(18, 0, 0);
+
+ {
+ IgnitePredicate predicate = factory.predicate("val > TIME '12:00:00'", inputType);
+
+ assertThat(predicate.test(context, row(early)), is(false));
+ assertThat(predicate.test(context, row(noon)), is(false));
+ assertThat(predicate.test(context, row(late)), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val <> TIME '12:00:00'", inputType);
+
+ assertThat(predicate.test(context, row(early)), is(true));
+ assertThat(predicate.test(context, row(noon)), is(false));
+ assertThat(predicate.test(context, row(late)), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val <= TIME '12:00:00'", inputType);
+
+ assertThat(predicate.test(context, row(early)), is(true));
+ assertThat(predicate.test(context, row(noon)), is(true));
+ assertThat(predicate.test(context, row(late)), is(false));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val IS NULL", inputType);
+
+ assertThat(predicate.test(context, row(early)), is(false));
+ assertThat(predicate.test(context, row(noon)), is(false));
+ assertThat(predicate.test(context, row(late)), is(false));
+ assertThat(predicate.test(context, row(null)), is(true));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val IS NOT NULL", inputType);
+
+ assertThat(predicate.test(context, row(early)), is(true));
+ assertThat(predicate.test(context, row(noon)), is(true));
+ assertThat(predicate.test(context, row(late)), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+ }
+
+ @Test
+ @TestFor(ColumnType.DATETIME)
+ void datetimeTest() throws Exception {
+ EvaluationContext<Object[]> context = context();
+ StructNativeType inputType = singleColumnType(NativeTypes.datetime(6));
+
+ LocalDateTime past = LocalDateTime.of(2020, 1, 1, 10, 0, 0);
+ LocalDateTime present = LocalDateTime.of(2023, 6, 15, 12, 0, 0);
+ LocalDateTime future = LocalDateTime.of(2025, 12, 31, 14, 0, 0);
+
+ {
+ IgnitePredicate predicate = factory.predicate("val > TIMESTAMP '2023-06-15 12:00:00'", inputType);
+
+ assertThat(predicate.test(context, row(past)), is(false));
+ assertThat(predicate.test(context, row(present)), is(false));
+ assertThat(predicate.test(context, row(future)), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val <> TIMESTAMP '2023-06-15 12:00:00'", inputType);
+
+ assertThat(predicate.test(context, row(past)), is(true));
+ assertThat(predicate.test(context, row(present)), is(false));
+ assertThat(predicate.test(context, row(future)), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val <= TIMESTAMP '2023-06-15 12:00:00'", inputType);
+
+ assertThat(predicate.test(context, row(past)), is(true));
+ assertThat(predicate.test(context, row(present)), is(true));
+ assertThat(predicate.test(context, row(future)), is(false));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val IS NULL", inputType);
+
+ assertThat(predicate.test(context, row(past)), is(false));
+ assertThat(predicate.test(context, row(present)), is(false));
+ assertThat(predicate.test(context, row(future)), is(false));
+ assertThat(predicate.test(context, row(null)), is(true));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val IS NOT NULL", inputType);
+
+ assertThat(predicate.test(context, row(past)), is(true));
+ assertThat(predicate.test(context, row(present)), is(true));
+ assertThat(predicate.test(context, row(future)), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+ }
+
+ @Test
+ @TestFor(ColumnType.TIMESTAMP)
+ void timestampTest() throws Exception {
+ StructNativeType inputType = singleColumnType(NativeTypes.timestamp(6));
+
+ Instant past = Instant.parse("2020-01-01T10:00:00Z");
+ Instant present = Instant.parse("2023-06-15T12:00:00Z");
+ Instant future = Instant.parse("2025-12-31T14:00:00Z");
+
+ EvaluationContext<Object[]> context = context(present);
+
+ {
+ IgnitePredicate predicate = factory.predicate("val > CURRENT_TIMESTAMP", inputType);
+
+ assertThat(predicate.test(context, row(past)), is(false));
+ assertThat(predicate.test(context, row(present)), is(false));
+ assertThat(predicate.test(context, row(future)), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val <> CURRENT_TIMESTAMP", inputType);
+
+ assertThat(predicate.test(context, row(past)), is(true));
+ assertThat(predicate.test(context, row(present)), is(false));
+ assertThat(predicate.test(context, row(future)), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val <= CURRENT_TIMESTAMP", inputType);
+
+ assertThat(predicate.test(context, row(past)), is(true));
+ assertThat(predicate.test(context, row(present)), is(true));
+ assertThat(predicate.test(context, row(future)), is(false));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val IS NULL", inputType);
+
+ assertThat(predicate.test(context, row(past)), is(false));
+ assertThat(predicate.test(context, row(present)), is(false));
+ assertThat(predicate.test(context, row(future)), is(false));
+ assertThat(predicate.test(context, row(null)), is(true));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val IS NOT NULL", inputType);
+
+ assertThat(predicate.test(context, row(past)), is(true));
+ assertThat(predicate.test(context, row(present)), is(true));
+ assertThat(predicate.test(context, row(future)), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+ }
+
+ @Test
+ @TestFor(ColumnType.UUID)
+ void uuidTest() throws Exception {
+ EvaluationContext<Object[]> context = context();
+ StructNativeType inputType = singleColumnType(NativeTypes.UUID);
+
+ UUID uuid1 = UUID.fromString("00000000-0000-0000-0000-000000000001");
+ UUID uuid2 = UUID.fromString("12345678-1234-1234-1234-123456789012");
+ UUID uuid3 = UUID.fromString("ffffffff-ffff-ffff-ffff-ffffffffffff");
+
+ {
+ IgnitePredicate predicate = factory.predicate("val > uuid '12345678-1234-1234-1234-123456789012'", inputType);
+
+ assertThat(predicate.test(context, row(uuid1)), is(false));
+ assertThat(predicate.test(context, row(uuid2)), is(false));
+ assertThat(predicate.test(context, row(uuid3)), is(false));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val <> uuid '12345678-1234-1234-1234-123456789012'", inputType);
+
+ assertThat(predicate.test(context, row(uuid1)), is(true));
+ assertThat(predicate.test(context, row(uuid2)), is(false));
+ assertThat(predicate.test(context, row(uuid3)), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val <= uuid '12345678-1234-1234-1234-123456789012'", inputType);
+
+ assertThat(predicate.test(context, row(uuid1)), is(true));
+ assertThat(predicate.test(context, row(uuid2)), is(true));
+ assertThat(predicate.test(context, row(uuid3)), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val IS NULL", inputType);
+
+ assertThat(predicate.test(context, row(uuid1)), is(false));
+ assertThat(predicate.test(context, row(uuid2)), is(false));
+ assertThat(predicate.test(context, row(uuid3)), is(false));
+ assertThat(predicate.test(context, row(null)), is(true));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val IS NOT NULL", inputType);
+
+ assertThat(predicate.test(context, row(uuid1)), is(true));
+ assertThat(predicate.test(context, row(uuid2)), is(true));
+ assertThat(predicate.test(context, row(uuid3)), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+ }
+
+ @Test
+ @TestFor(ColumnType.NULL)
+ void nullTest() throws Exception {
+ EvaluationContext<Object[]> context = context();
+ StructNativeType inputType = singleColumnType(NativeTypes.NULL);
+
+ {
+ IgnitePredicate predicate = factory.predicate("CAST(val AS BOOLEAN)", inputType);
+
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val > 0", inputType);
+
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val <> 0", inputType);
+
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val IS NULL", inputType);
+
+ assertThat(predicate.test(context, row(null)), is(true));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val IS NOT NULL", inputType);
+
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+ }
+
+ @Test
+ @TestFor(ColumnType.STRING)
+ void stringTest() throws Exception {
+ EvaluationContext<Object[]> context = context();
+ StructNativeType inputType = singleColumnType(NativeTypes.stringOf(100));
+
+ {
+ IgnitePredicate predicate = factory.predicate("val > 'medium'", inputType);
+
+ assertThat(predicate.test(context, row("aaa")), is(false));
+ assertThat(predicate.test(context, row("medium")), is(false));
+ assertThat(predicate.test(context, row("zzz")), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val <> 'medium'", inputType);
+
+ assertThat(predicate.test(context, row("aaa")), is(true));
+ assertThat(predicate.test(context, row("medium")), is(false));
+ assertThat(predicate.test(context, row("zzz")), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val <= 'medium'", inputType);
+
+ assertThat(predicate.test(context, row("aaa")), is(true));
+ assertThat(predicate.test(context, row("medium")), is(true));
+ assertThat(predicate.test(context, row("zzz")), is(false));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val IS NULL", inputType);
+
+ assertThat(predicate.test(context, row("aaa")), is(false));
+ assertThat(predicate.test(context, row("medium")), is(false));
+ assertThat(predicate.test(context, row("zzz")), is(false));
+ assertThat(predicate.test(context, row(null)), is(true));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val IS NOT NULL", inputType);
+
+ assertThat(predicate.test(context, row("aaa")), is(true));
+ assertThat(predicate.test(context, row("medium")), is(true));
+ assertThat(predicate.test(context, row("zzz")), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val LIKE '%ed%'", inputType);
+
+ assertThat(predicate.test(context, row("aaa")), is(false));
+ assertThat(predicate.test(context, row("medium")), is(true));
+ assertThat(predicate.test(context, row("zzz")), is(false));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+ }
+
+ @Test
+ @TestFor(ColumnType.BYTE_ARRAY)
+ void byteArrayTest() throws Exception {
+ EvaluationContext<Object[]> context = context();
+ StructNativeType inputType = singleColumnType(NativeTypes.blobOf(100));
+
+ byte[] small = {1, 2, 3};
+ byte[] medium = {10, 20, 30};
+ byte[] large = {100, 127, -128};
+
+ {
+ IgnitePredicate predicate = factory.predicate("val > x'0a141e'", inputType);
+
+ assertThat(predicate.test(context, row(small)), is(false));
+ assertThat(predicate.test(context, row(medium)), is(false));
+ assertThat(predicate.test(context, row(large)), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val <> x'0a141e'", inputType);
+
+ assertThat(predicate.test(context, row(small)), is(true));
+ assertThat(predicate.test(context, row(medium)), is(false));
+ assertThat(predicate.test(context, row(large)), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val <= x'0a141e'", inputType);
+
+ assertThat(predicate.test(context, row(small)), is(true));
+ assertThat(predicate.test(context, row(medium)), is(true));
+ assertThat(predicate.test(context, row(large)), is(false));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val IS NULL", inputType);
+
+ assertThat(predicate.test(context, row(small)), is(false));
+ assertThat(predicate.test(context, row(medium)), is(false));
+ assertThat(predicate.test(context, row(large)), is(false));
+ assertThat(predicate.test(context, row(null)), is(true));
+ }
+
+ {
+ IgnitePredicate predicate = factory.predicate("val IS NOT NULL", inputType);
+
+ assertThat(predicate.test(context, row(small)), is(true));
+ assertThat(predicate.test(context, row(medium)), is(true));
+ assertThat(predicate.test(context, row(large)), is(true));
+ assertThat(predicate.test(context, row(null)), is(false));
+ }
+ }
+
+ @ParameterizedTest
+ @CsvSource(delimiterString = ";", value = {
+ "val bar baz; Encountered \"bar\" at line 1, column 5",
+ "bar#baz; Lexical error at line 1, column 4. Encountered: \"#\" (35)"
+ })
+ void parsingErrors(String expression, String expectedMessage) {
+ assertThrows(
+ ExpressionParsingException.class,
+ () -> factory.predicate(expression, singleColumnType(NativeTypes.INT32)),
+ expectedMessage
+ );
+ }
+
+ @Test
+ void validationErrors() {
+ assertThrows(
+ ExpressionValidationException.class,
+ () -> factory.predicate("val", singleColumnType(NativeTypes.INT32)),
+ "Expected BOOLEAN expression but INTEGER was provided"
+ );
+
+ // Reference to table is a bit confusing. We'll improve that later.
+ assertThrows(
+ ExpressionValidationException.class,
+ () -> factory.predicate("non_existing_col > 5", singleColumnType(NativeTypes.INT32)),
+ "From line 1, column 1 to line 1, column 16: Column 'NON_EXISTING_COL' not found in any table"
+ );
+
+ // Same here.
+ assertThrows(
+ ExpressionValidationException.class,
+ () -> factory.predicate("non_existing_row_alias.val > 5", singleColumnType(NativeTypes.INT32)),
+ "From line 1, column 1 to line 1, column 22: Table 'NON_EXISTING_ROW_ALIAS' not found"
+ );
+
+ assertThrows(
+ ExpressionValidationException.class,
+ () -> factory.predicate("val = '12'", singleColumnType(NativeTypes.INT32)),
+ "From line 1, column 1 to line 1, column 10: Cannot apply '=' to arguments of type"
+ + " '<INTEGER> = <CHAR(2)>'. Supported form(s): '<EQUIVALENT_TYPE> = <EQUIVALENT_TYPE>'"
+ );
+
+ assertThrows(
+ ExpressionValidationException.class,
+ () -> factory.predicate("EXISTS (SELECT 1 FROM test)", singleColumnType(NativeTypes.INT32)),
+ "From line 1, column 8 to line 1, column 27: Subqueries are not supported in given context."
+ );
+
+ assertThrows(
+ ExpressionValidationException.class,
+ () -> factory.predicate("val > 10 AND val = (SELECT id FROM test) AND val < 5", singleColumnType(NativeTypes.INT32)),
+ "From line 1, column 20 to line 1, column 40: Subqueries are not supported in given context."
+ );
+
+ assertThrows(
+ ExpressionValidationException.class,
+ () -> factory.predicate("5 < AVG(val)", singleColumnType(NativeTypes.INT32)),
+ "From line 1, column 5 to line 1, column 12: Aggregates are not supported in given context."
+ );
+
+ assertThrows(
+ ExpressionValidationException.class,
+ () -> factory.predicate("CURRENT_USER", singleColumnType(NativeTypes.INT32)),
+ "From line 1, column 1 to line 1, column 12: CURRENT_USER is not supported in given context."
+ );
+
+ assertThrows(
+ ExpressionValidationException.class,
+ () -> factory.predicate("LOCALTIME", singleColumnType(NativeTypes.INT32)),
+ "From line 1, column 1 to line 1, column 9: LOCALTIME is not supported in given context."
+ );
+
+ assertThrows(
+ ExpressionValidationException.class,
+ () -> factory.predicate("LOCALTIMESTAMP", singleColumnType(NativeTypes.INT32)),
+ "From line 1, column 1 to line 1, column 14: LOCALTIMESTAMP is not supported in given context."
+ );
+
+ assertThrows(
+ ExpressionValidationException.class,
+ () -> factory.predicate("CURRENT_DATE", singleColumnType(NativeTypes.INT32)),
+ "From line 1, column 1 to line 1, column 12: CURRENT_DATE is not supported in given context."
+ );
+ }
+
+ @Test
+ void runtimeErrors() {
+ EvaluationContext<Object[]> context = context();
+
+ assertThrows(
+ ExpressionEvaluationException.class,
+ () -> factory.predicate("val / 0 > 10", singleColumnType(NativeTypes.INT32)).test(context, row(1)),
+ "Division by zero"
+ );
+
+ assertThrows(
+ ExpressionEvaluationException.class,
+ () -> factory.predicate("CAST('abaracadabara' AS BOOLEAN)", singleColumnType(NativeTypes.INT32)).test(context, row(1)),
+ "Invalid character for cast: abaracadabara"
+ );
+ }
+
+ private EvaluationContext<Object[]> context() {
+ return factory.<Object[]>contextBuilder()
+ .rowAccessor(ObjectArrayAccessor.INSTANCE)
+ .build();
+ }
+
+ private EvaluationContext<Object[]> context(Instant currentTime) {
+ return factory.<Object[]>contextBuilder()
+ .rowAccessor(ObjectArrayAccessor.INSTANCE)
+ .timeProvider(currentTime::toEpochMilli)
+ .build();
+ }
+
+ private static StructNativeType singleColumnType(NativeType type) {
+ return NativeTypes.structBuilder()
+ .addField("VAL", type, true)
+ .build();
+ }
+
+ private static @Nullable Object[] row(@Nullable Object value) {
+ return new Object[] {value};
+ }
+
+ static class ObjectArrayAccessor implements RowAccessor<Object[]> {
+ private static final ObjectArrayAccessor INSTANCE = new ObjectArrayAccessor();
+
+ @Override
+ public @Nullable Object get(int field, Object[] row) {
+ return row[field];
+ }
+
+ @Override
+ public int columnsCount(Object[] row) {
+ return row.length;
+ }
+
+ @Override
+ public boolean isNull(int field, Object[] row) {
+ return row[field] == null;
+ }
+ }
+}
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/framework/ArrayRowHandler.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/framework/ArrayRowHandler.java
index f2c77bbb80d..632824c7a02 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/framework/ArrayRowHandler.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/framework/ArrayRowHandler.java
@@ -30,9 +30,9 @@ import org.apache.ignite.internal.binarytuple.BinaryTupleBuilder;
import org.apache.ignite.internal.lang.InternalTuple;
import org.apache.ignite.internal.schema.BinaryTuple;
import org.apache.ignite.internal.schema.InvalidTypeException;
-import org.apache.ignite.internal.sql.engine.exec.RowFactory;
-import org.apache.ignite.internal.sql.engine.exec.RowFactory.RowBuilder;
-import org.apache.ignite.internal.sql.engine.exec.RowFactoryFactory;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory.RowBuilder;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.RowHandler;
import org.apache.ignite.internal.sql.engine.util.TypeUtils;
import org.apache.ignite.internal.type.DecimalNativeType;
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/framework/TestBuilders.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/framework/TestBuilders.java
index b05f5ecc955..12c9dfc91a5 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/framework/TestBuilders.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/framework/TestBuilders.java
@@ -96,6 +96,7 @@ import org.apache.ignite.internal.partitiondistribution.Assignment;
import org.apache.ignite.internal.partitiondistribution.TokenizedAssignments;
import org.apache.ignite.internal.partitiondistribution.TokenizedAssignmentsImpl;
import org.apache.ignite.internal.sql.SqlCommon;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory;
import org.apache.ignite.internal.sql.engine.api.kill.OperationKillHandler;
import org.apache.ignite.internal.sql.engine.exec.ExecutableTable;
import org.apache.ignite.internal.sql.engine.exec.ExecutableTableRegistry;
@@ -103,7 +104,6 @@ import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
import org.apache.ignite.internal.sql.engine.exec.ExecutionId;
import org.apache.ignite.internal.sql.engine.exec.PartitionWithConsistencyToken;
import org.apache.ignite.internal.sql.engine.exec.QueryTaskExecutor;
-import org.apache.ignite.internal.sql.engine.exec.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.ScannableTable;
import org.apache.ignite.internal.sql.engine.exec.TxAttributes;
import org.apache.ignite.internal.sql.engine.exec.UpdatableTable;
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/framework/TestClusterTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/framework/TestClusterTest.java
index 5fcd939b518..d743fe95c5b 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/framework/TestClusterTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/framework/TestClusterTest.java
@@ -41,10 +41,10 @@ import java.util.stream.IntStream;
import org.apache.ignite.internal.catalog.commands.CatalogUtils;
import org.apache.ignite.internal.hlc.HybridClock;
import org.apache.ignite.internal.sql.engine.InternalSqlRow;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.AsyncDataCursor;
import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
import org.apache.ignite.internal.sql.engine.exec.PartitionWithConsistencyToken;
-import org.apache.ignite.internal.sql.engine.exec.RowFactory;
import org.apache.ignite.internal.sql.engine.exec.ScannableTable;
import org.apache.ignite.internal.sql.engine.exec.exp.RangeCondition;
import org.apache.ignite.internal.sql.engine.prepare.KeyValueGetPlan;
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/framework/TestNode.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/framework/TestNode.java
index e2a5494da39..d2316ad0d58 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/framework/TestNode.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/framework/TestNode.java
@@ -53,6 +53,7 @@ import org.apache.ignite.internal.sql.engine.InternalSqlRow;
import org.apache.ignite.internal.sql.engine.QueryCancel;
import org.apache.ignite.internal.sql.engine.SqlOperationContext;
import org.apache.ignite.internal.sql.engine.SqlProperties;
+import org.apache.ignite.internal.sql.engine.api.expressions.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.api.kill.OperationKillHandler;
import org.apache.ignite.internal.sql.engine.exec.ExchangeService;
import org.apache.ignite.internal.sql.engine.exec.ExchangeServiceImpl;
@@ -66,7 +67,6 @@ import org.apache.ignite.internal.sql.engine.exec.MailboxRegistry;
import org.apache.ignite.internal.sql.engine.exec.MailboxRegistryImpl;
import org.apache.ignite.internal.sql.engine.exec.QueryTaskExecutor;
import org.apache.ignite.internal.sql.engine.exec.QueryTaskExecutorImpl;
-import org.apache.ignite.internal.sql.engine.exec.RowFactoryFactory;
import org.apache.ignite.internal.sql.engine.exec.RowHandler;
import org.apache.ignite.internal.sql.engine.exec.ddl.DdlCommandHandler;
import org.apache.ignite.internal.sql.engine.exec.exp.SqlExpressionFactoryImpl;