From d4ef0f6d94f6e764bc86db4dee67931f270f97e0 Mon Sep 17 00:00:00 2001 From: Gian Merlino Date: Tue, 25 Jul 2017 13:54:44 -0700 Subject: [PATCH] Improved SQL support for floats and doubles. (#4598) * Improved SQL support for floats and doubles. - Use Druid FLOAT for SQL FLOAT, and Druid DOUBLE for SQL DOUBLE, REAL, and DECIMAL. - Use float* aggregators when appropriate. - Add tests involving both float and double columns. - Adjust documentation accordingly. * CR comments. * Fix braces. --- docs/content/querying/sql.md | 20 +- .../sql/calcite/expression/Expressions.java | 47 ++-- .../druid/sql/calcite/planner/Calcites.java | 11 +- .../sql/calcite/planner/DruidTypeSystem.java | 107 ++++++++ .../sql/calcite/planner/PlannerFactory.java | 1 + .../io/druid/sql/calcite/rel/QueryMaker.java | 34 +-- .../druid/sql/calcite/rule/GroupByRules.java | 157 ++++++++++-- .../sql/avatica/DruidAvaticaHandlerTest.java | 8 + .../druid/sql/avatica/DruidStatementTest.java | 27 +- .../druid/sql/calcite/CalciteQueryTest.java | 232 ++++++++++++------ .../druid/sql/calcite/util/CalciteTests.java | 28 ++- 11 files changed, 502 insertions(+), 170 deletions(-) create mode 100644 sql/src/main/java/io/druid/sql/calcite/planner/DruidTypeSystem.java diff --git a/docs/content/querying/sql.md b/docs/content/querying/sql.md index 6b641ce800b..22ca7a1ddc9 100644 --- a/docs/content/querying/sql.md +++ b/docs/content/querying/sql.md @@ -207,13 +207,13 @@ Additionally, some Druid features are not supported by the SQL language. Some un ## Data types and casts -Druid natively supports four main column types: "long" (64 bit signed int), "float" (32 bit float), "string" (UTF-8 -encoded strings), and "complex" (catch-all for more exotic data types like hyperUnique and approxHistogram columns). -Timestamps (including the `__time` column) are stored as longs, with the value being the number of milliseconds since 1 -January 1970 UTC. +Druid natively supports five basic column types: "long" (64 bit signed int), "float" (32 bit float), "double" (64 bit +float) "string" (UTF-8 encoded strings), and "complex" (catch-all for more exotic data types like hyperUnique and +approxHistogram columns). Timestamps (including the `__time` column) are stored as longs, with the value being the +number of milliseconds since 1 January 1970 UTC. -At runtime, Druid will widen floats to "double" (64 bit float) for certain features, like `SUM` aggregators. But this -widening is not universal; some floating point operations retain 32 bit precision. +At runtime, Druid may widen 32-bit floats to 64-bit for certain operators, like SUM aggregators. The reverse will not +happen: 64-bit floats are not be narrowed to 32-bit. Druid generally treats NULLs and empty strings interchangeably, rather than according to the SQL standard. As such, Druid SQL only has partial support for NULLs. For example, the expressions `col IS NULL` and `col = ''` are equivalent, @@ -238,10 +238,10 @@ converted to zeroes). |--------|------------------|-------------|-----| |CHAR|STRING|`''`|| |VARCHAR|STRING|`''`|Druid STRING columns are reported as VARCHAR| -|DECIMAL|FLOAT or DOUBLE|`0.0`|DECIMAL uses floating point, not fixed point math| -|FLOAT|FLOAT or DOUBLE|`0.0`|Druid FLOAT columns are reported as FLOAT| -|REAL|FLOAT or DOUBLE|`0.0`|| -|DOUBLE|FLOAT or DOUBLE|`0.0`|| +|DECIMAL|DOUBLE|`0.0`|DECIMAL uses floating point, not fixed point math| +|FLOAT|FLOAT|`0.0`|Druid FLOAT columns are reported as FLOAT| +|REAL|DOUBLE|`0.0`|| +|DOUBLE|DOUBLE|`0.0`|Druid DOUBLE columns are reported as DOUBLE| |BOOLEAN|LONG|`false`|| |TINYINT|LONG|`0`|| |SMALLINT|LONG|`0`|| diff --git a/sql/src/main/java/io/druid/sql/calcite/expression/Expressions.java b/sql/src/main/java/io/druid/sql/calcite/expression/Expressions.java index f03c8901109..770844184c2 100644 --- a/sql/src/main/java/io/druid/sql/calcite/expression/Expressions.java +++ b/sql/src/main/java/io/druid/sql/calcite/expression/Expressions.java @@ -40,6 +40,7 @@ import io.druid.query.filter.OrDimFilter; import io.druid.query.ordering.StringComparator; import io.druid.query.ordering.StringComparators; import io.druid.segment.column.Column; +import io.druid.segment.column.ValueType; import io.druid.sql.calcite.filtration.BoundRefKey; import io.druid.sql.calcite.filtration.Bounds; import io.druid.sql.calcite.filtration.Filtration; @@ -121,33 +122,6 @@ public class Expressions .put(SqlStdOperatorTable.OR, "||") .build(); - private static final Map EXPRESSION_TYPES; - - static { - final ImmutableMap.Builder builder = ImmutableMap.builder(); - - for (SqlTypeName type : SqlTypeName.FRACTIONAL_TYPES) { - builder.put(type, ExprType.DOUBLE); - } - - for (SqlTypeName type : SqlTypeName.INT_TYPES) { - builder.put(type, ExprType.LONG); - } - - for (SqlTypeName type : SqlTypeName.STRING_TYPES) { - builder.put(type, ExprType.STRING); - } - - // Booleans are treated as longs in Druid expressions, using two-value logic (positive = true, nonpositive = false). - builder.put(SqlTypeName.BOOLEAN, ExprType.LONG); - - // Timestamps are treated as longs (millis since the epoch) in Druid expressions. - builder.put(SqlTypeName.TIMESTAMP, ExprType.LONG); - builder.put(SqlTypeName.DATE, ExprType.LONG); - - EXPRESSION_TYPES = builder.build(); - } - private Expressions() { // No instantiation. @@ -277,8 +251,8 @@ public class Expressions ); } else { // Handle other casts. - final ExprType fromExprType = EXPRESSION_TYPES.get(fromType); - final ExprType toExprType = EXPRESSION_TYPES.get(toType); + final ExprType fromExprType = exprTypeForValueType(Calcites.getValueTypeForSqlTypeName(fromType)); + final ExprType toExprType = exprTypeForValueType(Calcites.getValueTypeForSqlTypeName(toType)); if (fromExprType == null || toExprType == null) { // We have no runtime type for these SQL types. @@ -644,6 +618,21 @@ public class Expressions } } + public static ExprType exprTypeForValueType(final ValueType valueType) + { + switch (valueType) { + case LONG: + return ExprType.LONG; + case FLOAT: + case DOUBLE: + return ExprType.DOUBLE; + case STRING: + return ExprType.STRING; + default: + throw new ISE("No ExprType for valueType[%s]", valueType); + } + } + /** * Translates to an "expression" type leaf filter. Used as a fallback if we can't use a simple leaf filter. */ diff --git a/sql/src/main/java/io/druid/sql/calcite/planner/Calcites.java b/sql/src/main/java/io/druid/sql/calcite/planner/Calcites.java index 84bf943c8a3..afbfac840fc 100644 --- a/sql/src/main/java/io/druid/sql/calcite/planner/Calcites.java +++ b/sql/src/main/java/io/druid/sql/calcite/planner/Calcites.java @@ -22,6 +22,7 @@ package io.druid.sql.calcite.planner; import com.google.common.io.BaseEncoding; import com.google.common.primitives.Chars; import io.druid.java.util.common.IAE; +import io.druid.java.util.common.ISE; import io.druid.java.util.common.StringUtils; import io.druid.query.ordering.StringComparator; import io.druid.query.ordering.StringComparators; @@ -110,8 +111,10 @@ public class Calcites public static ValueType getValueTypeForSqlTypeName(SqlTypeName sqlTypeName) { - if (SqlTypeName.FRACTIONAL_TYPES.contains(sqlTypeName)) { + if (SqlTypeName.FLOAT == sqlTypeName) { return ValueType.FLOAT; + } else if (SqlTypeName.FRACTIONAL_TYPES.contains(sqlTypeName)) { + return ValueType.DOUBLE; } else if (SqlTypeName.TIMESTAMP == sqlTypeName || SqlTypeName.DATE == sqlTypeName || SqlTypeName.BOOLEAN == sqlTypeName @@ -129,10 +132,12 @@ public class Calcites public static StringComparator getStringComparatorForSqlTypeName(SqlTypeName sqlTypeName) { final ValueType valueType = getValueTypeForSqlTypeName(sqlTypeName); - if (valueType == ValueType.LONG || valueType == ValueType.FLOAT) { + if (ValueType.isNumeric(valueType)) { return StringComparators.NUMERIC; - } else { + } else if (ValueType.STRING == valueType) { return StringComparators.LEXICOGRAPHIC; + } else { + throw new ISE("Unrecognized valueType[%s]", valueType); } } diff --git a/sql/src/main/java/io/druid/sql/calcite/planner/DruidTypeSystem.java b/sql/src/main/java/io/druid/sql/calcite/planner/DruidTypeSystem.java new file mode 100644 index 00000000000..ea9281ecc37 --- /dev/null +++ b/sql/src/main/java/io/druid/sql/calcite/planner/DruidTypeSystem.java @@ -0,0 +1,107 @@ +/* + * Licensed to Metamarkets Group Inc. (Metamarkets) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. Metamarkets licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.druid.sql.calcite.planner; + +import org.apache.calcite.rel.type.RelDataType; +import org.apache.calcite.rel.type.RelDataTypeFactory; +import org.apache.calcite.rel.type.RelDataTypeSystem; +import org.apache.calcite.sql.type.SqlTypeName; + +public class DruidTypeSystem implements RelDataTypeSystem +{ + public static final DruidTypeSystem INSTANCE = new DruidTypeSystem(); + + private DruidTypeSystem() + { + // Singleton. + } + + @Override + public int getMaxScale(final SqlTypeName typeName) + { + return RelDataTypeSystem.DEFAULT.getMaxScale(typeName); + } + + @Override + public int getDefaultPrecision(final SqlTypeName typeName) + { + return RelDataTypeSystem.DEFAULT.getDefaultPrecision(typeName); + } + + @Override + public int getMaxPrecision(final SqlTypeName typeName) + { + return RelDataTypeSystem.DEFAULT.getMaxPrecision(typeName); + } + + @Override + public int getMaxNumericScale() + { + return RelDataTypeSystem.DEFAULT.getMaxNumericScale(); + } + + @Override + public int getMaxNumericPrecision() + { + return RelDataTypeSystem.DEFAULT.getMaxNumericPrecision(); + } + + @Override + public String getLiteral(final SqlTypeName typeName, final boolean isPrefix) + { + return RelDataTypeSystem.DEFAULT.getLiteral(typeName, isPrefix); + } + + @Override + public boolean isCaseSensitive(final SqlTypeName typeName) + { + return RelDataTypeSystem.DEFAULT.isCaseSensitive(typeName); + } + + @Override + public boolean isAutoincrement(final SqlTypeName typeName) + { + return RelDataTypeSystem.DEFAULT.isAutoincrement(typeName); + } + + @Override + public int getNumTypeRadix(final SqlTypeName typeName) + { + return RelDataTypeSystem.DEFAULT.getNumTypeRadix(typeName); + } + + @Override + public RelDataType deriveSumType(final RelDataTypeFactory typeFactory, final RelDataType argumentType) + { + // Widen all sums to 64-bits regardless of the size of the inputs. + + if (SqlTypeName.INT_TYPES.contains(argumentType.getSqlTypeName())) { + return typeFactory.createSqlType(SqlTypeName.BIGINT); + } else { + return typeFactory.createSqlType(SqlTypeName.DOUBLE); + } + } + + @Override + public boolean isSchemaCaseSensitive() + { + return RelDataTypeSystem.DEFAULT.isSchemaCaseSensitive(); + } +} diff --git a/sql/src/main/java/io/druid/sql/calcite/planner/PlannerFactory.java b/sql/src/main/java/io/druid/sql/calcite/planner/PlannerFactory.java index 105c08809de..d75adc5bfb6 100644 --- a/sql/src/main/java/io/druid/sql/calcite/planner/PlannerFactory.java +++ b/sql/src/main/java/io/druid/sql/calcite/planner/PlannerFactory.java @@ -86,6 +86,7 @@ public class PlannerFactory .context(Contexts.EMPTY_CONTEXT) .typeSystem(RelDataTypeSystem.DEFAULT) .defaultSchema(rootSchema.getSubSchema(DruidSchema.NAME)) + .typeSystem(DruidTypeSystem.INSTANCE) .build(); return new DruidPlanner(Frameworks.getPlanner(frameworkConfig), plannerContext); diff --git a/sql/src/main/java/io/druid/sql/calcite/rel/QueryMaker.java b/sql/src/main/java/io/druid/sql/calcite/rel/QueryMaker.java index d9ff5a2641b..2faf3db2bd8 100644 --- a/sql/src/main/java/io/druid/sql/calcite/rel/QueryMaker.java +++ b/sql/src/main/java/io/druid/sql/calcite/rel/QueryMaker.java @@ -23,9 +23,7 @@ import com.google.common.base.Function; import com.google.common.base.Preconditions; import com.google.common.base.Strings; import com.google.common.collect.Iterables; -import com.google.common.primitives.Doubles; import com.google.common.primitives.Ints; -import io.druid.common.guava.GuavaUtils; import io.druid.data.input.Row; import io.druid.java.util.common.ISE; import io.druid.java.util.common.guava.Sequence; @@ -45,6 +43,7 @@ import io.druid.query.timeseries.TimeseriesResultValue; import io.druid.query.topn.DimensionAndMetricValueExtractor; import io.druid.query.topn.TopNQuery; import io.druid.query.topn.TopNResultValue; +import io.druid.segment.DimensionHandlerUtils; import io.druid.segment.column.Column; import io.druid.server.QueryLifecycleFactory; import io.druid.sql.calcite.planner.Calcites; @@ -346,7 +345,9 @@ public class QueryMaker return ColumnMetaData.Rep.of(Integer.class); } else if (sqlType == SqlTypeName.BIGINT) { return ColumnMetaData.Rep.of(Long.class); - } else if (sqlType == SqlTypeName.FLOAT || sqlType == SqlTypeName.DOUBLE || sqlType == SqlTypeName.DECIMAL) { + } else if (sqlType == SqlTypeName.FLOAT) { + return ColumnMetaData.Rep.of(Float.class); + } else if (sqlType == SqlTypeName.DOUBLE || sqlType == SqlTypeName.DECIMAL) { return ColumnMetaData.Rep.of(Double.class); } else if (sqlType == SqlTypeName.OTHER) { return ColumnMetaData.Rep.of(Object.class); @@ -416,19 +417,24 @@ public class QueryMaker throw new ISE("Cannot coerce[%s] to %s", value.getClass().getName(), sqlType); } } else if (sqlType == SqlTypeName.BIGINT) { - if (value instanceof String) { - coercedValue = GuavaUtils.tryParseLong((String) value); - } else if (value instanceof Number) { - coercedValue = ((Number) value).longValue(); - } else { + try { + coercedValue = DimensionHandlerUtils.convertObjectToLong(value); + } + catch (Exception e) { throw new ISE("Cannot coerce[%s] to %s", value.getClass().getName(), sqlType); } - } else if (sqlType == SqlTypeName.FLOAT || sqlType == SqlTypeName.DOUBLE || sqlType == SqlTypeName.DECIMAL) { - if (value instanceof String) { - coercedValue = Doubles.tryParse((String) value); - } else if (value instanceof Number) { - coercedValue = ((Number) value).doubleValue(); - } else { + } else if (sqlType == SqlTypeName.FLOAT) { + try { + coercedValue = DimensionHandlerUtils.convertObjectToFloat(value); + } + catch (Exception e) { + throw new ISE("Cannot coerce[%s] to %s", value.getClass().getName(), sqlType); + } + } else if (SqlTypeName.FRACTIONAL_TYPES.contains(sqlType)) { + try { + coercedValue = DimensionHandlerUtils.convertObjectToDouble(value); + } + catch (Exception e) { throw new ISE("Cannot coerce[%s] to %s", value.getClass().getName(), sqlType); } } else if (sqlType == SqlTypeName.OTHER) { diff --git a/sql/src/main/java/io/druid/sql/calcite/rule/GroupByRules.java b/sql/src/main/java/io/druid/sql/calcite/rule/GroupByRules.java index 71dcd82590f..4e19c617cbf 100644 --- a/sql/src/main/java/io/druid/sql/calcite/rule/GroupByRules.java +++ b/sql/src/main/java/io/druid/sql/calcite/rule/GroupByRules.java @@ -26,11 +26,15 @@ import com.google.common.collect.Lists; import com.google.common.collect.Maps; import io.druid.java.util.common.ISE; import io.druid.math.expr.ExprMacroTable; +import io.druid.math.expr.ExprType; import io.druid.query.aggregation.AggregatorFactory; import io.druid.query.aggregation.CountAggregatorFactory; import io.druid.query.aggregation.DoubleMaxAggregatorFactory; import io.druid.query.aggregation.DoubleMinAggregatorFactory; import io.druid.query.aggregation.DoubleSumAggregatorFactory; +import io.druid.query.aggregation.FloatMaxAggregatorFactory; +import io.druid.query.aggregation.FloatMinAggregatorFactory; +import io.druid.query.aggregation.FloatSumAggregatorFactory; import io.druid.query.aggregation.LongMaxAggregatorFactory; import io.druid.query.aggregation.LongMinAggregatorFactory; import io.druid.query.aggregation.LongSumAggregatorFactory; @@ -477,23 +481,19 @@ public class GroupByRules // Walk through the postProject expressions. int projectPostAggregatorCount = 0; - for (final RexNode projectExpression : postProject.getChildExps()) { + for (final RexNode postAggregatorRexNode : postProject.getChildExps()) { // Attempt to convert to PostAggregator. final DruidExpression postAggregatorExpression = Expressions.toDruidExpression( druidRel.getPlannerContext(), druidRel.getOutputRowSignature(), - projectExpression + postAggregatorRexNode ); if (postAggregatorExpression == null) { return null; } - if (postAggregatorExpression.isDirectColumnAccess() - && druidRel.getQueryBuilder() - .getOutputRowSignature() - .getColumnType(postAggregatorExpression.getDirectColumn()) - .equals(Calcites.getValueTypeForSqlTypeName(projectExpression.getType().getSqlTypeName()))) { + if (postAggregatorDirectColumnIsOk(druidRel, postAggregatorExpression, postAggregatorRexNode)) { // Direct column access, without any type cast as far as Druid's runtime is concerned. // (There might be a SQL-level type cast that we don't care about) newRowOrder.add(postAggregatorExpression.getDirectColumn()); @@ -523,6 +523,40 @@ public class GroupByRules ); } + /** + * Returns true if a post-aggregation "expression" can be realized as a direct field access. This is true if it's + * a direct column access that doesn't require an implicit cast. + * + * @param druidRel druid aggregation rel + * @param expression post-aggregation expression + * @param rexNode RexNode for the post-aggregation expression + * + * @return + */ + private static boolean postAggregatorDirectColumnIsOk( + final DruidRel druidRel, + final DruidExpression expression, + final RexNode rexNode + ) + { + if (!expression.isDirectColumnAccess()) { + return false; + } + + // Check if a cast is necessary. + final ExprType toExprType = Expressions.exprTypeForValueType( + druidRel.getQueryBuilder() + .getOutputRowSignature() + .getColumnType(expression.getDirectColumn()) + ); + + final ExprType fromExprType = Expressions.exprTypeForValueType( + Calcites.getValueTypeForSqlTypeName(rexNode.getType().getSqlTypeName()) + ); + + return toExprType.equals(fromExprType); + } + private static boolean canApplyHaving(final DruidRel druidRel) { return druidRel.getQueryBuilder().getGrouping() != null @@ -779,9 +813,24 @@ public class GroupByRules // Built-in aggregator that is not COUNT. final Aggregation retVal; - final boolean isLong = SqlTypeName.INT_TYPES.contains(outputType) - || SqlTypeName.TIMESTAMP == outputType - || SqlTypeName.DATE == outputType; + final ValueType aggregationType; + + if (SqlTypeName.INT_TYPES.contains(outputType) + || SqlTypeName.TIMESTAMP == outputType + || SqlTypeName.DATE == outputType) { + aggregationType = ValueType.LONG; + } else if (SqlTypeName.FLOAT == outputType) { + aggregationType = ValueType.FLOAT; + } else if (SqlTypeName.FRACTIONAL_TYPES.contains(outputType)) { + aggregationType = ValueType.DOUBLE; + } else { + throw new ISE( + "Cannot determine aggregation type for SQL operator[%s] type[%s]", + call.getAggregation().getName(), + outputType + ); + } + final String fieldName; final String expression; final ExprMacroTable macroTable = plannerContext.getExprMacroTable(); @@ -795,23 +844,27 @@ public class GroupByRules } if (kind == SqlKind.SUM || kind == SqlKind.SUM0) { - retVal = isLong - ? Aggregation.create(new LongSumAggregatorFactory(name, fieldName, expression, macroTable)) - : Aggregation.create(new DoubleSumAggregatorFactory(name, fieldName, expression, macroTable)); + retVal = Aggregation.create( + createSumAggregatorFactory(aggregationType, name, fieldName, expression, macroTable) + ); } else if (kind == SqlKind.MIN) { - retVal = isLong - ? Aggregation.create(new LongMinAggregatorFactory(name, fieldName, expression, macroTable)) - : Aggregation.create(new DoubleMinAggregatorFactory(name, fieldName, expression, macroTable)); + retVal = Aggregation.create( + createMinAggregatorFactory(aggregationType, name, fieldName, expression, macroTable) + ); } else if (kind == SqlKind.MAX) { - retVal = isLong - ? Aggregation.create(new LongMaxAggregatorFactory(name, fieldName, expression, macroTable)) - : Aggregation.create(new DoubleMaxAggregatorFactory(name, fieldName, expression, macroTable)); + retVal = Aggregation.create( + createMaxAggregatorFactory(aggregationType, name, fieldName, expression, macroTable) + ); } else if (kind == SqlKind.AVG) { final String sumName = String.format("%s:sum", name); final String countName = String.format("%s:count", name); - final AggregatorFactory sum = isLong - ? new LongSumAggregatorFactory(sumName, fieldName, expression, macroTable) - : new DoubleSumAggregatorFactory(sumName, fieldName, expression, macroTable); + final AggregatorFactory sum = createSumAggregatorFactory( + aggregationType, + sumName, + fieldName, + expression, + macroTable + ); final AggregatorFactory count = new CountAggregatorFactory(countName); retVal = Aggregation.create( ImmutableList.of(sum, count), @@ -881,4 +934,64 @@ public class GroupByRules .optimizeFilterOnly(sourceRowSignature) .getDimFilter(); } + + private static AggregatorFactory createSumAggregatorFactory( + final ValueType aggregationType, + final String name, + final String fieldName, + final String expression, + final ExprMacroTable macroTable + ) + { + switch (aggregationType) { + case LONG: + return new LongSumAggregatorFactory(name, fieldName, expression, macroTable); + case FLOAT: + return new FloatSumAggregatorFactory(name, fieldName, expression, macroTable); + case DOUBLE: + return new DoubleSumAggregatorFactory(name, fieldName, expression, macroTable); + default: + throw new ISE("Cannot create aggregator factory for type[%s]", aggregationType); + } + } + + private static AggregatorFactory createMinAggregatorFactory( + final ValueType aggregationType, + final String name, + final String fieldName, + final String expression, + final ExprMacroTable macroTable + ) + { + switch (aggregationType) { + case LONG: + return new LongMinAggregatorFactory(name, fieldName, expression, macroTable); + case FLOAT: + return new FloatMinAggregatorFactory(name, fieldName, expression, macroTable); + case DOUBLE: + return new DoubleMinAggregatorFactory(name, fieldName, expression, macroTable); + default: + throw new ISE("Cannot create aggregator factory for type[%s]", aggregationType); + } + } + + private static AggregatorFactory createMaxAggregatorFactory( + final ValueType aggregationType, + final String name, + final String fieldName, + final String expression, + final ExprMacroTable macroTable + ) + { + switch (aggregationType) { + case LONG: + return new LongMaxAggregatorFactory(name, fieldName, expression, macroTable); + case FLOAT: + return new FloatMaxAggregatorFactory(name, fieldName, expression, macroTable); + case DOUBLE: + return new DoubleMaxAggregatorFactory(name, fieldName, expression, macroTable); + default: + throw new ISE("Cannot create aggregator factory for type[%s]", aggregationType); + } + } } diff --git a/sql/src/test/java/io/druid/sql/avatica/DruidAvaticaHandlerTest.java b/sql/src/test/java/io/druid/sql/avatica/DruidAvaticaHandlerTest.java index da84d198f80..bd96ce11057 100644 --- a/sql/src/test/java/io/druid/sql/avatica/DruidAvaticaHandlerTest.java +++ b/sql/src/test/java/io/druid/sql/avatica/DruidAvaticaHandlerTest.java @@ -355,6 +355,14 @@ public class DruidAvaticaHandlerTest Pair.of("TABLE_SCHEM", "druid"), Pair.of("TABLE_NAME", "foo"), Pair.of("COLUMN_NAME", "m1"), + Pair.of("DATA_TYPE", Types.FLOAT), + Pair.of("TYPE_NAME", "FLOAT"), + Pair.of("IS_NULLABLE", "NO") + ), + ROW( + Pair.of("TABLE_SCHEM", "druid"), + Pair.of("TABLE_NAME", "foo"), + Pair.of("COLUMN_NAME", "m2"), Pair.of("DATA_TYPE", Types.DOUBLE), Pair.of("TYPE_NAME", "DOUBLE"), Pair.of("IS_NULLABLE", "NO") diff --git a/sql/src/test/java/io/druid/sql/avatica/DruidStatementTest.java b/sql/src/test/java/io/druid/sql/avatica/DruidStatementTest.java index a3757ebd763..af10663a8a3 100644 --- a/sql/src/test/java/io/druid/sql/avatica/DruidStatementTest.java +++ b/sql/src/test/java/io/druid/sql/avatica/DruidStatementTest.java @@ -98,7 +98,8 @@ public class DruidStatementTest Lists.newArrayList("cnt", "BIGINT", "java.lang.Long"), Lists.newArrayList("dim1", "VARCHAR", "java.lang.String"), Lists.newArrayList("dim2", "VARCHAR", "java.lang.String"), - Lists.newArrayList("m1", "DOUBLE", "java.lang.Double"), + Lists.newArrayList("m1", "FLOAT", "java.lang.Float"), + Lists.newArrayList("m2", "DOUBLE", "java.lang.Double"), Lists.newArrayList("unique_dim1", "OTHER", "java.lang.Object") ), Lists.transform( @@ -132,12 +133,12 @@ public class DruidStatementTest 0, true, Lists.newArrayList( - new Object[]{new DateTime("2000-01-01").getMillis(), 1L, "", "a", 1.0}, - new Object[]{new DateTime("2000-01-02").getMillis(), 1L, "10.1", "", 2.0}, - new Object[]{new DateTime("2000-01-03").getMillis(), 1L, "2", "", 3.0}, - new Object[]{new DateTime("2001-01-01").getMillis(), 1L, "1", "a", 4.0}, - new Object[]{new DateTime("2001-01-02").getMillis(), 1L, "def", "abc", 5.0}, - new Object[]{new DateTime("2001-01-03").getMillis(), 1L, "abc", "", 6.0} + new Object[]{new DateTime("2000-01-01").getMillis(), 1L, "", "a", 1.0f}, + new Object[]{new DateTime("2000-01-02").getMillis(), 1L, "10.1", "", 2.0f}, + new Object[]{new DateTime("2000-01-03").getMillis(), 1L, "2", "", 3.0f}, + new Object[]{new DateTime("2001-01-01").getMillis(), 1L, "1", "a", 4.0f}, + new Object[]{new DateTime("2001-01-02").getMillis(), 1L, "def", "abc", 5.0f}, + new Object[]{new DateTime("2001-01-03").getMillis(), 1L, "abc", "", 6.0f} ) ), frame @@ -158,8 +159,8 @@ public class DruidStatementTest 0, false, Lists.newArrayList( - new Object[]{new DateTime("2000-01-01").getMillis(), 1L, "", "a", 1.0}, - new Object[]{new DateTime("2000-01-02").getMillis(), 1L, "10.1", "", 2.0} + new Object[]{new DateTime("2000-01-01").getMillis(), 1L, "", "a", 1.0f}, + new Object[]{new DateTime("2000-01-02").getMillis(), 1L, "10.1", "", 2.0f} ) ), frame @@ -173,10 +174,10 @@ public class DruidStatementTest 2, true, Lists.newArrayList( - new Object[]{new DateTime("2000-01-03").getMillis(), 1L, "2", "", 3.0}, - new Object[]{new DateTime("2001-01-01").getMillis(), 1L, "1", "a", 4.0}, - new Object[]{new DateTime("2001-01-02").getMillis(), 1L, "def", "abc", 5.0}, - new Object[]{new DateTime("2001-01-03").getMillis(), 1L, "abc", "", 6.0} + new Object[]{new DateTime("2000-01-03").getMillis(), 1L, "2", "", 3.0f}, + new Object[]{new DateTime("2001-01-01").getMillis(), 1L, "1", "a", 4.0f}, + new Object[]{new DateTime("2001-01-02").getMillis(), 1L, "def", "abc", 5.0f}, + new Object[]{new DateTime("2001-01-03").getMillis(), 1L, "abc", "", 6.0f} ) ), frame diff --git a/sql/src/test/java/io/druid/sql/calcite/CalciteQueryTest.java b/sql/src/test/java/io/druid/sql/calcite/CalciteQueryTest.java index ebf93328300..156ed170bb3 100644 --- a/sql/src/test/java/io/druid/sql/calcite/CalciteQueryTest.java +++ b/sql/src/test/java/io/druid/sql/calcite/CalciteQueryTest.java @@ -36,9 +36,10 @@ import io.druid.query.QueryDataSource; import io.druid.query.aggregation.AggregatorFactory; import io.druid.query.aggregation.CountAggregatorFactory; import io.druid.query.aggregation.DoubleMaxAggregatorFactory; -import io.druid.query.aggregation.DoubleMinAggregatorFactory; import io.druid.query.aggregation.DoubleSumAggregatorFactory; import io.druid.query.aggregation.FilteredAggregatorFactory; +import io.druid.query.aggregation.FloatMaxAggregatorFactory; +import io.druid.query.aggregation.FloatMinAggregatorFactory; import io.druid.query.aggregation.LongMaxAggregatorFactory; import io.druid.query.aggregation.LongMinAggregatorFactory; import io.druid.query.aggregation.LongSumAggregatorFactory; @@ -304,7 +305,8 @@ public class CalciteQueryTest new Object[]{"cnt", "BIGINT", "NO"}, new Object[]{"dim1", "VARCHAR", "YES"}, new Object[]{"dim2", "VARCHAR", "YES"}, - new Object[]{"m1", "DOUBLE", "NO"}, + new Object[]{"m1", "FLOAT", "NO"}, + new Object[]{"m2", "DOUBLE", "NO"}, new Object[]{"unique_dim1", "OTHER", "NO"} ) ); @@ -354,7 +356,7 @@ public class CalciteQueryTest .intervals(QSS(Filtration.eternity())) .granularity(Granularities.ALL) .dimensions(ImmutableList.of("dummy")) - .metrics(ImmutableList.of("__time", "cnt", "dim1", "dim2", "m1", "unique_dim1")) + .metrics(ImmutableList.of("__time", "cnt", "dim1", "dim2", "m1", "m2", "unique_dim1")) .pagingSpec(FIRST_PAGING_SPEC) .context(QUERY_CONTEXT_DEFAULT) .build(), @@ -363,7 +365,7 @@ public class CalciteQueryTest .intervals(QSS(Filtration.eternity())) .granularity(Granularities.ALL) .dimensions(ImmutableList.of("dummy")) - .metrics(ImmutableList.of("__time", "cnt", "dim1", "dim2", "m1", "unique_dim1")) + .metrics(ImmutableList.of("__time", "cnt", "dim1", "dim2", "m1", "m2", "unique_dim1")) .pagingSpec( new PagingSpec( ImmutableMap.of("foo_1970-01-01T00:00:00.000Z_2001-01-03T00:00:00.001Z_1", 5), @@ -375,12 +377,12 @@ public class CalciteQueryTest .build() ), ImmutableList.of( - new Object[]{T("2000-01-01"), 1L, "", "a", 1.0, HLLCV1.class.getName()}, - new Object[]{T("2000-01-02"), 1L, "10.1", "", 2.0, HLLCV1.class.getName()}, - new Object[]{T("2000-01-03"), 1L, "2", "", 3.0, HLLCV1.class.getName()}, - new Object[]{T("2001-01-01"), 1L, "1", "a", 4.0, HLLCV1.class.getName()}, - new Object[]{T("2001-01-02"), 1L, "def", "abc", 5.0, HLLCV1.class.getName()}, - new Object[]{T("2001-01-03"), 1L, "abc", "", 6.0, HLLCV1.class.getName()} + new Object[]{T("2000-01-01"), 1L, "", "a", 1f, 1.0, HLLCV1.class.getName()}, + new Object[]{T("2000-01-02"), 1L, "10.1", "", 2f, 2.0, HLLCV1.class.getName()}, + new Object[]{T("2000-01-03"), 1L, "2", "", 3f, 3.0, HLLCV1.class.getName()}, + new Object[]{T("2001-01-01"), 1L, "1", "a", 4f, 4.0, HLLCV1.class.getName()}, + new Object[]{T("2001-01-02"), 1L, "def", "abc", 5f, 5.0, HLLCV1.class.getName()}, + new Object[]{T("2001-01-03"), 1L, "abc", "", 6f, 6.0, HLLCV1.class.getName()} ) ); } @@ -430,14 +432,14 @@ public class CalciteQueryTest .intervals(QSS(Filtration.eternity())) .granularity(Granularities.ALL) .dimensions(ImmutableList.of("dummy")) - .metrics(ImmutableList.of("__time", "cnt", "dim1", "dim2", "m1", "unique_dim1")) + .metrics(ImmutableList.of("__time", "cnt", "dim1", "dim2", "m1", "m2", "unique_dim1")) .pagingSpec(FIRST_PAGING_SPEC) .context(QUERY_CONTEXT_DEFAULT) .build() ), ImmutableList.of( - new Object[]{T("2000-01-01"), 1L, "", "a", 1.0, HLLCV1.class.getName()}, - new Object[]{T("2000-01-02"), 1L, "10.1", "", 2.0, HLLCV1.class.getName()} + new Object[]{T("2000-01-01"), 1L, "", "a", 1.0f, 1.0, HLLCV1.class.getName()}, + new Object[]{T("2000-01-02"), 1L, "10.1", "", 2.0f, 2.0, HLLCV1.class.getName()} ) ); } @@ -453,15 +455,15 @@ public class CalciteQueryTest .intervals(QSS(Filtration.eternity())) .granularity(Granularities.ALL) .dimensions(ImmutableList.of("dummy")) - .metrics(ImmutableList.of("__time", "cnt", "dim1", "dim2", "m1", "unique_dim1")) + .metrics(ImmutableList.of("__time", "cnt", "dim1", "dim2", "m1", "m2", "unique_dim1")) .descending(true) .pagingSpec(FIRST_PAGING_SPEC) .context(QUERY_CONTEXT_DEFAULT) .build() ), ImmutableList.of( - new Object[]{T("2001-01-03"), 1L, "abc", "", 6.0, HLLCV1.class.getName()}, - new Object[]{T("2001-01-02"), 1L, "def", "abc", 5.0, HLLCV1.class.getName()} + new Object[]{T("2001-01-03"), 1L, "abc", "", 6f, 6d, HLLCV1.class.getName()}, + new Object[]{T("2001-01-02"), 1L, "def", "abc", 5f, 5d, HLLCV1.class.getName()} ) ); } @@ -569,7 +571,7 @@ public class CalciteQueryTest .intervals(QSS(Filtration.eternity())) .granularity(Granularities.ALL) .dimensions(ImmutableList.of("dummy")) - .metrics(ImmutableList.of("__time", "cnt", "dim1", "dim2", "m1", "unique_dim1")) + .metrics(ImmutableList.of("__time", "cnt", "dim1", "dim2", "m1", "m2", "unique_dim1")) .pagingSpec(FIRST_PAGING_SPEC) .context(QUERY_CONTEXT_DEFAULT) .build(), @@ -578,7 +580,7 @@ public class CalciteQueryTest .intervals(QSS(Filtration.eternity())) .granularity(Granularities.ALL) .dimensions(ImmutableList.of("dummy")) - .metrics(ImmutableList.of("__time", "cnt", "dim1", "dim2", "m1", "unique_dim1")) + .metrics(ImmutableList.of("__time", "cnt", "dim1", "dim2", "m1", "m2", "unique_dim1")) .pagingSpec( new PagingSpec( ImmutableMap.of("foo_1970-01-01T00:00:00.000Z_2001-01-03T00:00:00.001Z_1", 5), @@ -594,7 +596,7 @@ public class CalciteQueryTest .granularity(Granularities.ALL) .filters(NOT(SELECTOR("dim1", "", null))) .dimensions(ImmutableList.of("dummy")) - .metrics(ImmutableList.of("__time", "cnt", "dim1", "dim2", "m1", "unique_dim1")) + .metrics(ImmutableList.of("__time", "cnt", "dim1", "dim2", "m1", "m2", "unique_dim1")) .pagingSpec(FIRST_PAGING_SPEC) .context(QUERY_CONTEXT_DEFAULT) .build(), @@ -604,7 +606,7 @@ public class CalciteQueryTest .granularity(Granularities.ALL) .filters(NOT(SELECTOR("dim1", "", null))) .dimensions(ImmutableList.of("dummy")) - .metrics(ImmutableList.of("__time", "cnt", "dim1", "dim2", "m1", "unique_dim1")) + .metrics(ImmutableList.of("__time", "cnt", "dim1", "dim2", "m1", "m2", "unique_dim1")) .pagingSpec( new PagingSpec( ImmutableMap.of("foo_1970-01-01T00:00:00.000Z_2001-01-03T00:00:00.001Z_1", 4), @@ -635,8 +637,8 @@ public class CalciteQueryTest ImmutableList.of(), ImmutableList.of( new Object[]{ - "BindableProject(dim1=[$8], dim10=[$2], dim2=[$3])\n" - + " BindableJoin(condition=[=($8, $3)], joinType=[inner])\n" + "BindableProject(dim1=[$9], dim10=[$2], dim2=[$3])\n" + + " BindableJoin(condition=[=($9, $3)], joinType=[inner])\n" + " DruidQueryRel(dataSource=[foo])\n" + " DruidQueryRel(dataSource=[foo], filter=[!dim1 = ])\n" } @@ -680,6 +682,32 @@ public class CalciteQueryTest .setContext(QUERY_CONTEXT_DEFAULT) .build() ), + ImmutableList.of( + new Object[]{1.0f, 1L}, + new Object[]{2.0f, 1L}, + new Object[]{3.0f, 1L}, + new Object[]{4.0f, 1L}, + new Object[]{5.0f, 1L}, + new Object[]{6.0f, 1L} + ) + ); + } + + @Test + public void testGroupByDouble() throws Exception + { + testQuery( + "SELECT m2, COUNT(*) FROM druid.foo GROUP BY m2", + ImmutableList.of( + GroupByQuery.builder() + .setDataSource(CalciteTests.DATASOURCE1) + .setInterval(QSS(Filtration.eternity())) + .setGranularity(Granularities.ALL) + .setDimensions(DIMS(new DefaultDimensionSpec("m2", "d0", ValueType.DOUBLE))) + .setAggregatorSpecs(AGGS(new CountAggregatorFactory("a0"))) + .setContext(QUERY_CONTEXT_DEFAULT) + .build() + ), ImmutableList.of( new Object[]{1.0d, 1L}, new Object[]{2.0d, 1L}, @@ -713,7 +741,28 @@ public class CalciteQueryTest } @Test - public void testHavingOnFloat() throws Exception + public void testFilterOnDouble() throws Exception + { + testQuery( + "SELECT COUNT(*) FROM druid.foo WHERE m2 = 1.0", + ImmutableList.of( + Druids.newTimeseriesQueryBuilder() + .dataSource(CalciteTests.DATASOURCE1) + .intervals(QSS(Filtration.eternity())) + .granularity(Granularities.ALL) + .aggregators(AGGS(new CountAggregatorFactory("a0"))) + .filters(SELECTOR("m2", "1.0", null)) + .context(TIMESERIES_CONTEXT_DEFAULT) + .build() + ), + ImmutableList.of( + new Object[]{1L} + ) + ); + } + + @Test + public void testHavingOnDoubleSum() throws Exception { testQuery( "SELECT dim1, SUM(m1) AS m1_sum FROM druid.foo GROUP BY dim1 HAVING SUM(m1) > 1", @@ -751,6 +800,45 @@ public class CalciteQueryTest ); } + @Test + public void testHavingOnFloatSum() throws Exception + { + testQuery( + "SELECT dim1, CAST(SUM(m1) AS FLOAT) AS m1_sum FROM druid.foo GROUP BY dim1 HAVING CAST(SUM(m1) AS FLOAT) > 1", + ImmutableList.of( + GroupByQuery.builder() + .setDataSource(CalciteTests.DATASOURCE1) + .setInterval(QSS(Filtration.eternity())) + .setGranularity(Granularities.ALL) + .setDimensions(DIMS(new DefaultDimensionSpec("dim1", "d0"))) + .setAggregatorSpecs(AGGS(new DoubleSumAggregatorFactory("a0", "m1"))) + .setHavingSpec( + new DimFilterHavingSpec( + new BoundDimFilter( + "a0", + "1", + null, + true, + false, + false, + null, + StringComparators.NUMERIC + ) + ) + ) + .setContext(QUERY_CONTEXT_DEFAULT) + .build() + ), + ImmutableList.of( + new Object[]{"1", 4.0f}, + new Object[]{"10.1", 2.0f}, + new Object[]{"2", 3.0f}, + new Object[]{"abc", 6.0f}, + new Object[]{"def", 5.0f} + ) + ); + } + @Test public void testColumnComparison() throws Exception { @@ -771,8 +859,8 @@ public class CalciteQueryTest .build() ), ImmutableList.of( - new Object[]{"", 1.0d, 1L}, - new Object[]{"2", 3.0d, 1L} + new Object[]{"", 1.0f, 1L}, + new Object[]{"2", 3.0f, 1L} ) ); } @@ -1187,7 +1275,7 @@ public class CalciteQueryTest ) ) .dimensions(ImmutableList.of("dummy")) - .metrics(ImmutableList.of("__time", "cnt", "dim1", "dim2", "m1", "unique_dim1")) + .metrics(ImmutableList.of("__time", "cnt", "dim1", "dim2", "m1", "m2", "unique_dim1")) .context(QUERY_CONTEXT_DEFAULT) .build(), Druids.newSelectQueryBuilder() @@ -1208,14 +1296,14 @@ public class CalciteQueryTest ) ) .dimensions(ImmutableList.of("dummy")) - .metrics(ImmutableList.of("__time", "cnt", "dim1", "dim2", "m1", "unique_dim1")) + .metrics(ImmutableList.of("__time", "cnt", "dim1", "dim2", "m1", "m2", "unique_dim1")) .context(QUERY_CONTEXT_DEFAULT) .build() ), ImmutableList.of( - new Object[]{T("2000-01-01"), 1L, "", "a", 1.0, HLLCV1.class.getName()}, - new Object[]{T("2001-01-01"), 1L, "1", "a", 4.0, HLLCV1.class.getName()}, - new Object[]{T("2001-01-02"), 1L, "def", "abc", 5.0, HLLCV1.class.getName()} + new Object[]{T("2000-01-01"), 1L, "", "a", 1.0f, 1d, HLLCV1.class.getName()}, + new Object[]{T("2001-01-01"), 1L, "1", "a", 4.0f, 4d, HLLCV1.class.getName()}, + new Object[]{T("2001-01-02"), 1L, "def", "abc", 5.0f, 5d, HLLCV1.class.getName()} ) ); } @@ -1239,7 +1327,7 @@ public class CalciteQueryTest ) ) .dimensions(ImmutableList.of("dummy")) - .metrics(ImmutableList.of("__time", "cnt", "dim1", "dim2", "m1", "unique_dim1")) + .metrics(ImmutableList.of("__time", "cnt", "dim1", "dim2", "m1", "m2", "unique_dim1")) .context(QUERY_CONTEXT_DEFAULT) .build(), Druids.newSelectQueryBuilder() @@ -1260,7 +1348,7 @@ public class CalciteQueryTest ) ) .dimensions(ImmutableList.of("dummy")) - .metrics(ImmutableList.of("__time", "cnt", "dim1", "dim2", "m1", "unique_dim1")) + .metrics(ImmutableList.of("__time", "cnt", "dim1", "dim2", "m1", "m2", "unique_dim1")) .context(QUERY_CONTEXT_DEFAULT) .build(), Druids.newSelectQueryBuilder() @@ -1281,14 +1369,14 @@ public class CalciteQueryTest ) ) .dimensions(ImmutableList.of("dummy")) - .metrics(ImmutableList.of("__time", "cnt", "dim1", "dim2", "m1", "unique_dim1")) + .metrics(ImmutableList.of("__time", "cnt", "dim1", "dim2", "m1", "m2", "unique_dim1")) .context(QUERY_CONTEXT_DEFAULT) .build() ), ImmutableList.of( - new Object[]{T("2000-01-01"), 1L, "", "a", 1.0, HLLCV1.class.getName()}, - new Object[]{T("2001-01-01"), 1L, "1", "a", 4.0, HLLCV1.class.getName()}, - new Object[]{T("2001-01-02"), 1L, "def", "abc", 5.0, HLLCV1.class.getName()} + new Object[]{T("2000-01-01"), 1L, "", "a", 1.0f, 1.0d, HLLCV1.class.getName()}, + new Object[]{T("2001-01-01"), 1L, "1", "a", 4.0f, 4.0d, HLLCV1.class.getName()}, + new Object[]{T("2001-01-02"), 1L, "def", "abc", 5.0f, 5.0d, HLLCV1.class.getName()} ) ); } @@ -1762,8 +1850,8 @@ public class CalciteQueryTest .dimension(new DefaultDimensionSpec("dim1", "d0")) .metric(new InvertedTopNMetricSpec(new NumericTopNMetricSpec("p0"))) .aggregators(AGGS( - new DoubleMinAggregatorFactory("a0", "m1"), - new DoubleMaxAggregatorFactory("a1", "m1") + new FloatMinAggregatorFactory("a0", "m1"), + new FloatMaxAggregatorFactory("a1", "m1") )) .postAggregators( ImmutableList.of( @@ -1775,9 +1863,9 @@ public class CalciteQueryTest .build() ), ImmutableList.of( - new Object[]{"", 2.0}, - new Object[]{"10.1", 4.0}, - new Object[]{"2", 6.0} + new Object[]{"", 2.0f}, + new Object[]{"10.1", 4.0f}, + new Object[]{"2", 6.0f} ) ); } @@ -1798,8 +1886,8 @@ public class CalciteQueryTest .setDimensions(DIMS(new DefaultDimensionSpec("dim1", "d0"))) .setAggregatorSpecs( ImmutableList.of( - new DoubleMinAggregatorFactory("a0", "m1"), - new DoubleMaxAggregatorFactory("a1", "m1") + new FloatMinAggregatorFactory("a0", "m1"), + new FloatMaxAggregatorFactory("a1", "m1") ) ) .setPostAggregatorSpecs(ImmutableList.of(EXPRESSION_POST_AGG("p0", "(\"a0\" + \"a1\")"))) @@ -1819,9 +1907,9 @@ public class CalciteQueryTest .build() ), ImmutableList.of( - new Object[]{"", 2.0}, - new Object[]{"10.1", 4.0}, - new Object[]{"2", 6.0} + new Object[]{"", 2.0f}, + new Object[]{"10.1", 4.0f}, + new Object[]{"2", 6.0f} ) ); } @@ -1843,8 +1931,8 @@ public class CalciteQueryTest .setDimensions(DIMS(new DefaultDimensionSpec("dim1", "d0"))) .setAggregatorSpecs( ImmutableList.of( - new DoubleMinAggregatorFactory("a0", "m1"), - new DoubleMaxAggregatorFactory("a1", "m1") + new FloatMinAggregatorFactory("a0", "m1"), + new FloatMaxAggregatorFactory("a1", "m1") ) ) .setPostAggregatorSpecs( @@ -1868,9 +1956,9 @@ public class CalciteQueryTest .build() ), ImmutableList.of( - new Object[]{"", 2.0}, - new Object[]{"10.1", 4.0}, - new Object[]{"2", 6.0} + new Object[]{"", 2.0f}, + new Object[]{"10.1", 4.0f}, + new Object[]{"2", 6.0f} ) ); } @@ -1948,7 +2036,7 @@ public class CalciteQueryTest .build() ), ImmutableList.of( - new Object[]{1L, 5L, 1L, 2L, 5L, 5, 2L, 1L, 5L, 1L} + new Object[]{1L, 5L, 1L, 2L, 5L, 5L, 2L, 1L, 5L, 1L} ) ); } @@ -1986,7 +2074,7 @@ public class CalciteQueryTest .build() ), ImmutableList.of( - new Object[]{18L, 3.295836866004329, 2, 12, 3f + (Math.log(5.0))} + new Object[]{18L, 3.295836866004329, 2, 12L, 3f + (Math.log(5.0))} ) ); } @@ -2029,10 +2117,10 @@ public class CalciteQueryTest .build() ), ImmutableList.of( - new Object[]{6.0, 1L}, - new Object[]{4.0, 2L}, - new Object[]{2.0, 2L}, - new Object[]{0.0, 1L} + new Object[]{6.0f, 1L}, + new Object[]{4.0f, 2L}, + new Object[]{2.0f, 2L}, + new Object[]{0.0f, 1L} ) ); } @@ -2129,9 +2217,9 @@ public class CalciteQueryTest .build() ), ImmutableList.of( - new Object[]{10.0, 1L}, - new Object[]{2.0, 1L}, - new Object[]{0.0, 4L} + new Object[]{10.0f, 1L}, + new Object[]{2.0f, 1L}, + new Object[]{0.0f, 4L} ) ); } @@ -2616,7 +2704,7 @@ public class CalciteQueryTest .build() ), ImmutableList.of( - new Object[]{3} + new Object[]{3L} ) ); } @@ -2645,7 +2733,7 @@ public class CalciteQueryTest .build() ), ImmutableList.of( - new Object[]{3} + new Object[]{3L} ) ); } @@ -3471,7 +3559,7 @@ public class CalciteQueryTest .build() ), ImmutableList.of( - new Object[]{5L, 5L, -0.1222693591629298} + new Object[]{5L, 5L, -0.12226936f} ) ); } @@ -3606,7 +3694,7 @@ public class CalciteQueryTest .build() ), ImmutableList.of( - new Object[]{6L, 3L, 3.0021994137521975, 1L, 4L, 4.9985347983600805} + new Object[]{6L, 3L, 3.0021994f, 1L, 4L, 4.9985347f} ) ); } @@ -4094,10 +4182,10 @@ public class CalciteQueryTest .build() ), ImmutableList.of( - new Object[]{0.0, 3L}, - new Object[]{1.0, 1L}, - new Object[]{2.0, 1L}, - new Object[]{10.0, 1L} + new Object[]{0.0f, 3L}, + new Object[]{1.0f, 1L}, + new Object[]{2.0f, 1L}, + new Object[]{10.0f, 1L} ) ); } @@ -4145,10 +4233,10 @@ public class CalciteQueryTest .build() ), ImmutableList.of( - new Object[]{10.0, 1L}, - new Object[]{2.0, 1L}, - new Object[]{1.0, 1L}, - new Object[]{0.0, 3L} + new Object[]{10.0f, 1L}, + new Object[]{2.0f, 1L}, + new Object[]{1.0f, 1L}, + new Object[]{0.0f, 3L} ) ); } diff --git a/sql/src/test/java/io/druid/sql/calcite/util/CalciteTests.java b/sql/src/test/java/io/druid/sql/calcite/util/CalciteTests.java index 2b74cf52396..fc3f30e0c0a 100644 --- a/sql/src/test/java/io/druid/sql/calcite/util/CalciteTests.java +++ b/sql/src/test/java/io/druid/sql/calcite/util/CalciteTests.java @@ -54,6 +54,7 @@ import io.druid.query.QueryToolChest; import io.druid.query.QueryToolChestWarehouse; import io.druid.query.aggregation.CountAggregatorFactory; import io.druid.query.aggregation.DoubleSumAggregatorFactory; +import io.druid.query.aggregation.FloatSumAggregatorFactory; import io.druid.query.aggregation.hyperloglog.HyperUniquesAggregatorFactory; import io.druid.query.expression.LookupExprMacro; import io.druid.query.expression.TestExprMacroTable; @@ -250,19 +251,32 @@ public class CalciteTests private static final IncrementalIndexSchema INDEX_SCHEMA = new IncrementalIndexSchema.Builder() .withMetrics( new CountAggregatorFactory("cnt"), - new DoubleSumAggregatorFactory("m1", "m1"), + new FloatSumAggregatorFactory("m1", "m1"), + new DoubleSumAggregatorFactory("m2", "m2"), new HyperUniquesAggregatorFactory("unique_dim1", "dim1") ) .withRollup(false) .build(); public static final List ROWS1 = ImmutableList.of( - createRow(ImmutableMap.of("t", "2000-01-01", "m1", "1.0", "dim1", "", "dim2", ImmutableList.of("a"))), - createRow(ImmutableMap.of("t", "2000-01-02", "m1", "2.0", "dim1", "10.1", "dim2", ImmutableList.of())), - createRow(ImmutableMap.of("t", "2000-01-03", "m1", "3.0", "dim1", "2", "dim2", ImmutableList.of(""))), - createRow(ImmutableMap.of("t", "2001-01-01", "m1", "4.0", "dim1", "1", "dim2", ImmutableList.of("a"))), - createRow(ImmutableMap.of("t", "2001-01-02", "m1", "5.0", "dim1", "def", "dim2", ImmutableList.of("abc"))), - createRow(ImmutableMap.of("t", "2001-01-03", "m1", "6.0", "dim1", "abc")) + createRow( + ImmutableMap.of("t", "2000-01-01", "m1", "1.0", "m2", "1.0", "dim1", "", "dim2", ImmutableList.of("a")) + ), + createRow( + ImmutableMap.of("t", "2000-01-02", "m1", "2.0", "m2", "2.0", "dim1", "10.1", "dim2", ImmutableList.of()) + ), + createRow( + ImmutableMap.of("t", "2000-01-03", "m1", "3.0", "m2", "3.0", "dim1", "2", "dim2", ImmutableList.of("")) + ), + createRow( + ImmutableMap.of("t", "2001-01-01", "m1", "4.0", "m2", "4.0", "dim1", "1", "dim2", ImmutableList.of("a")) + ), + createRow( + ImmutableMap.of("t", "2001-01-02", "m1", "5.0", "m2", "5.0", "dim1", "def", "dim2", ImmutableList.of("abc")) + ), + createRow( + ImmutableMap.of("t", "2001-01-03", "m1", "6.0", "m2", "6.0", "dim1", "abc") + ) ); public static final List ROWS2 = ImmutableList.of(