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() {