This is an automated email from the ASF dual-hosted git repository.
amashenkov 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 d071242834 IGNITE-18827: Sql. UUID. Implement min/max functions.
(#1686)
d071242834 is described below
commit d0712428348f403b78fd7df5c617ecb582a497d5
Author: Max Zhuravkov <[email protected]>
AuthorDate: Wed Feb 22 21:53:00 2023 +0400
IGNITE-18827: Sql. UUID. Implement min/max functions. (#1686)
---
.../ignite/internal/sql/engine/ItMetadataTest.java | 10 +-
.../ignite/internal/sql/engine/ItUuidTest.java | 16 +-
.../sql/engine/exec/exp/agg/Accumulators.java | 329 +++++----------------
.../engine/exec/exp/agg/AccumulatorsFactory.java | 10 +-
4 files changed, 99 insertions(+), 266 deletions(-)
diff --git
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItMetadataTest.java
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItMetadataTest.java
index 671963eced..cc14334d22 100644
---
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItMetadataTest.java
+++
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItMetadataTest.java
@@ -139,8 +139,8 @@ public class ItMetadataTest extends
AbstractBasicIntegrationTest {
// + "INTERVAL_SEC_C2 INTERVAL SECOND(9), "
// Custom types
- // TODO: IGNITE-16376 support additional data types.
- // + "UUID_C UUID, "
+ + "UUID_C UUID, "
+ // TODO: IGNITE-18431: Sql. BitSet is not supported.
// + "BITSET_C BITMASK, "
// + "BITSET_C BITMASK(8), "
@@ -194,7 +194,7 @@ public class ItMetadataTest extends
AbstractBasicIntegrationTest {
new
MetadataMatcher().name("TIMESTAMP_C2").type(ColumnType.TIMESTAMP).precision(9).scale(UNDEFINED_SCALE),
// Interval types
- // TODO: Ignite doesn't support interval types.
+ // TODO: IGNITE-17373: Ignite doesn't support interval
types yet.
// new MetadataMatcher().name("INTERVAL_YEAR_C"),
// new MetadataMatcher().name("INTERVAL_MONTH_C"),
// new MetadataMatcher().name("INTERVAL_DAY_C"),
@@ -204,8 +204,8 @@ public class ItMetadataTest extends
AbstractBasicIntegrationTest {
// new MetadataMatcher().name("INTERVAL_SEC_C2"),
// Custom types
- // TODO: IGNITE-16376 support additional data types.
- // new MetadataMatcher().name("UUID_C"),
+ new MetadataMatcher().name("UUID_C"),
+ // TODO: IGNITE-18431: Sql. BitSet is not supported.
// new MetadataMatcher().name("BITSET_C"),
// new MetadataMatcher().name("BITSET_C2"),
diff --git
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItUuidTest.java
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItUuidTest.java
index d69949e994..2630f24fc8 100644
---
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItUuidTest.java
+++
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItUuidTest.java
@@ -22,7 +22,6 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.junit.jupiter.api.Assertions.assertThrows;
-import java.util.Arrays;
import java.util.UUID;
import java.util.stream.Stream;
import org.apache.calcite.runtime.CalciteContextException;
@@ -141,11 +140,18 @@ public class ItUuidTest extends
AbstractBasicIntegrationTest {
.returns(1, UUID_1)
.returns(2, UUID_2)
.check();
+ }
- // UUID can be used by several aggregate functions
- for (var func : Arrays.asList("COUNT", "ANY_VALUE")) {
- sql(format("SELECT {}(uuid_key) FROM t", func));
- }
+ @Test
+ public void testBasicAggregates() {
+ sql(format("INSERT INTO t VALUES (1, CAST('{}' as UUID))", UUID_1));
+ sql(format("INSERT INTO t VALUES (2, CAST('{}' as UUID))", UUID_2));
+
+ assertQuery("SELECT COUNT(uuid_key) FROM t").returns(2L).check();
+ assertQuery("SELECT ANY_VALUE(uuid_key) FROM t").check();
+
+ assertQuery("SELECT MIN(uuid_key) FROM t").returns(UUID_2).check();
+ assertQuery("SELECT MAX(uuid_key) FROM t").returns(UUID_1).check();
}
@Test
diff --git
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/agg/Accumulators.java
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/agg/Accumulators.java
index 3047060c85..f6b875cfbc 100644
---
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/agg/Accumulators.java
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/agg/Accumulators.java
@@ -34,6 +34,8 @@ import java.util.Set;
import java.util.function.Supplier;
import org.apache.calcite.rel.core.AggregateCall;
import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
+import org.apache.ignite.internal.sql.engine.type.IgniteCustomType;
import org.apache.ignite.internal.sql.engine.type.IgniteTypeFactory;
/**
@@ -41,11 +43,21 @@ import
org.apache.ignite.internal.sql.engine.type.IgniteTypeFactory;
* TODO Documentation https://issues.apache.org/jira/browse/IGNITE-15859
*/
public class Accumulators {
+
+ private final IgniteTypeFactory typeFactory;
+
+ /**
+ * TODO Documentation https://issues.apache.org/jira/browse/IGNITE-15859
+ */
+ public Accumulators(IgniteTypeFactory typeFactory) {
+ this.typeFactory = typeFactory;
+ }
+
/**
* AccumulatorFactory.
* TODO Documentation https://issues.apache.org/jira/browse/IGNITE-15859
*/
- public static Supplier<Accumulator> accumulatorFactory(AggregateCall call)
{
+ public Supplier<Accumulator> accumulatorFactory(AggregateCall call) {
if (!call.isDistinct()) {
return accumulatorFunctionFactory(call);
}
@@ -55,11 +67,7 @@ public class Accumulators {
return () -> new DistinctAccumulator(fac);
}
- /**
- * AccumulatorFunctionFactory.
- * TODO Documentation https://issues.apache.org/jira/browse/IGNITE-15859
- */
- public static Supplier<Accumulator>
accumulatorFunctionFactory(AggregateCall call) {
+ private Supplier<Accumulator> accumulatorFunctionFactory(AggregateCall
call) {
// Update documentation in IgniteCustomType when you add an aggregate
// that can work for any type out of the box.
switch (call.getAggregation().getName()) {
@@ -72,9 +80,9 @@ public class Accumulators {
case "$SUM0":
return sumEmptyIsZeroFactory(call);
case "MIN":
- return minFactory(call);
+ return minMaxFactory(true, call);
case "MAX":
- return maxFactory(call);
+ return minMaxFactory(false, call);
case "SINGLE_VALUE":
return SingleVal.FACTORY;
case "ANY_VALUE":
@@ -84,7 +92,7 @@ public class Accumulators {
}
}
- private static Supplier<Accumulator> avgFactory(AggregateCall call) {
+ private Supplier<Accumulator> avgFactory(AggregateCall call) {
switch (call.type.getSqlTypeName()) {
case BIGINT:
case DECIMAL:
@@ -102,7 +110,7 @@ public class Accumulators {
}
}
- private static Supplier<Accumulator> sumFactory(AggregateCall call) {
+ private Supplier<Accumulator> sumFactory(AggregateCall call) {
switch (call.type.getSqlTypeName()) {
case BIGINT:
case DECIMAL:
@@ -125,7 +133,7 @@ public class Accumulators {
}
}
- private static Supplier<Accumulator> sumEmptyIsZeroFactory(AggregateCall
call) {
+ private Supplier<Accumulator> sumEmptyIsZeroFactory(AggregateCall call) {
switch (call.type.getSqlTypeName()) {
case BIGINT:
case DECIMAL:
@@ -148,49 +156,22 @@ public class Accumulators {
}
}
- private static Supplier<Accumulator> minFactory(AggregateCall call) {
- switch (call.type.getSqlTypeName()) {
- case DOUBLE:
- case REAL:
- case FLOAT:
- return DoubleMinMax.MIN_FACTORY;
- case DECIMAL:
- return DecimalMinMax.MIN_FACTORY;
- case INTEGER:
- return IntMinMax.MIN_FACTORY;
- case CHAR:
- case VARCHAR:
- return VarCharMinMax.MIN_FACTORY;
- case BIGINT:
- default:
- // IgniteCustomType: MIN for a custom type should go here.
- if (call.type.getSqlTypeName() == ANY) {
- throw unsupportedAggregateFunction(call);
- }
- return LongMinMax.MIN_FACTORY;
- }
- }
+ private Supplier<Accumulator> minMaxFactory(boolean min, AggregateCall
call) {
+ var type = call.getType();
+ var typeName = type.getSqlTypeName();
- private static Supplier<Accumulator> maxFactory(AggregateCall call) {
- switch (call.type.getSqlTypeName()) {
- case DOUBLE:
- case REAL:
- case FLOAT:
- return DoubleMinMax.MAX_FACTORY;
- case DECIMAL:
- return DecimalMinMax.MAX_FACTORY;
- case INTEGER:
- return IntMinMax.MAX_FACTORY;
+ switch (typeName) {
case CHAR:
case VARCHAR:
- return VarCharMinMax.MAX_FACTORY;
- case BIGINT:
+ return min ? VarCharMinMax.MIN_FACTORY :
VarCharMinMax.MAX_FACTORY;
default:
- // IgniteCustomType: MAX for a custom type should go here.
- if (call.type.getSqlTypeName() == ANY) {
+ if (type instanceof IgniteCustomType) {
+ return MinMaxAccumulator.newAccumulator(min, typeFactory,
type);
+ } else if (type.getSqlTypeName() == ANY) {
throw unsupportedAggregateFunction(call);
+ } else {
+ return MinMaxAccumulator.newAccumulator(min, typeFactory,
type);
}
- return LongMinMax.MAX_FACTORY;
}
}
@@ -644,63 +625,81 @@ public class Accumulators {
}
}
- private static class DoubleMinMax implements Accumulator {
- public static final Supplier<Accumulator> MIN_FACTORY = () -> new
DoubleMinMax(true);
+ private static final class MinMaxAccumulator implements Accumulator {
- public static final Supplier<Accumulator> MAX_FACTORY = () -> new
DoubleMinMax(false);
+ private static final long serialVersionUID = 0;
private final boolean min;
- private double val;
+ private final List<RelDataType> arguments;
- private boolean empty = true;
+ private final RelDataType returnType;
+
+ @SuppressWarnings({"rawtypes"})
+ private Comparable val;
+
+ private MinMaxAccumulator(boolean min, RelDataTypeFactory typeFactory,
RelDataType relDataType) {
+ var nullableType =
typeFactory.createTypeWithNullability(relDataType, true);
- private DoubleMinMax(boolean min) {
this.min = min;
+ this.arguments = List.of(nullableType);
+ this.returnType = nullableType;
}
- /** {@inheritDoc} */
+ static Supplier<Accumulator> newAccumulator(boolean min,
RelDataTypeFactory typeFactory, RelDataType type) {
+ return () -> new MinMaxAccumulator(min, typeFactory, type);
+ }
+
+ /** {@inheritDoc} **/
@Override
+ @SuppressWarnings({"rawtypes"})
public void add(Object... args) {
- Double in = (Double) args[0];
+ Comparable in = (Comparable) args[0];
- if (in == null) {
- return;
- }
-
- val = empty ? in : min ? Math.min(val, in) : Math.max(val, in);
- empty = false;
+ doApply(in);
}
- /** {@inheritDoc} */
+ /** {@inheritDoc} **/
@Override
public void apply(Accumulator other) {
- DoubleMinMax other0 = (DoubleMinMax) other;
-
- if (other0.empty) {
- return;
- }
-
- val = empty ? other0.val : min ? Math.min(val, other0.val) :
Math.max(val, other0.val);
- empty = false;
+ MinMaxAccumulator other0 = (MinMaxAccumulator) other;
+ doApply(other0.val);
}
- /** {@inheritDoc} */
+ /** {@inheritDoc} **/
@Override
public Object end() {
- return empty ? null : val;
+ return val;
}
- /** {@inheritDoc} */
+ /** {@inheritDoc} **/
@Override
public List<RelDataType> argumentTypes(IgniteTypeFactory typeFactory) {
- return
List.of(typeFactory.createTypeWithNullability(typeFactory.createSqlType(DOUBLE),
true));
+ return arguments;
}
- /** {@inheritDoc} */
+ /** {@inheritDoc} **/
@Override
public RelDataType returnType(IgniteTypeFactory typeFactory) {
- return
typeFactory.createTypeWithNullability(typeFactory.createSqlType(DOUBLE), true);
+ return returnType;
+ }
+
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ private void doApply(Comparable in) {
+ if (in == null) {
+ return;
+ }
+
+ if (val == null) {
+ val = in;
+ } else {
+ var cmp = val.compareTo(in);
+ if (min) {
+ val = cmp > 0 ? in : val;
+ } else {
+ val = cmp < 0 ? in : val;
+ }
+ }
}
}
@@ -791,182 +790,6 @@ public class Accumulators {
}
}
- private static class IntMinMax implements Accumulator {
- public static final Supplier<Accumulator> MIN_FACTORY = () -> new
IntMinMax(true);
-
- public static final Supplier<Accumulator> MAX_FACTORY = () -> new
IntMinMax(false);
-
- private final boolean min;
-
- private int val;
-
- private boolean empty = true;
-
- private IntMinMax(boolean min) {
- this.min = min;
- }
-
- /** {@inheritDoc} */
- @Override
- public void add(Object... args) {
- Integer in = (Integer) args[0];
-
- if (in == null) {
- return;
- }
-
- val = empty ? in : min ? Math.min(val, in) : Math.max(val, in);
- empty = false;
- }
-
- /** {@inheritDoc} */
- @Override
- public void apply(Accumulator other) {
- IntMinMax other0 = (IntMinMax) other;
-
- if (other0.empty) {
- return;
- }
-
- val = empty ? other0.val : min ? Math.min(val, other0.val) :
Math.max(val, other0.val);
- empty = false;
- }
-
- /** {@inheritDoc} */
- @Override
- public Object end() {
- return empty ? null : val;
- }
-
- /** {@inheritDoc} */
- @Override
- public List<RelDataType> argumentTypes(IgniteTypeFactory typeFactory) {
- return
List.of(typeFactory.createTypeWithNullability(typeFactory.createSqlType(INTEGER),
true));
- }
-
- /** {@inheritDoc} */
- @Override
- public RelDataType returnType(IgniteTypeFactory typeFactory) {
- return
typeFactory.createTypeWithNullability(typeFactory.createSqlType(INTEGER), true);
- }
- }
-
- private static class LongMinMax implements Accumulator {
- public static final Supplier<Accumulator> MIN_FACTORY = () -> new
LongMinMax(true);
-
- public static final Supplier<Accumulator> MAX_FACTORY = () -> new
LongMinMax(false);
-
- private final boolean min;
-
- private long val;
-
- private boolean empty = true;
-
- private LongMinMax(boolean min) {
- this.min = min;
- }
-
- /** {@inheritDoc} */
- @Override
- public void add(Object... args) {
- Long in = (Long) args[0];
-
- if (in == null) {
- return;
- }
-
- val = empty ? in : min ? Math.min(val, in) : Math.max(val, in);
- empty = false;
- }
-
- /** {@inheritDoc} */
- @Override
- public void apply(Accumulator other) {
- LongMinMax other0 = (LongMinMax) other;
-
- if (other0.empty) {
- return;
- }
-
- val = empty ? other0.val : min ? Math.min(val, other0.val) :
Math.max(val, other0.val);
- empty = false;
- }
-
- /** {@inheritDoc} */
- @Override
- public Object end() {
- return empty ? null : val;
- }
-
- /** {@inheritDoc} */
- @Override
- public List<RelDataType> argumentTypes(IgniteTypeFactory typeFactory) {
- return
List.of(typeFactory.createTypeWithNullability(typeFactory.createSqlType(BIGINT),
true));
- }
-
- /** {@inheritDoc} */
- @Override
- public RelDataType returnType(IgniteTypeFactory typeFactory) {
- return
typeFactory.createTypeWithNullability(typeFactory.createSqlType(BIGINT), true);
- }
- }
-
- private static class DecimalMinMax implements Accumulator {
- public static final Supplier<Accumulator> MIN_FACTORY = () -> new
DecimalMinMax(true);
-
- public static final Supplier<Accumulator> MAX_FACTORY = () -> new
DecimalMinMax(false);
-
- private final boolean min;
-
- private BigDecimal val;
-
- private DecimalMinMax(boolean min) {
- this.min = min;
- }
-
- /** {@inheritDoc} */
- @Override
- public void add(Object... args) {
- BigDecimal in = (BigDecimal) args[0];
-
- if (in == null) {
- return;
- }
-
- val = val == null ? in : min ? val.min(in) : val.max(in);
- }
-
- /** {@inheritDoc} */
- @Override
- public void apply(Accumulator other) {
- DecimalMinMax other0 = (DecimalMinMax) other;
-
- if (other0.val == null) {
- return;
- }
-
- val = val == null ? other0.val : min ? val.min(other0.val) :
val.max(other0.val);
- }
-
- /** {@inheritDoc} */
- @Override
- public Object end() {
- return val;
- }
-
- /** {@inheritDoc} */
- @Override
- public List<RelDataType> argumentTypes(IgniteTypeFactory typeFactory) {
- return
List.of(typeFactory.createTypeWithNullability(typeFactory.createSqlType(DECIMAL),
true));
- }
-
- /** {@inheritDoc} */
- @Override
- public RelDataType returnType(IgniteTypeFactory typeFactory) {
- return
typeFactory.createTypeWithNullability(typeFactory.createSqlType(DECIMAL), true);
- }
- }
-
private static class DistinctAccumulator implements Accumulator {
private final Accumulator acc;
diff --git
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/agg/AccumulatorsFactory.java
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/agg/AccumulatorsFactory.java
index 0b4ea007cf..455f38383e 100644
---
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/agg/AccumulatorsFactory.java
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/agg/AccumulatorsFactory.java
@@ -150,7 +150,8 @@ public class AccumulatorsFactory<RowT> implements
Supplier<List<AccumulatorWrapp
this.type = type;
this.inputRowType = inputRowType;
- prototypes = Commons.transform(aggCalls, WrapperPrototype::new);
+ var accumulators = new Accumulators(ctx.getTypeFactory());
+ prototypes = Commons.transform(aggCalls, call -> new
WrapperPrototype(accumulators, call));
}
/** {@inheritDoc} */
@@ -162,13 +163,16 @@ public class AccumulatorsFactory<RowT> implements
Supplier<List<AccumulatorWrapp
private final class WrapperPrototype implements
Supplier<AccumulatorWrapper<RowT>> {
private Supplier<Accumulator> accFactory;
+ private final Accumulators accumulators;
+
private final AggregateCall call;
private Function<Object[], Object[]> inAdapter;
private Function<Object, Object> outAdapter;
- private WrapperPrototype(AggregateCall call) {
+ private WrapperPrototype(Accumulators accumulators, AggregateCall
call) {
+ this.accumulators = accumulators;
this.call = call;
}
@@ -187,7 +191,7 @@ public class AccumulatorsFactory<RowT> implements
Supplier<List<AccumulatorWrapp
}
// init factory and adapters
- accFactory = Accumulators.accumulatorFactory(call);
+ accFactory = accumulators.accumulatorFactory(call);
Accumulator accumulator = accFactory.get();
inAdapter = createInAdapter(accumulator);