This is an automated email from the ASF dual-hosted git repository.

ppa pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


The following commit(s) were added to refs/heads/main by this push:
     new d05574d75e IGNITE-20356 Sql. Remove "set" method from RowHandler 
(#2800)
d05574d75e is described below

commit d05574d75ecf9ad3f3f48a95ec30f03148993522
Author: Max Zhuravkov <[email protected]>
AuthorDate: Fri Nov 10 12:42:57 2023 +0200

    IGNITE-20356 Sql. Remove "set" method from RowHandler (#2800)
---
 .../internal/sql/engine/exec/ExecutionContext.java |   6 +-
 .../internal/sql/engine/exec/RowHandler.java       |  59 ++++++--
 .../internal/sql/engine/exec/SqlRowHandler.java    |  88 +++++++++---
 .../internal/sql/engine/exec/exp/BiScalar.java     |   3 +-
 .../sql/engine/exec/exp/ExpressionFactoryImpl.java | 114 +++++++++-------
 .../internal/sql/engine/exec/exp/SingleScalar.java |   3 +-
 .../internal/sql/engine/util/IgniteMethod.java     |  13 +-
 .../ignite/internal/sql/engine/util/TypeUtils.java |  31 ++---
 .../engine/exec/exp/ExpressionFactoryImplTest.java | 134 +++++++++++++++++++
 .../sql/engine/exec/rel/AbstractExecutionTest.java |   7 +
 .../sql/engine/exec/row/SqlRowHandlerTest.java     | 148 ++++++++++++++++++---
 .../sql/engine/framework/ArrayRowHandler.java      |  80 +++++++++--
 .../internal/sql/engine/util/TypeUtilsTest.java    |   3 -
 13 files changed, 552 insertions(+), 137 deletions(-)

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 6f883d516d..8b3a2a84dc 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
@@ -239,10 +239,12 @@ public class ExecutionContext<RowT> implements 
DataContext {
         }
 
         if (name.startsWith("?")) {
-            return TypeUtils.toInternal(params.get(name));
+            Object val = params.get(name);
+            return val != null ? TypeUtils.toInternal(val, val.getClass()) : 
null;
+        } else {
+            return params.get(name);
         }
 
-        return params.get(name);
     }
 
     /** Gets dynamic parameters by name. */
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 8b35056635..46c5f52853 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
@@ -23,7 +23,7 @@ import 
org.apache.ignite.internal.sql.engine.exec.row.RowSchema;
 import org.jetbrains.annotations.Nullable;
 
 /**
- * Universal accessor and mutator for rows. It also has factory methods.
+ * Universal accessor for rows. It also has factory methods.
  */
 public interface RowHandler<RowT> {
     /**
@@ -34,14 +34,6 @@ public interface RowHandler<RowT> {
      */
     @Nullable Object get(int field, RowT row);
 
-    /** Set incoming row field.
-     *
-     * @param field Field position to be processed.
-     * @param row Row which field need to be changed.
-     * @param val Value which should be set.
-     */
-    void set(int field, RowT row, @Nullable Object val);
-
     /** Concatenate two rows. */
     RowT concat(RowT left, RowT right);
 
@@ -85,6 +77,9 @@ public interface RowHandler<RowT> {
         /** Return row accessor and mutator implementation. */
         RowHandler<RowT> handler();
 
+        /** Creates a {@link RowBuilder row builder}. */
+        RowBuilder<RowT> rowBuilder();
+
         /** Create empty row. */
         RowT create();
 
@@ -104,4 +99,50 @@ public interface RowHandler<RowT> {
          */
         RowT create(InternalTuple tuple);
     }
+
+    /**
+     * A builder to create rows. It uses the schema provided by an instance of 
row factory that created it.
+     *
+     * <pre>
+     *     // Create a row builder.
+     *     var rowBuilder = rowFactory.rowBuilder();
+     *     ...
+     *     // Call build() after all fields have been set.
+     *     var row1 = rowBuilder.build();
+     *     // Call reset() to cleanup builder's state.
+     *     rowBuilder.reset();
+     * </pre>
+     */
+    interface RowBuilder<RowT> {
+
+        /**
+         * Adds a field to the current row.
+         *
+         * @param value Field value.
+         * @return this.
+         */
+        RowBuilder<RowT> addField(@Nullable Object value);
+
+        /** Creates a new row from a previously added fields. */
+        RowT build();
+
+        /**
+         * Resets the state of this builder.
+         */
+        void reset();
+
+        /**
+         * Creates a new row and resets the state of this builder. This is a 
shorthand for:
+         * <pre>
+         *     Row row = builder.build();
+         *     builder.reset();
+         *     return row;
+         * </pre>
+         */
+        default RowT buildAndReset() {
+            RowT row = build();
+            reset();
+            return row;
+        }
+    }
 }
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 5ac300e8c1..4562c6720a 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
@@ -64,6 +64,8 @@ import org.jetbrains.annotations.Nullable;
 public class SqlRowHandler implements RowHandler<RowWrapper> {
     public static final RowHandler<RowWrapper> INSTANCE = new SqlRowHandler();
 
+    private static final ObjectsArrayRowWrapper EMPTY_ROW = new 
ObjectsArrayRowWrapper(RowSchema.builder().build(), new Object[0]);
+
     private SqlRowHandler() {
     }
 
@@ -73,12 +75,6 @@ public class SqlRowHandler implements RowHandler<RowWrapper> 
{
         return row.get(field);
     }
 
-    /** {@inheritDoc} */
-    @Override
-    public void set(int field, RowWrapper row, @Nullable Object val) {
-        row.set(field, val);
-    }
-
     /** {@inheritDoc} */
     @Override
     public RowWrapper concat(RowWrapper left, RowWrapper right) {
@@ -152,6 +148,11 @@ public class SqlRowHandler implements 
RowHandler<RowWrapper> {
                 return SqlRowHandler.this;
             }
 
+            @Override
+            public RowBuilder<RowWrapper> rowBuilder() {
+                return new RowBuilderImpl(rowSchema);
+            }
+
             /** {@inheritDoc} */
             @Override
             public RowWrapper create() {
@@ -186,8 +187,6 @@ public class SqlRowHandler implements 
RowHandler<RowWrapper> {
 
         abstract @Nullable Object get(int field);
 
-        abstract void set(int field, @Nullable Object value);
-
         abstract BinaryTuple toBinaryTuple();
     }
 
@@ -213,11 +212,6 @@ public class SqlRowHandler implements 
RowHandler<RowWrapper> {
             return row[field];
         }
 
-        @Override
-        void set(int field, @Nullable Object value) {
-            row[field] = value;
-        }
-
         @Override
         BinaryTuple toBinaryTuple() {
             int estimatedSize = 0;
@@ -363,8 +357,6 @@ public class SqlRowHandler implements 
RowHandler<RowWrapper> {
 
     /**
      * Wrapper over an {@link BinaryTuple}.
-     *
-     * <p>Since {@link BinaryTuple binary tuple} is immutable this wrapper 
doesn't support {@link #set(int, Object)} operation.
      */
     private static class BinaryTupleRowWrapper extends RowWrapper {
         private final RowSchema rowSchema;
@@ -397,12 +389,6 @@ public class SqlRowHandler implements 
RowHandler<RowWrapper> {
             return TypeUtils.toInternal(value, 
Commons.nativeTypeToClass(nativeType));
         }
 
-        @Override
-        void set(int field, @Nullable Object value) {
-            // TODO https://issues.apache.org/jira/browse/IGNITE-20356
-            throw new UnsupportedOperationException();
-        }
-
         @Override
         BinaryTuple toBinaryTuple() {
             if (tuple instanceof BinaryTuple) {
@@ -440,4 +426,64 @@ public class SqlRowHandler implements 
RowHandler<RowWrapper> {
             }
         }
     }
+
+    private static class RowBuilderImpl implements RowBuilder<RowWrapper> {
+
+        private final int schemaLen;
+
+        private final RowSchema rowSchema;
+
+        Object[] data;
+
+        int fieldIdx;
+
+        RowBuilderImpl(RowSchema rowSchema) {
+            this.rowSchema = rowSchema;
+            this.schemaLen = rowSchema.fields().size();
+            fieldIdx = 0;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public RowBuilder<RowWrapper> addField(Object value) {
+            if (fieldIdx == 0 && data == null) {
+                data = new Object[schemaLen];
+            }
+
+            checkIndex();
+
+            data[fieldIdx++] = value;
+            return this;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public RowWrapper build() {
+            checkState();
+
+            return rowSchema.fields().isEmpty() ? EMPTY_ROW : new 
ObjectsArrayRowWrapper(rowSchema, data);
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void reset() {
+            data = null;
+            fieldIdx = 0;
+        }
+
+        private void checkState() {
+            if (schemaLen != 0 && data == null) {
+                throw new IllegalStateException("Row has not been 
initialised");
+            }
+            if (fieldIdx != schemaLen) {
+                throw new IllegalStateException(format("Row has not been fully 
built. Index: {}, fields: {}", fieldIdx, schemaLen));
+            }
+        }
+
+        private void checkIndex() {
+            if (fieldIdx >= schemaLen) {
+                throw new IllegalStateException(format("Field index is out of 
bounds. Index: {}, fields: {}", fieldIdx, schemaLen));
+            }
+        }
+    }
 }
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/BiScalar.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/BiScalar.java
index 7799efddf4..5c61a7fc9a 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/BiScalar.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/BiScalar.java
@@ -18,6 +18,7 @@
 package org.apache.ignite.internal.sql.engine.exec.exp;
 
 import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
+import org.apache.ignite.internal.sql.engine.exec.RowHandler.RowBuilder;
 
 /**
  * Binary scalar used for two inputs and single output.
@@ -25,5 +26,5 @@ import 
org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
 @FunctionalInterface
 public interface BiScalar extends Scalar {
     /** Two inputs and single output. */
-    void execute(ExecutionContext ctx, Object in1, Object in2, Object out);
+    void execute(ExecutionContext ctx, Object in1, Object in2, RowBuilder out);
 }
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/ExpressionFactoryImpl.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/ExpressionFactoryImpl.java
index 8a0560ea1b..99481908f0 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/ExpressionFactoryImpl.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/ExpressionFactoryImpl.java
@@ -66,6 +66,7 @@ import org.apache.calcite.sql.validate.SqlConformance;
 import org.apache.calcite.util.ImmutableBitSet;
 import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
 import org.apache.ignite.internal.sql.engine.exec.RowHandler;
+import org.apache.ignite.internal.sql.engine.exec.RowHandler.RowBuilder;
 import org.apache.ignite.internal.sql.engine.exec.RowHandler.RowFactory;
 import 
org.apache.ignite.internal.sql.engine.exec.exp.RexToLixTranslator.InputGetter;
 import org.apache.ignite.internal.sql.engine.exec.exp.agg.AccumulatorWrapper;
@@ -291,17 +292,24 @@ public class ExpressionFactoryImpl<RowT> implements 
ExpressionFactory<RowT> {
         }
 
         List<RowT> rows = new ArrayList<>(values.size() / columns);
-        RowT currRow = null;
-        for (int i = 0; i < values.size(); i++) {
-            int field = i % columns;
 
-            if (field == 0) {
-                rows.add(currRow = factory.create());
-            }
+        if (!values.isEmpty()) {
+            RowBuilder<RowT> rowBuilder = factory.rowBuilder();
+
+            for (int i = 0; i < values.size(); i++) {
+                int field = i % columns;
+
+                if (field == 0 && i > 0) {
+                    rows.add(rowBuilder.buildAndReset());
+                }
 
-            RexLiteral literal = values.get(i);
+                RexLiteral literal = values.get(i);
+                Object val = literal.getValueAs(types.get(field));
 
-            handler.set(field, currRow, literal.getValueAs(types.get(field)));
+                rowBuilder.addField(val);
+            }
+
+            rows.add(rowBuilder.buildAndReset());
         }
 
         return rows;
@@ -537,7 +545,7 @@ public class ExpressionFactoryImpl<RowT> implements 
ExpressionFactory<RowT> {
                 Expressions.parameter(Object.class, "in2");
 
         ParameterExpression out =
-                Expressions.parameter(Object.class, "out");
+                Expressions.parameter(RowBuilder.class, "out");
 
         builder.add(
                 Expressions.declare(Modifier.FINAL, DataContext.ROOT, 
Expressions.convert_(ctx, DataContext.class)));
@@ -556,12 +564,8 @@ public class ExpressionFactoryImpl<RowT> implements 
ExpressionFactory<RowT> {
 
         for (int i = 0; i < projects.size(); i++) {
             Expression val = projects.get(i);
-
-            builder.add(
-                        Expressions.statement(
-                                Expressions.call(hnd,
-                                        IgniteMethod.ROW_HANDLER_SET.method(),
-                                        Expressions.constant(i), out, val)));
+            Expression addRowField = Expressions.call(out, 
IgniteMethod.ROW_BUILDER_ADD_FIELD.method(), val);
+            builder.add(Expressions.statement(addRowField));
         }
 
         ParameterExpression ex = Expressions.parameter(0, Exception.class, 
"e");
@@ -637,10 +641,10 @@ public class ExpressionFactoryImpl<RowT> implements 
ExpressionFactory<RowT> {
     private abstract class AbstractScalarPredicate<T extends Scalar> {
         protected final T scalar;
 
-        protected final RowT out;
-
         protected final RowHandler<RowT> hnd;
 
+        protected final RowBuilder<RowT> rowBuilder;
+
         /**
          * Constructor.
          *
@@ -654,7 +658,7 @@ public class ExpressionFactoryImpl<RowT> implements 
ExpressionFactory<RowT> {
             RelDataType nullableType = 
TYPE_FACTORY.createTypeWithNullability(booleanType, true);
             RowSchema schema = 
TypeUtils.rowSchemaFromRelTypes(List.of(nullableType));
 
-            out = hnd.factory(schema).create();
+            rowBuilder = hnd.factory(schema).rowBuilder();
         }
     }
 
@@ -669,9 +673,10 @@ public class ExpressionFactoryImpl<RowT> implements 
ExpressionFactory<RowT> {
         /** {@inheritDoc} */
         @Override
         public boolean test(RowT r) {
-            scalar.execute(ctx, r, out);
+            scalar.execute(ctx, r, rowBuilder);
+            RowT res = rowBuilder.buildAndReset();
 
-            return Boolean.TRUE.equals(hnd.get(0, out));
+            return Boolean.TRUE.equals(hnd.get(0, res));
         }
     }
 
@@ -686,15 +691,17 @@ public class ExpressionFactoryImpl<RowT> implements 
ExpressionFactory<RowT> {
         /** {@inheritDoc} */
         @Override
         public boolean test(RowT r1, RowT r2) {
-            scalar.execute(ctx, r1, r2, out);
-            return Boolean.TRUE.equals(hnd.get(0, out));
+            scalar.execute(ctx, r1, r2, rowBuilder);
+            RowT res = rowBuilder.buildAndReset();
+
+            return Boolean.TRUE.equals(hnd.get(0, res));
         }
     }
 
     private class ProjectImpl implements Function<RowT, RowT> {
         private final SingleScalar scalar;
 
-        private final RowFactory<RowT> factory;
+        private final RowBuilder<RowT> rowBuilder;
 
         /**
          * Constructor.
@@ -704,60 +711,58 @@ public class ExpressionFactoryImpl<RowT> implements 
ExpressionFactory<RowT> {
          */
         private ProjectImpl(SingleScalar scalar, RowFactory<RowT> factory) {
             this.scalar = scalar;
-            this.factory = factory;
+            this.rowBuilder = factory.rowBuilder();
         }
 
         /** {@inheritDoc} */
         @Override
         public RowT apply(RowT r) {
-            RowT res = factory.create();
-            scalar.execute(ctx, r, res);
+            scalar.execute(ctx, r, rowBuilder);
 
-            return res;
+            return rowBuilder.buildAndReset();
         }
     }
 
     private class ValuesImpl implements Supplier<RowT> {
         private final SingleScalar scalar;
 
-        private final RowFactory<RowT> factory;
+        private final RowBuilder<RowT> rowBuilder;
 
         /**
          * Constructor.
          */
         private ValuesImpl(SingleScalar scalar, RowFactory<RowT> factory) {
             this.scalar = scalar;
-            this.factory = factory;
+            this.rowBuilder = factory.rowBuilder();
         }
 
         /** {@inheritDoc} */
         @Override
         public RowT get() {
-            RowT res = factory.create();
-            scalar.execute(ctx, null, res);
+            scalar.execute(ctx, null, rowBuilder);
 
-            return res;
+            return rowBuilder.buildAndReset();
         }
     }
 
     private class ValueImpl<T> implements Supplier<T> {
         private final SingleScalar scalar;
 
-        private final RowFactory<RowT> factory;
+        private final RowBuilder<RowT> rowBuilder;
 
         /**
          * Constructor.
          */
         private ValueImpl(SingleScalar scalar, RowFactory<RowT> factory) {
             this.scalar = scalar;
-            this.factory = factory;
+            this.rowBuilder = factory.rowBuilder();
         }
 
         /** {@inheritDoc} */
         @Override
         public T get() {
-            RowT res = factory.create();
-            scalar.execute(ctx, null, res);
+            scalar.execute(ctx, null, rowBuilder);
+            RowT res = rowBuilder.buildAndReset();
 
             return (T) ctx.rowHandler().get(0, res);
         }
@@ -783,10 +788,10 @@ public class ExpressionFactoryImpl<RowT> implements 
ExpressionFactory<RowT> {
         private @Nullable RowT upperRow;
 
         /** Lower bound row factory. */
-        private final RowFactory<RowT> lowerFactory;
+        private final RowBuilder<RowT> lowerRowBuilder;
 
         /** Upper bound row factory. */
-        private final RowFactory<RowT> upperFactory;
+        private final RowBuilder<RowT> upperRowBuilder;
 
         /** Cached skip range flag. */
         private @Nullable Boolean skip;
@@ -806,20 +811,20 @@ public class ExpressionFactoryImpl<RowT> implements 
ExpressionFactory<RowT> {
             this.lowerInclude = lowerInclude;
             this.upperInclude = upperInclude;
 
-            this.lowerFactory = lowerFactory;
-            this.upperFactory = upperFactory;
+            this.lowerRowBuilder = lowerFactory.rowBuilder();
+            this.upperRowBuilder = upperFactory.rowBuilder();
         }
 
         /** {@inheritDoc} */
         @Override
         public @Nullable RowT lower() {
-            return lowerRow != null ? lowerRow : (lowerRow = 
getRow(lowerBound, lowerFactory));
+            return lowerRow != null ? lowerRow : (lowerRow = 
getRow(lowerBound, lowerRowBuilder));
         }
 
         /** {@inheritDoc} */
         @Override
         public @Nullable RowT upper() {
-            return upperRow != null ? upperRow : (upperRow = 
getRow(upperBound, upperFactory));
+            return upperRow != null ? upperRow : (upperRow = 
getRow(upperBound, upperRowBuilder));
         }
 
         /** {@inheritDoc} */
@@ -835,14 +840,16 @@ public class ExpressionFactoryImpl<RowT> implements 
ExpressionFactory<RowT> {
         }
 
         /** Compute row. */
-        private RowT getRow(SingleScalar scalar, RowFactory<RowT> factory) {
-            RowT res = factory.create();
-            scalar.execute(ctx, null, res);
+        private RowT getRow(SingleScalar scalar, RowBuilder<RowT> rowBuilder) {
+            scalar.execute(ctx, null, rowBuilder);
+            RowT res = rowBuilder.buildAndReset();
 
             RowHandler<RowT> hnd = ctx.rowHandler();
 
             // Check bound for NULL values. If bound contains NULLs, the whole 
range should be skipped.
             // There is special placeholder for searchable NULLs, make this 
replacement here too.
+            boolean hasNullBounds = false;
+
             for (int i = 0; i < hnd.columnCount(res); i++) {
                 Object fldVal = hnd.get(i, res);
 
@@ -851,11 +858,24 @@ public class ExpressionFactoryImpl<RowT> implements 
ExpressionFactory<RowT> {
                 }
 
                 if (fldVal == ctx.nullBound()) {
-                    hnd.set(i, res, null);
+                    hasNullBounds = true;
                 }
             }
 
-            return res;
+            if (!hasNullBounds) {
+                return res;
+            } else {
+                for (int i = 0; i < hnd.columnCount(res); i++) {
+                    Object fldVal = hnd.get(i, res);
+                    if (fldVal == ctx.nullBound()) {
+                        rowBuilder.addField(null);
+                    } else {
+                        rowBuilder.addField(fldVal);
+                    }
+                }
+
+                return rowBuilder.buildAndReset();
+            }
         }
 
         /** Clear cached rows. */
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/SingleScalar.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/SingleScalar.java
index 42d2f5183d..d7e86bdd8e 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/SingleScalar.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/SingleScalar.java
@@ -18,6 +18,7 @@
 package org.apache.ignite.internal.sql.engine.exec.exp;
 
 import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
+import org.apache.ignite.internal.sql.engine.exec.RowHandler.RowBuilder;
 
 /**
  * Single scalar used for single input and single output.
@@ -25,5 +26,5 @@ import 
org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
 @FunctionalInterface
 public interface SingleScalar extends Scalar {
     /** Single input and single output. */
-    void execute(ExecutionContext ctx, Object in, Object out);
+    void execute(ExecutionContext ctx, Object in, RowBuilder out);
 }
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 43be2825b8..a36da794d2 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
@@ -32,6 +32,7 @@ import org.apache.calcite.sql.SqlIntervalQualifier;
 import org.apache.calcite.sql.parser.SqlParserUtil;
 import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
 import org.apache.ignite.internal.sql.engine.exec.RowHandler;
+import org.apache.ignite.internal.sql.engine.exec.RowHandler.RowBuilder;
 import org.apache.ignite.internal.sql.engine.exec.exp.BiScalar;
 import org.apache.ignite.internal.sql.engine.exec.exp.IgniteSqlFunctions;
 import org.apache.ignite.internal.sql.engine.exec.exp.SingleScalar;
@@ -40,8 +41,8 @@ import 
org.apache.ignite.internal.sql.engine.exec.exp.SingleScalar;
  * Contains methods used in metadata definitions.
  */
 public enum IgniteMethod {
-    /** See {@link RowHandler#set(int, Object, Object)}. */
-    ROW_HANDLER_SET(RowHandler.class, "set", int.class, Object.class, 
Object.class),
+    /** See {@link RowBuilder#addField(Object)} )}. */
+    ROW_BUILDER_ADD_FIELD(RowBuilder.class, "addField", Object.class),
 
     /** See {@link RowHandler#get(int, Object)}. */
     ROW_HANDLER_GET(RowHandler.class, "get", int.class, Object.class),
@@ -62,11 +63,11 @@ public enum IgniteMethod {
     /** See {@link ExecutionContext#getParameter(String, Type)}. */
     CONTEXT_GET_PARAMETER_VALUE(ExecutionContext.class, "getParameter", 
String.class, Type.class),
 
-    /** See {@link SingleScalar#execute(ExecutionContext, Object, Object)}. */
-    SCALAR_EXECUTE(SingleScalar.class, "execute", ExecutionContext.class, 
Object.class, Object.class),
+    /** See {@link SingleScalar#execute(ExecutionContext, Object, 
RowBuilder)}. */
+    SCALAR_EXECUTE(SingleScalar.class, "execute", ExecutionContext.class, 
Object.class, RowBuilder.class),
 
-    /** See {@link BiScalar#execute(ExecutionContext, Object, Object, 
Object)}. */
-    BI_SCALAR_EXECUTE(BiScalar.class, "execute", ExecutionContext.class, 
Object.class, Object.class, Object.class),
+    /** See {@link BiScalar#execute(ExecutionContext, Object, Object, 
RowBuilder)}. */
+    BI_SCALAR_EXECUTE(BiScalar.class, "execute", ExecutionContext.class, 
Object.class, Object.class, RowBuilder.class),
 
     SYSTEM_RANGE2(IgniteSqlFunctions.class, "systemRange", Object.class, 
Object.class),
 
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 63a4b904cf..b72739258e 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
@@ -53,6 +53,7 @@ import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.calcite.sql.type.SqlTypeUtil;
 import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
 import org.apache.ignite.internal.sql.engine.exec.RowHandler;
+import org.apache.ignite.internal.sql.engine.exec.RowHandler.RowBuilder;
 import org.apache.ignite.internal.sql.engine.exec.row.BaseTypeSpec;
 import org.apache.ignite.internal.sql.engine.exec.row.RowSchema;
 import org.apache.ignite.internal.sql.engine.exec.row.RowSchemaTypes;
@@ -177,12 +178,19 @@ public class TypeUtils {
             RowHandler.RowFactory<RowT> factory = handler.factory(rowSchema);
             List<Function<Object, Object>> converters = transform(types, t -> 
fieldConverter(ectx, t));
             return r -> {
-                RowT newRow = factory.create();
-                assert handler.columnCount(newRow) == converters.size();
                 assert handler.columnCount(r) == converters.size();
+
+                RowBuilder<RowT> rowBuilder = factory.rowBuilder();
+
                 for (int i = 0; i < converters.size(); i++) {
-                    handler.set(i, newRow, 
converters.get(i).apply(handler.get(i, r)));
+                    Object converted = converters.get(i).apply(handler.get(i, 
r));
+                    rowBuilder.addField(converted);
                 }
+
+                RowT newRow = rowBuilder.buildAndReset();
+
+                assert handler.columnCount(newRow) == converters.size();
+
                 return newRow;
             };
         }
@@ -214,22 +222,9 @@ public class TypeUtils {
     }
 
     /**
-     * ToInternal. Converts the given value to its presentation used by the 
execution engine.
-     *
-     * @deprecated The implementation of this method is incorrect because it 
relies on the assumption that
-     *      {@code val.getClass() == storageType(val)} is always true, which 
sometimes is not the case.
-     *      Use {@link #toInternal(Object, Type)} that provides type 
information instead.
-     */
-    @Deprecated
-    public static @Nullable Object toInternal(Object val) {
-        return val == null ? null : toInternal(val, val.getClass());
-    }
-
-    /**
-     * ToInternal.
-     * TODO Documentation https://issues.apache.org/jira/browse/IGNITE-15859
+     * Converts the given value to its presentation used by the execution 
engine.
      */
-    public static @Nullable Object toInternal(Object val, Type storageType) {
+    public static @Nullable Object toInternal(@Nullable Object val, Type 
storageType) {
         if (val == null) {
             return null;
         } else if (storageType == LocalDate.class) {
diff --git 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/exp/ExpressionFactoryImplTest.java
 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/exp/ExpressionFactoryImplTest.java
index b9716646cb..5c0fa08d99 100644
--- 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/exp/ExpressionFactoryImplTest.java
+++ 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/exp/ExpressionFactoryImplTest.java
@@ -17,10 +17,13 @@
 
 package org.apache.ignite.internal.sql.engine.exec.exp;
 
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNotSame;
 import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import it.unimi.dsi.fastutil.longs.Long2ObjectMaps;
 import java.math.BigDecimal;
@@ -29,6 +32,9 @@ import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.UUID;
+import java.util.function.BiPredicate;
+import java.util.function.Function;
+import java.util.function.Predicate;
 import org.apache.calcite.rel.RelCollations;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeFactory.Builder;
@@ -38,6 +44,7 @@ import org.apache.calcite.rel.type.RelDataTypeSystem;
 import org.apache.calcite.rel.type.RelRecordType;
 import org.apache.calcite.rex.RexBuilder;
 import org.apache.calcite.rex.RexDynamicParam;
+import org.apache.calcite.rex.RexInputRef;
 import org.apache.calcite.rex.RexLiteral;
 import org.apache.calcite.rex.RexLocalRef;
 import org.apache.calcite.rex.RexNode;
@@ -243,6 +250,133 @@ public class ExpressionFactoryImplTest extends 
BaseIgniteAbstractTest {
         assertEquals(1, ranges.iterator().next().upper().length);
     }
 
+    @Test
+    public void testProject() {
+        RexBuilder rexBuilder = Commons.rexBuilder();
+        IgniteTypeFactory tf = Commons.typeFactory();
+
+        RelDataType intType = tf.createSqlType(SqlTypeName.INTEGER);
+        RelDataType bigIntType = tf.createSqlType(SqlTypeName.BIGINT);
+
+        RexNode val1 = rexBuilder.makeExactLiteral(new BigDecimal("1"), 
intType);
+        RexNode val2 = rexBuilder.makeExactLiteral(new BigDecimal("2"), 
bigIntType);
+
+        RelDataType rowType = new Builder(tf)
+                .add("c1", intType)
+                .add("c2", bigIntType)
+                .build();
+
+        Function<Object[], Object[]> project = 
expFactory.project(List.of(val1, val2), rowType);
+        Object[] result = project.apply(new Object[]{null, null});
+
+        assertArrayEquals(new Object[]{1, 2L}, result);
+    }
+
+    @Test
+    public void testPredicate() {
+        RexBuilder rexBuilder = Commons.rexBuilder();
+        IgniteTypeFactory tf = Commons.typeFactory();
+
+        RelDataType intType = tf.createSqlType(SqlTypeName.INTEGER);
+        RelDataType rowType = new Builder(tf)
+                .add("c1", intType)
+                .build();
+
+        RexInputRef ref = rexBuilder.makeInputRef(rowType, 0);
+        RexNode filter = rexBuilder.makeCall(SqlStdOperatorTable.IS_NULL, 
List.of(ref));
+
+        Predicate<Object[]> predicate = expFactory.predicate(filter, rowType);
+        assertFalse(predicate.test(new Object[]{1}));
+    }
+
+    @Test
+    public void testBiPredicate() {
+        RexBuilder rexBuilder = Commons.rexBuilder();
+        IgniteTypeFactory tf = Commons.typeFactory();
+
+        RelDataType intType = tf.createSqlType(SqlTypeName.INTEGER);
+        RelDataType rowType = new Builder(tf)
+                .add("c1", intType)
+                .add("c2", intType)
+                .build();
+
+        RexInputRef ref1 = rexBuilder.makeInputRef(rowType, 0);
+        RexInputRef ref2 = rexBuilder.makeInputRef(rowType, 1);
+        RexNode filter = rexBuilder.makeCall(SqlStdOperatorTable.EQUALS, 
List.of(ref1, ref2));
+
+        BiPredicate<Object[], Object[]> predicate = 
expFactory.biPredicate(filter, rowType);
+        assertFalse(predicate.test(new Object[]{0, 1}, new Object[]{1, 0}));
+        assertTrue(predicate.test(new Object[]{0, 0}, new Object[]{0, 0}));
+    }
+
+    @Test
+    public void testValues() {
+        RexBuilder rexBuilder = Commons.rexBuilder();
+        IgniteTypeFactory tf = Commons.typeFactory();
+
+        RelDataType intType = tf.createSqlType(SqlTypeName.INTEGER);
+        RelDataType bigIntType = tf.createSqlType(SqlTypeName.BIGINT);
+
+        RexLiteral val10 = rexBuilder.makeExactLiteral(new BigDecimal("1"), 
intType);
+        RexLiteral val11 = rexBuilder.makeExactLiteral(new BigDecimal("2"), 
bigIntType);
+        RexLiteral val20 = rexBuilder.makeExactLiteral(new BigDecimal("3"), 
intType);
+        RexLiteral val21 = rexBuilder.makeExactLiteral(new BigDecimal("4"), 
bigIntType);
+
+        RelDataType rowType = new Builder(tf)
+                .add("c1", intType)
+                .add("c2", bigIntType)
+                .build();
+
+        List<List<Object>> actual = new ArrayList<>();
+        expFactory.values(List.of(val10, val11, val20, val21), 
rowType).forEach(v -> actual.add(Arrays.asList(v)));
+
+        assertEquals(List.of(List.of(1, 2L), List.of(3, 4L)), actual);
+    }
+
+    @Test
+    public void testValuesEmpty() {
+        IgniteTypeFactory tf = Commons.typeFactory();
+
+        RelDataType intType = tf.createSqlType(SqlTypeName.INTEGER);
+        RelDataType bigIntType = tf.createSqlType(SqlTypeName.BIGINT);
+
+        RelDataType rowType = new Builder(tf)
+                .add("c1", intType)
+                .add("c2", bigIntType)
+                .build();
+
+        List<List<Object>> actual = new ArrayList<>();
+        expFactory.values(List.of(), rowType).forEach(v -> 
actual.add(Arrays.asList(v)));
+
+        assertEquals(List.of(), actual);
+    }
+
+    @Test
+    public void testRowSource() {
+        RexBuilder rexBuilder = Commons.rexBuilder();
+        IgniteTypeFactory tf = Commons.typeFactory();
+
+        RelDataType intType = tf.createSqlType(SqlTypeName.INTEGER);
+        RelDataType bigIntType = tf.createSqlType(SqlTypeName.BIGINT);
+
+        RexNode val10 = rexBuilder.makeExactLiteral(new BigDecimal("1"), 
intType);
+        RexNode val11 = rexBuilder.makeExactLiteral(new BigDecimal("2"), 
bigIntType);
+
+        Object[] actual = expFactory.rowSource(List.of(val10, val11)).get();
+        assertEquals(List.of(1, 2L), Arrays.asList(actual));
+    }
+
+    @Test
+    public void testExpression() {
+        RexBuilder rexBuilder = Commons.rexBuilder();
+        IgniteTypeFactory tf = Commons.typeFactory();
+
+        RelDataType varcharType = tf.createSqlType(SqlTypeName.VARCHAR);
+        Object actual = expFactory.execute(rexBuilder.makeLiteral("42", 
varcharType)).get();
+
+        assertEquals("42", actual);
+    }
+
     static final class TestRange {
 
         final Object[] lower;
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 c24276cfcf..0d9f9a3b79 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
@@ -45,6 +45,8 @@ import org.apache.ignite.internal.schema.BinaryTupleSchema;
 import org.apache.ignite.internal.sql.engine.exec.ExecutionContext;
 import org.apache.ignite.internal.sql.engine.exec.QueryTaskExecutorImpl;
 import org.apache.ignite.internal.sql.engine.exec.RowHandler;
+import org.apache.ignite.internal.sql.engine.exec.RowHandler.RowBuilder;
+import org.apache.ignite.internal.sql.engine.exec.RowHandler.RowFactory;
 import org.apache.ignite.internal.sql.engine.exec.TxAttributes;
 import org.apache.ignite.internal.sql.engine.exec.mapping.FragmentDescription;
 import org.apache.ignite.internal.sql.engine.framework.ArrayRowHandler;
@@ -339,6 +341,11 @@ public abstract class AbstractExecutionTest<T> extends 
IgniteAbstractTest {
                 return ArrayRowHandler.INSTANCE;
             }
 
+            @Override
+            public RowBuilder<Object[]> rowBuilder() {
+                throw new UnsupportedOperationException();
+            }
+
             @Override
             public Object[] create() {
                 throw new AssertionError();
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 ecf4fecbbf..79159b2be8 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
@@ -18,10 +18,12 @@
 package org.apache.ignite.internal.sql.engine.exec.row;
 
 import static 
org.apache.ignite.internal.sql.engine.util.SqlTestUtils.generateValueByType;
-import static 
org.apache.ignite.internal.sql.engine.util.TypeUtils.columnType2NativeType;
-import static org.apache.ignite.internal.sql.engine.util.TypeUtils.toInternal;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.equalTo;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -29,19 +31,23 @@ import java.util.Collections;
 import java.util.EnumSet;
 import java.util.List;
 import java.util.Random;
+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.RowHandler;
+import org.apache.ignite.internal.sql.engine.exec.RowHandler.RowBuilder;
 import org.apache.ignite.internal.sql.engine.exec.RowHandler.RowFactory;
 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.exec.row.RowSchema.Builder;
+import org.apache.ignite.internal.sql.engine.util.Commons;
 import org.apache.ignite.internal.sql.engine.util.TypeUtils;
 import org.apache.ignite.internal.testframework.IgniteAbstractTest;
 import org.apache.ignite.internal.type.NativeType;
 import org.apache.ignite.internal.type.NativeTypes;
 import org.apache.ignite.sql.ColumnType;
+import org.jetbrains.annotations.Nullable;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Named;
 import org.junit.jupiter.api.Test;
@@ -66,14 +72,14 @@ public class SqlRowHandlerTest extends IgniteAbstractTest {
 
     @Test
     public void testBytebufferSerialization() {
-        List<ColumnType> columnTypes = columnTypes();
+        List<ColumnType> columnTypes = shuffledColumnTypes();
         Object[] sourceData = values(columnTypes);
         RowSchema schema = rowSchema(columnTypes, sourceData);
 
         int elementsCount = schema.fields().size();
 
         RowFactory<RowWrapper> factory = handler.factory(schema);
-        RowWrapper src = factory.create(wrap(sourceData));
+        RowWrapper src = factory.create(wrap(sourceData, schema));
 
         // Serialization to binary tuple representation.
         BinaryTuple tuple = handler.toBinaryTuple(src);
@@ -81,8 +87,8 @@ public class SqlRowHandlerTest extends IgniteAbstractTest {
 
         for (int i = 0; i < elementsCount; i++) {
             String msg = schema.fields().get(i).toString();
-
-            Object expected = toInternal(sourceData[i]);
+            TypeSpec typeSpec = schema.fields().get(i);
+            Object expected = convertToInternal(typeSpec, sourceData[i]);
 
             assertThat(msg, handler.get(i, src), equalTo(expected));
             assertThat(msg, handler.get(i, dest), equalTo(expected));
@@ -116,14 +122,19 @@ public class SqlRowHandlerTest extends IgniteAbstractTest 
{
         RowWrapper result = handler.factory(concatenatedSchema).create(tuple);
 
         for (int i = 0; i < leftLen; i++) {
-            assertThat(handler.get(i, result), 
equalTo(TypeUtils.toInternal(params.leftData[i])));
+            TypeSpec typeSpec = params.leftSchema.fields().get(i);
+
+            assertThat(handler.get(i, result), 
equalTo(convertToInternal(typeSpec, params.leftData[i])));
         }
 
         for (int i = 0; i < rightLen; i++) {
-            assertThat(handler.get(leftLen + i, result), 
equalTo(TypeUtils.toInternal(params.rightData[i])));
+            TypeSpec typeSpec = params.rightSchema.fields().get(i);
+
+            assertThat(handler.get(leftLen + i, result), 
equalTo(convertToInternal(typeSpec, params.rightData[i])));
         }
     }
 
+
     @Test
     public void testMap() {
         List<ColumnType> columnTypes = List.of(
@@ -172,6 +183,85 @@ public class SqlRowHandlerTest extends IgniteAbstractTest {
         );
     }
 
+    @ParameterizedTest
+    @MethodSource("columnTypes")
+    public void testRowBuilder(ColumnType type) {
+        Object value1 = generateValueByType(0, type);
+
+        RowSchema rowSchema = rowSchema(List.of(type), new Object[]{value1});
+        RowFactory<RowWrapper> rowFactory = handler.factory(rowSchema);
+        RowBuilder<RowWrapper> builder = rowFactory.rowBuilder();
+
+        RowWrapper row00 = builder.addField(value1).build();
+        assertEquals(value1, handler.get(0, row00));
+
+        RowWrapper row01 = builder.build();
+        assertEquals(value1, handler.get(0, row01));
+
+        builder.reset();
+
+        Object value2 = generateValueByType(0, type);
+        RowWrapper row2 = builder.addField(value2).build();
+        assertEquals(value2, handler.get(0, row2));
+    }
+
+    @Test
+    public void testRowBuilderRejectInvalidField() {
+        RowSchema rowSchema = rowSchema(List.of(ColumnType.INT32), new 
Object[]{1});
+        RowFactory<RowWrapper> rowFactory = handler.factory(rowSchema);
+
+        RowBuilder<RowWrapper> builder = rowFactory.rowBuilder();
+        builder.addField(1);
+
+        IllegalStateException err = assertThrows(IllegalStateException.class, 
() -> builder.addField(1));
+        assertThat(err.getMessage(), containsString("Field index is out of 
bounds"));
+    }
+
+    @Test
+    public void testRowBuilderBuildReset() {
+        RowSchema rowSchema = rowSchema(List.of(ColumnType.INT32), new 
Object[]{1});
+        RowFactory<RowWrapper> rowFactory = handler.factory(rowSchema);
+
+        RowBuilder<RowWrapper> builder = rowFactory.rowBuilder();
+
+        RowWrapper row1 = builder.addField(1).build();
+        assertEquals(1, handler.get(0, row1));
+
+        builder.reset();
+
+        String message = "Row has not been initialised";
+        IllegalStateException err1 = assertThrows(IllegalStateException.class, 
builder::build);
+        assertThat(err1.getMessage(), containsString(message));
+
+        RowWrapper row2 = builder.addField(2).buildAndReset();
+        assertEquals(2, handler.get(0, row2));
+
+        IllegalStateException err2 = assertThrows(IllegalStateException.class, 
builder::build);
+        assertThat(err2.getMessage(), containsString(message));
+
+        RowWrapper row3 = builder.addField(3).build();
+        assertEquals(3, handler.get(0, row3));
+    }
+
+    @Test
+    public void testRowBuilderBuildingIncompleteRowIsNotAllowed() {
+        RowSchema rowSchema = rowSchema(List.of(ColumnType.INT32, 
ColumnType.INT32), new Object[]{1, 2});
+        RowFactory<RowWrapper> rowFactory = handler.factory(rowSchema);
+
+        RowBuilder<RowWrapper> rowBuilder = rowFactory.rowBuilder();
+        rowBuilder.addField(1);
+
+        IllegalStateException err = assertThrows(IllegalStateException.class, 
rowBuilder::build);
+        assertThat(err.getMessage(), containsString("Row has not been fully 
built"));
+    }
+
+    @Test
+    public void testRowBuilderEmptyRow() {
+        RowFactory<RowWrapper> rowFactory = 
handler.factory(RowSchema.builder().build());
+        RowBuilder<RowWrapper> rowBuilder = rowFactory.rowBuilder();
+        assertNotNull(rowBuilder.build());
+    }
+
     private RowSchema rowSchema(List<ColumnType> columnTypes, Object[] values) 
{
         Builder schemaBuilder = RowSchema.builder();
 
@@ -185,7 +275,7 @@ public class SqlRowHandlerTest extends IgniteAbstractTest {
             }
 
             NativeType nativeType = values[i] == null
-                    ? columnType2NativeType(type, 9, 3, 20)
+                    ? TypeUtils.columnType2NativeType(type, 9, 3, 20)
                     : NativeTypes.fromObject(values[i]);
 
             schemaBuilder.addField(nativeType, values[i] == null || 
rnd.nextBoolean());
@@ -207,21 +297,24 @@ public class SqlRowHandlerTest extends IgniteAbstractTest 
{
         return values;
     }
 
-    private static Object[] wrap(Object[] values) {
+    private static Object[] wrap(Object[] values, RowSchema rowSchema) {
         Object[] newValues = new Object[values.length];
 
         for (int i = 0; i < values.length; i++) {
-            newValues[i] = toInternal(values[i]);
+            TypeSpec typeSpec = rowSchema.fields().get(i);
+            newValues[i] = convertToInternal(typeSpec, values[i]);
         }
 
         return newValues;
     }
 
-    private List<ColumnType> columnTypes() {
-        List<ColumnType> columnTypes = new ArrayList<>(
-                // TODO Include ignored types to test after 
https://issues.apache.org/jira/browse/IGNITE-15200
-                EnumSet.complementOf(EnumSet.of(ColumnType.PERIOD, 
ColumnType.DURATION))
-        );
+    private static Set<ColumnType> columnTypes() {
+        // TODO Include ignored types to test after 
https://issues.apache.org/jira/browse/IGNITE-15200
+        return EnumSet.complementOf(EnumSet.of(ColumnType.PERIOD, 
ColumnType.DURATION));
+    }
+
+    private List<ColumnType> shuffledColumnTypes() {
+        List<ColumnType> columnTypes = new ArrayList<>(columnTypes());
 
         Collections.shuffle(columnTypes, rnd);
 
@@ -237,8 +330,8 @@ public class SqlRowHandlerTest extends IgniteAbstractTest {
         final RowWrapper right;
 
         ConcatTestParameters(boolean leftTupleRequired, boolean 
rightTupleRequired) {
-            List<ColumnType> columnTypes1 = columnTypes();
-            List<ColumnType> columnTypes2 = columnTypes();
+            List<ColumnType> columnTypes1 = shuffledColumnTypes();
+            List<ColumnType> columnTypes2 = shuffledColumnTypes();
 
             leftData = values(columnTypes1);
             rightData = values(columnTypes2);
@@ -248,11 +341,26 @@ public class SqlRowHandlerTest extends IgniteAbstractTest 
{
             RowFactory<RowWrapper> factory1 = handler.factory(leftSchema);
             RowFactory<RowWrapper> factory2 = handler.factory(rightSchema);
 
-            RowWrapper left = factory1.create(wrap(leftData));
-            RowWrapper right = factory2.create(wrap(rightData));
+            RowWrapper left = factory1.create(wrap(leftData, leftSchema));
+            RowWrapper right = factory2.create(wrap(rightData, rightSchema));
 
             this.left = leftTupleRequired ? 
factory1.create(handler.toBinaryTuple(left)) : left;
             this.right = rightTupleRequired ? 
factory2.create(handler.toBinaryTuple(right)) : right;
         }
     }
+
+    private static @Nullable Object convertToInternal(NativeType nativeType, 
Object value) {
+        return convertToInternal(RowSchemaTypes.nativeType(nativeType), value);
+    }
+
+    private static @Nullable Object convertToInternal(TypeSpec typeSpec, 
Object value) {
+        if (typeSpec instanceof NullTypeSpec) {
+            return null;
+        } else {
+            BaseTypeSpec baseTypeSpec = (BaseTypeSpec) typeSpec;
+            NativeType nativeType = baseTypeSpec.nativeType();
+            Class<?> type = Commons.nativeTypeToClass(nativeType);
+            return TypeUtils.toInternal(value, type);
+        }
+    }
 }
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 c80817c63b..19fa6b696a 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
@@ -17,6 +17,8 @@
 
 package org.apache.ignite.internal.sql.engine.framework;
 
+import static org.apache.ignite.internal.lang.IgniteStringFormatter.format;
+
 import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.time.Instant;
@@ -56,12 +58,6 @@ public class ArrayRowHandler implements RowHandler<Object[]> 
{
         return row[field];
     }
 
-    /** {@inheritDoc} */
-    @Override
-    public void set(int field, Object[] row, Object val) {
-        row[field] = val;
-    }
-
     /** {@inheritDoc} */
     @Override
     public Object[] concat(Object[] left, Object[] right) {
@@ -106,7 +102,7 @@ public class ArrayRowHandler implements 
RowHandler<Object[]> {
     /** {@inheritDoc} */
     @Override
     public RowFactory<Object[]> factory(RowSchema rowSchema) {
-        int rowLen = rowSchema.fields().size();
+        int schemaLen = rowSchema.fields().size();
 
         return new RowFactory<>() {
             /** {@inheritDoc} */
@@ -115,16 +111,21 @@ public class ArrayRowHandler implements 
RowHandler<Object[]> {
                 return ArrayRowHandler.this;
             }
 
+            @Override
+            public RowBuilder<Object[]> rowBuilder() {
+                return new RowBuilderImpl(schemaLen);
+            }
+
             /** {@inheritDoc} */
             @Override
             public Object[] create() {
-                return new Object[rowLen];
+                return new Object[schemaLen];
             }
 
             /** {@inheritDoc} */
             @Override
             public Object[] create(Object... fields) {
-                assert fields.length == rowLen;
+                assert fields.length == schemaLen;
 
                 return fields;
             }
@@ -262,4 +263,65 @@ public class ArrayRowHandler implements 
RowHandler<Object[]> {
             default: throw new InvalidTypeException("Unknown element type: " + 
nativeType);
         }
     }
+
+    private static class RowBuilderImpl implements RowBuilder<Object[]> {
+
+        private final int schemaLen;
+
+        Object[] data;
+
+        int fieldIdx;
+
+        RowBuilderImpl(int schemaLen) {
+            this.schemaLen = schemaLen;
+            fieldIdx = 0;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void reset() {
+            this.data = null;
+            fieldIdx = 0;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public RowBuilder<Object[]> addField(Object value) {
+            if (fieldIdx == 0 && data == null) {
+                data = new Object[schemaLen];
+            }
+
+            checkIndex();
+
+            data[fieldIdx++] = value;
+            return this;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public Object[] build() {
+            checkState();
+
+            if (schemaLen == 0) {
+                return new Object[0];
+            } else {
+                return data;
+            }
+        }
+
+        private void checkState() {
+            if (schemaLen != 0 && data == null) {
+                throw new IllegalStateException("Row has not been 
initialised");
+            }
+            if (fieldIdx != schemaLen) {
+                throw new IllegalStateException(format("Row has not been fully 
built. Index: {}, fields: {}", fieldIdx, schemaLen));
+            }
+        }
+
+        private void checkIndex() {
+            if (fieldIdx >= schemaLen) {
+                throw new IllegalStateException(format("Field index is out of 
bounds. Index: {}, fields: {}", fieldIdx, schemaLen));
+            }
+        }
+    }
 }
diff --git 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/util/TypeUtilsTest.java
 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/util/TypeUtilsTest.java
index 875de9ac59..2518012957 100644
--- 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/util/TypeUtilsTest.java
+++ 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/util/TypeUtilsTest.java
@@ -91,9 +91,6 @@ public class TypeUtilsTest extends BaseIgniteAbstractTest {
         Object original = TypeUtils.fromInternal(internal, type);
         assertEquals(value, original, "toInternal -> fromInternal");
         assertNotNull(original, "Conversion from internal has produced null");
-
-        Object internal2 = TypeUtils.toInternal(original);
-        assertEquals(internal, internal2, "toInternal w/o type parameter");
     }
 
     private static Stream<Arguments> valueAndType() {

Reply via email to