diff --git a/core/src/main/java/org/apache/druid/math/expr/ApplyFunction.java b/core/src/main/java/org/apache/druid/math/expr/ApplyFunction.java index 4bf2fa5e934b..920ee8967302 100644 --- a/core/src/main/java/org/apache/druid/math/expr/ApplyFunction.java +++ b/core/src/main/java/org/apache/druid/math/expr/ApplyFunction.java @@ -26,6 +26,7 @@ import org.apache.druid.java.util.common.IAE; import org.apache.druid.java.util.common.RE; import org.apache.druid.java.util.common.StringUtils; +import org.apache.druid.segment.column.ValueType; import javax.annotation.Nullable; import java.util.ArrayList; @@ -56,7 +57,7 @@ public interface ApplyFunction ExprEval apply(LambdaExpr lambdaExpr, List argsExpr, Expr.ObjectBinding bindings); /** - * Get list of input arguments which must evaluate to an array {@link ExprType} + * Get list of input arguments which must evaluate to an array {@link ValueType} */ Set getArrayInputs(List args); @@ -97,7 +98,7 @@ ExprEval applyMap(LambdaExpr expr, IndexableMapLambdaObjectBinding bindings) Long[] longsOut = null; Double[] doublesOut = null; - ExprType elementType = null; + ValueType elementType = null; for (int i = 0; i < length; i++) { ExprEval evaluated = expr.eval(bindings.withIndex(i)); @@ -274,7 +275,7 @@ ExprEval applyFold(LambdaExpr lambdaExpr, Object accumulator, IndexableFoldLambd accumulator = evaluated.value(); } if (accumulator instanceof Boolean) { - return ExprEval.of((boolean) accumulator, ExprType.LONG); + return ExprEval.ofLongBoolean((boolean) accumulator); } return ExprEval.bestEffortOf(accumulator); } @@ -501,7 +502,7 @@ public ExprEval apply(LambdaExpr lambdaExpr, List argsExpr, Expr.ObjectBin final Object[] array = arrayEval.asArray(); if (array == null) { - return ExprEval.of(false, ExprType.LONG); + return ExprEval.ofLongBoolean(false); } SettableLambdaBinding lambdaBinding = new SettableLambdaBinding(lambdaExpr, bindings); @@ -550,7 +551,7 @@ public ExprEval match(Object[] values, LambdaExpr expr, SettableLambdaBinding bi { boolean anyMatch = Arrays.stream(values) .anyMatch(o -> expr.eval(bindings.withBinding(expr.getIdentifier(), o)).asBoolean()); - return ExprEval.of(anyMatch, ExprType.LONG); + return ExprEval.ofLongBoolean(anyMatch); } } @@ -573,7 +574,7 @@ public ExprEval match(Object[] values, LambdaExpr expr, SettableLambdaBinding bi { boolean allMatch = Arrays.stream(values) .allMatch(o -> expr.eval(bindings.withBinding(expr.getIdentifier(), o)).asBoolean()); - return ExprEval.of(allMatch, ExprType.LONG); + return ExprEval.ofLongBoolean(allMatch); } } diff --git a/core/src/main/java/org/apache/druid/math/expr/BinaryLogicalOperatorExpr.java b/core/src/main/java/org/apache/druid/math/expr/BinaryLogicalOperatorExpr.java index dad35f30560a..3e4c9b8218f7 100644 --- a/core/src/main/java/org/apache/druid/math/expr/BinaryLogicalOperatorExpr.java +++ b/core/src/main/java/org/apache/druid/math/expr/BinaryLogicalOperatorExpr.java @@ -42,7 +42,7 @@ protected BinaryOpExprBase copy(Expr left, Expr right) @Override protected ExprEval evalString(@Nullable String left, @Nullable String right) { - return ExprEval.of(Comparators.naturalNullsFirst().compare(left, right) < 0, ExprType.LONG); + return ExprEval.ofLongBoolean(Comparators.naturalNullsFirst().compare(left, right) < 0); } @Override @@ -75,7 +75,7 @@ protected BinaryOpExprBase copy(Expr left, Expr right) @Override protected ExprEval evalString(@Nullable String left, @Nullable String right) { - return ExprEval.of(Comparators.naturalNullsFirst().compare(left, right) <= 0, ExprType.LONG); + return ExprEval.ofLongBoolean(Comparators.naturalNullsFirst().compare(left, right) <= 0); } @Override @@ -108,7 +108,7 @@ protected BinaryOpExprBase copy(Expr left, Expr right) @Override protected ExprEval evalString(@Nullable String left, @Nullable String right) { - return ExprEval.of(Comparators.naturalNullsFirst().compare(left, right) > 0, ExprType.LONG); + return ExprEval.ofLongBoolean(Comparators.naturalNullsFirst().compare(left, right) > 0); } @Override @@ -141,7 +141,7 @@ protected BinaryOpExprBase copy(Expr left, Expr right) @Override protected ExprEval evalString(@Nullable String left, @Nullable String right) { - return ExprEval.of(Comparators.naturalNullsFirst().compare(left, right) >= 0, ExprType.LONG); + return ExprEval.ofLongBoolean(Comparators.naturalNullsFirst().compare(left, right) >= 0); } @Override @@ -174,7 +174,7 @@ protected BinaryOpExprBase copy(Expr left, Expr right) @Override protected ExprEval evalString(@Nullable String left, @Nullable String right) { - return ExprEval.of(Objects.equals(left, right), ExprType.LONG); + return ExprEval.ofLongBoolean(Objects.equals(left, right)); } @Override @@ -206,7 +206,7 @@ protected BinaryOpExprBase copy(Expr left, Expr right) @Override protected ExprEval evalString(@Nullable String left, @Nullable String right) { - return ExprEval.of(!Objects.equals(left, right), ExprType.LONG); + return ExprEval.ofLongBoolean(!Objects.equals(left, right)); } @Override diff --git a/core/src/main/java/org/apache/druid/math/expr/BinaryOperatorExpr.java b/core/src/main/java/org/apache/druid/math/expr/BinaryOperatorExpr.java index 9c390587bd4e..f18945c5a6c3 100644 --- a/core/src/main/java/org/apache/druid/math/expr/BinaryOperatorExpr.java +++ b/core/src/main/java/org/apache/druid/math/expr/BinaryOperatorExpr.java @@ -22,6 +22,7 @@ import com.google.common.collect.ImmutableSet; import org.apache.druid.common.config.NullHandling; import org.apache.druid.java.util.common.StringUtils; +import org.apache.druid.segment.column.ValueType; import javax.annotation.Nullable; import java.util.Objects; @@ -132,9 +133,9 @@ public ExprEval eval(ObjectBinding bindings) return ExprEval.of(null); } - if (leftVal.type() == ExprType.STRING && rightVal.type() == ExprType.STRING) { + if (leftVal.type() == ValueType.STRING && rightVal.type() == ValueType.STRING) { return evalString(leftVal.asString(), rightVal.asString()); - } else if (leftVal.type() == ExprType.LONG && rightVal.type() == ExprType.LONG) { + } else if (leftVal.type() == ValueType.LONG && rightVal.type() == ValueType.LONG) { if (NullHandling.sqlCompatible() && (leftVal.isNumericNull() || rightVal.isNumericNull())) { return ExprEval.of(null); } @@ -149,7 +150,7 @@ public ExprEval eval(ObjectBinding bindings) protected ExprEval evalString(@Nullable String left, @Nullable String right) { - throw new IllegalArgumentException("unsupported type " + ExprType.STRING); + throw new IllegalArgumentException("unsupported type " + ValueType.STRING); } protected abstract long evalLong(long left, long right); diff --git a/core/src/main/java/org/apache/druid/math/expr/ExprEval.java b/core/src/main/java/org/apache/druid/math/expr/ExprEval.java index 61cdc26f6dd1..376726dc3ba4 100644 --- a/core/src/main/java/org/apache/druid/math/expr/ExprEval.java +++ b/core/src/main/java/org/apache/druid/math/expr/ExprEval.java @@ -23,12 +23,13 @@ import org.apache.druid.common.config.NullHandling; import org.apache.druid.common.guava.GuavaUtils; import org.apache.druid.java.util.common.IAE; +import org.apache.druid.segment.column.ValueType; import javax.annotation.Nullable; import java.util.Arrays; /** - * Generic result holder for evaluated {@link Expr} containing the value and {@link ExprType} of the value to allow + * Generic result holder for evaluated {@link Expr} containing the value and {@link ValueType} of the value to allow */ public abstract class ExprEval { @@ -75,7 +76,7 @@ public static ExprEval ofStringArray(@Nullable String[] stringValue) return new StringArrayExprEval(stringValue); } - public static ExprEval of(boolean value, ExprType type) + public static ExprEval of(boolean value, ValueType type) { switch (type) { case DOUBLE: @@ -89,6 +90,11 @@ public static ExprEval of(boolean value, ExprType type) } } + public static ExprEval ofLongBoolean(boolean value) + { + return ExprEval.of(Evals.asLong(value)); + } + public static ExprEval bestEffortOf(@Nullable Object val) { if (val instanceof ExprEval) { @@ -129,7 +135,10 @@ private ExprEval(@Nullable T value) this.value = value; } - public abstract ExprType type(); + /** + * Get the type of this evaluated {@link Expr} result. + */ + public abstract ValueType type(); @Nullable public T value() @@ -201,7 +210,7 @@ public boolean isArray() @Nullable public abstract Double[] asDoubleArray(); - public abstract ExprEval castTo(ExprType castTo); + public abstract ExprEval castTo(ValueType castTo); public abstract Expr toExpr(); @@ -266,9 +275,9 @@ private DoubleExprEval(@Nullable Number value) } @Override - public final ExprType type() + public final ValueType type() { - return ExprType.DOUBLE; + return ValueType.DOUBLE; } @Override @@ -285,7 +294,7 @@ public Object[] asArray() } @Override - public final ExprEval castTo(ExprType castTo) + public final ExprEval castTo(ValueType castTo) { switch (castTo) { case DOUBLE: @@ -326,9 +335,9 @@ private LongExprEval(@Nullable Number value) } @Override - public final ExprType type() + public final ValueType type() { - return ExprType.LONG; + return ValueType.LONG; } @Override @@ -352,7 +361,7 @@ public Long[] asLongArray() } @Override - public final ExprEval castTo(ExprType castTo) + public final ExprEval castTo(ValueType castTo) { switch (castTo) { case DOUBLE: @@ -408,9 +417,9 @@ private StringExprEval(@Nullable String value) } @Override - public final ExprType type() + public final ValueType type() { - return ExprType.STRING; + return ValueType.STRING; } @Override @@ -552,7 +561,7 @@ public Double[] asDoubleArray() } @Override - public final ExprEval castTo(ExprType castTo) + public final ExprEval castTo(ValueType castTo) { switch (castTo) { case DOUBLE: @@ -658,9 +667,9 @@ private LongArrayExprEval(@Nullable Long[] value) } @Override - public ExprType type() + public ValueType type() { - return ExprType.LONG_ARRAY; + return ValueType.LONG_ARRAY; } @Nullable @@ -685,7 +694,7 @@ public Double[] asDoubleArray() } @Override - public ExprEval castTo(ExprType castTo) + public ExprEval castTo(ValueType castTo) { if (value == null) { return StringExprEval.OF_NULL; @@ -717,9 +726,9 @@ private DoubleArrayExprEval(@Nullable Double[] value) } @Override - public ExprType type() + public ValueType type() { - return ExprType.DOUBLE_ARRAY; + return ValueType.DOUBLE_ARRAY; } @Nullable @@ -744,7 +753,7 @@ public Double[] asDoubleArray() } @Override - public ExprEval castTo(ExprType castTo) + public ExprEval castTo(ValueType castTo) { if (value == null) { return StringExprEval.OF_NULL; @@ -781,9 +790,9 @@ private StringArrayExprEval(@Nullable String[] value) } @Override - public ExprType type() + public ValueType type() { - return ExprType.STRING_ARRAY; + return ValueType.STRING_ARRAY; } @Nullable @@ -816,7 +825,7 @@ public Double[] asDoubleArray() } @Override - public ExprEval castTo(ExprType castTo) + public ExprEval castTo(ValueType castTo) { if (value == null) { return StringExprEval.OF_NULL; diff --git a/core/src/main/java/org/apache/druid/math/expr/ExprType.java b/core/src/main/java/org/apache/druid/math/expr/ExprType.java deleted file mode 100644 index 0bc1573bef56..000000000000 --- a/core/src/main/java/org/apache/druid/math/expr/ExprType.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.druid.math.expr; - -/** - * Base 'value' types of Druid expression language, all {@link Expr} must evaluate to one of these types. - */ -public enum ExprType -{ - DOUBLE, - LONG, - STRING, - DOUBLE_ARRAY, - LONG_ARRAY, - STRING_ARRAY -} diff --git a/core/src/main/java/org/apache/druid/math/expr/Function.java b/core/src/main/java/org/apache/druid/math/expr/Function.java index a20863929875..6d277ad9c45c 100644 --- a/core/src/main/java/org/apache/druid/math/expr/Function.java +++ b/core/src/main/java/org/apache/druid/math/expr/Function.java @@ -25,6 +25,7 @@ import org.apache.druid.java.util.common.IAE; import org.apache.druid.java.util.common.RE; import org.apache.druid.java.util.common.StringUtils; +import org.apache.druid.segment.column.ValueType; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.joda.time.format.DateTimeFormat; @@ -163,9 +164,9 @@ protected final ExprEval eval(ExprEval param) if (NullHandling.sqlCompatible() && param.isNumericNull()) { return ExprEval.of(null); } - if (param.type() == ExprType.LONG) { + if (param.type() == ValueType.LONG) { return eval(param.asLong()); - } else if (param.type() == ExprType.DOUBLE) { + } else if (param.type() == ValueType.DOUBLE) { return eval(param.asDouble()); } return ExprEval.of(null); @@ -191,10 +192,10 @@ abstract class BivariateMathFunction extends BivariateFunction @Override protected final ExprEval eval(ExprEval x, ExprEval y) { - if (x.type() == ExprType.STRING || y.type() == ExprType.STRING) { + if (x.type() == ValueType.STRING || y.type() == ValueType.STRING) { return ExprEval.of(null); } - if (x.type() == ExprType.LONG && y.type() == ExprType.LONG) { + if (x.type() == ValueType.LONG && y.type() == ValueType.LONG) { return eval(x.asLong(), y.asLong()); } else { return eval(x.asDouble(), y.asDouble()); @@ -213,15 +214,15 @@ protected ExprEval eval(double x, double y) } /** - * Base class for a 2 variable input {@link Function} whose first argument is a {@link ExprType#STRING} and second - * argument is {@link ExprType#LONG} + * Base class for a 2 variable input {@link Function} whose first argument is a {@link ValueType#STRING} and second + * argument is {@link ValueType#LONG} */ abstract class StringLongFunction extends BivariateFunction { @Override protected final ExprEval eval(ExprEval x, ExprEval y) { - if (x.type() != ExprType.STRING || y.type() != ExprType.LONG) { + if (x.type() != ValueType.STRING || y.type() != ValueType.LONG) { throw new IAE( "Function[%s] needs a string as first argument and an integer as second argument", name() @@ -709,7 +710,7 @@ public String name() public ExprEval apply(List args, Expr.ObjectBinding bindings) { ExprEval value1 = args.get(0).eval(bindings); - if (value1.type() != ExprType.LONG && value1.type() != ExprType.DOUBLE) { + if (value1.type() != ValueType.LONG && value1.type() != ValueType.DOUBLE) { throw new IAE( "The first argument to the function[%s] should be integer or double type but got the type: %s", name(), @@ -721,7 +722,7 @@ public ExprEval apply(List args, Expr.ObjectBinding bindings) return eval(value1); } else { ExprEval value2 = args.get(1).eval(bindings); - if (value2.type() != ExprType.LONG) { + if (value2.type() != ValueType.LONG) { throw new IAE( "The second argument to the function[%s] should be integer type but got the type: %s", name(), @@ -747,9 +748,9 @@ private ExprEval eval(ExprEval param) private ExprEval eval(ExprEval param, int scale) { - if (param.type() == ExprType.LONG) { + if (param.type() == ValueType.LONG) { return ExprEval.of(BigDecimal.valueOf(param.asLong()).setScale(scale, RoundingMode.HALF_UP).longValue()); - } else if (param.type() == ExprType.DOUBLE) { + } else if (param.type() == ValueType.DOUBLE) { BigDecimal decimal = safeGetFromDouble(param.asDouble()); return ExprEval.of(decimal.setScale(scale, RoundingMode.HALF_UP).doubleValue()); } else { @@ -1106,7 +1107,7 @@ public ExprEval apply(List args, Expr.ObjectBinding bindings) } /** - * Determines which {@link ExprType} to use to compare non-null evaluated expressions. + * Determines which {@link ValueType} to use to compare non-null evaluated expressions. * * @param exprs Expressions to analyze * @param bindings Bindings for expressions @@ -1115,12 +1116,12 @@ public ExprEval apply(List args, Expr.ObjectBinding bindings) */ private ExprAnalysis analyzeExprs(List exprs, Expr.ObjectBinding bindings) { - Set presentTypes = EnumSet.noneOf(ExprType.class); + Set presentTypes = EnumSet.noneOf(ValueType.class); List> exprEvals = new ArrayList<>(); for (Expr expr : exprs) { ExprEval exprEval = expr.eval(bindings); - ExprType exprType = exprEval.type(); + ValueType exprType = exprEval.type(); if (isValidType(exprType)) { presentTypes.add(exprType); @@ -1131,11 +1132,11 @@ private ExprAnalysis analyzeExprs(List exprs, Expr.ObjectBinding bindings) } } - ExprType comparisonType = getComparisionType(presentTypes); + ValueType comparisonType = getComparisionType(presentTypes); return new ExprAnalysis(comparisonType, exprEvals); } - private boolean isValidType(ExprType exprType) + private boolean isValidType(ValueType exprType) { switch (exprType) { case DOUBLE: @@ -1152,23 +1153,23 @@ private boolean isValidType(ExprType exprType) * * @see org.apache.druid.sql.calcite.expression.builtin.ReductionOperatorConversionHelper#TYPE_INFERENCE */ - private static ExprType getComparisionType(Set exprTypes) + private static ValueType getComparisionType(Set exprTypes) { - if (exprTypes.contains(ExprType.STRING)) { - return ExprType.STRING; - } else if (exprTypes.contains(ExprType.DOUBLE)) { - return ExprType.DOUBLE; + if (exprTypes.contains(ValueType.STRING)) { + return ValueType.STRING; + } else if (exprTypes.contains(ValueType.DOUBLE)) { + return ValueType.DOUBLE; } else { - return ExprType.LONG; + return ValueType.LONG; } } private static class ExprAnalysis { - final ExprType comparisonType; + final ValueType comparisonType; final List> exprEvals; - ExprAnalysis(ExprType comparisonType, List> exprEvals) + ExprAnalysis(ValueType comparisonType, List> exprEvals) { this.comparisonType = comparisonType; this.exprEvals = exprEvals; @@ -1331,9 +1332,9 @@ protected ExprEval eval(ExprEval x, ExprEval y) if (NullHandling.sqlCompatible() && x.value() == null) { return ExprEval.of(null); } - ExprType castTo; + ValueType castTo; try { - castTo = ExprType.valueOf(StringUtils.toUpperCase(y.asString())); + castTo = ValueType.valueOf(StringUtils.toUpperCase(y.asString())); } catch (IllegalArgumentException e) { throw new IAE("invalid type '%s'", y.asString()); @@ -1345,7 +1346,7 @@ protected ExprEval eval(ExprEval x, ExprEval y) public Set getScalarInputs(List args) { if (args.get(1).isLiteral()) { - ExprType castTo = ExprType.valueOf(StringUtils.toUpperCase(args.get(1).getLiteralValue().toString())); + ValueType castTo = ValueType.valueOf(StringUtils.toUpperCase(args.get(1).getLiteralValue().toString())); switch (castTo) { case LONG_ARRAY: case DOUBLE_ARRAY: @@ -1363,7 +1364,7 @@ public Set getScalarInputs(List args) public Set getArrayInputs(List args) { if (args.get(1).isLiteral()) { - ExprType castTo = ExprType.valueOf(StringUtils.toUpperCase(args.get(1).getLiteralValue().toString())); + ValueType castTo = ValueType.valueOf(StringUtils.toUpperCase(args.get(1).getLiteralValue().toString())); switch (castTo) { case LONG: case DOUBLE: @@ -1390,14 +1391,14 @@ public String name() public ExprEval apply(List args, Expr.ObjectBinding bindings) { ExprEval value = args.get(0).eval(bindings); - if (value.type() != ExprType.STRING) { + if (value.type() != ValueType.STRING) { throw new IAE("first argument should be string type but got %s type", value.type()); } DateTimes.UtcFormatter formatter = DateTimes.ISO_DATE_OPTIONAL_TIME; if (args.size() > 1) { ExprEval format = args.get(1).eval(bindings); - if (format.type() != ExprType.STRING) { + if (format.type() != ValueType.STRING) { throw new IAE("second argument should be string type but got %s type", format.type()); } formatter = DateTimes.wrapFormatter(DateTimeFormat.forPattern(format.asString())); @@ -1790,7 +1791,7 @@ public String name() @Override protected ExprEval eval(ExprEval param) { - if (param.type() != ExprType.STRING) { + if (param.type() != ValueType.STRING) { throw new IAE( "Function[%s] needs a string argument", name() @@ -1828,7 +1829,7 @@ public String name() public ExprEval apply(List args, Expr.ObjectBinding bindings) { final ExprEval expr = args.get(0).eval(bindings); - return ExprEval.of(expr.value() == null, ExprType.LONG); + return ExprEval.ofLongBoolean(expr.value() == null); } @Override @@ -1852,7 +1853,7 @@ public String name() public ExprEval apply(List args, Expr.ObjectBinding bindings) { final ExprEval expr = args.get(0).eval(bindings); - return ExprEval.of(expr.value() != null, ExprType.LONG); + return ExprEval.ofLongBoolean(expr.value() != null); } @Override @@ -1978,7 +1979,7 @@ public ExprEval apply(List args, Expr.ObjectBinding bindings) Long[] longsOut = null; Double[] doublesOut = null; - ExprType elementType = null; + ValueType elementType = null; for (int i = 0; i < length; i++) { ExprEval evaluated = args.get(i).eval(bindings); if (elementType == null) { @@ -2020,7 +2021,7 @@ static void setArrayOutputElement( String[] stringsOut, Long[] longsOut, Double[] doublesOut, - ExprType elementType, + ValueType elementType, int i, ExprEval evaluated ) @@ -2414,7 +2415,7 @@ ExprEval doApply(ExprEval lhsExpr, ExprEval rhsExpr) { final Object[] array1 = lhsExpr.asArray(); final Object[] array2 = rhsExpr.asArray(); - return ExprEval.of(Arrays.asList(array1).containsAll(Arrays.asList(array2)), ExprType.LONG); + return ExprEval.ofLongBoolean(Arrays.asList(array1).containsAll(Arrays.asList(array2))); } } @@ -2435,7 +2436,7 @@ ExprEval doApply(ExprEval lhsExpr, ExprEval rhsExpr) for (Object check : array1) { any |= array2.contains(check); } - return ExprEval.of(any, ExprType.LONG); + return ExprEval.ofLongBoolean(any); } } diff --git a/core/src/main/java/org/apache/druid/math/expr/UnaryOperatorExpr.java b/core/src/main/java/org/apache/druid/math/expr/UnaryOperatorExpr.java index 5a41e9042509..ceafc0dfbad1 100644 --- a/core/src/main/java/org/apache/druid/math/expr/UnaryOperatorExpr.java +++ b/core/src/main/java/org/apache/druid/math/expr/UnaryOperatorExpr.java @@ -23,6 +23,7 @@ import org.apache.druid.common.config.NullHandling; import org.apache.druid.java.util.common.IAE; import org.apache.druid.java.util.common.StringUtils; +import org.apache.druid.segment.column.ValueType; import java.util.Objects; @@ -105,10 +106,10 @@ public ExprEval eval(ObjectBinding bindings) if (NullHandling.sqlCompatible() && (ret.value() == null)) { return ExprEval.of(null); } - if (ret.type() == ExprType.LONG) { + if (ret.type() == ValueType.LONG) { return ExprEval.of(-ret.asLong()); } - if (ret.type() == ExprType.DOUBLE) { + if (ret.type() == ValueType.DOUBLE) { return ExprEval.of(-ret.asDouble()); } throw new IAE("unsupported type " + ret.type()); @@ -148,7 +149,7 @@ public ExprEval eval(ObjectBinding bindings) return ExprEval.of(null); } // conforming to other boolean-returning binary operators - ExprType retType = ret.type() == ExprType.DOUBLE ? ExprType.DOUBLE : ExprType.LONG; + ValueType retType = ret.type() == ValueType.DOUBLE ? ValueType.DOUBLE : ValueType.LONG; return ExprEval.of(!ret.asBoolean(), retType); } diff --git a/core/src/main/java/org/apache/druid/segment/column/ValueType.java b/core/src/main/java/org/apache/druid/segment/column/ValueType.java index fa5e4894ef2f..7b41d7e35fd5 100644 --- a/core/src/main/java/org/apache/druid/segment/column/ValueType.java +++ b/core/src/main/java/org/apache/druid/segment/column/ValueType.java @@ -20,6 +20,7 @@ package org.apache.druid.segment.column; import com.fasterxml.jackson.annotation.JsonCreator; +import org.apache.druid.java.util.common.ISE; import org.apache.druid.java.util.common.StringUtils; import javax.annotation.Nullable; @@ -46,7 +47,8 @@ public enum ValueType /** * 32-bit single precision floating point number primitive type. This type may be used as a grouping key, or as an * input to any aggregators which support primitive numerical operations like sums, minimums, maximums, etc, as well - * as an input to expression virtual columns. + * as an input to expression virtual columns, with the caveat that the expression system does not directly support + * this type and must be treated as {@link #DOUBLE} instead. */ FLOAT, /** @@ -117,6 +119,18 @@ public boolean isPrimitive() return this.equals(ValueType.STRING) || isNumeric(this); } + /** + * The expression system does not distinguish between {@link #FLOAT} and {@link #DOUBLE}, and cannot currently handle + * {@link #COMPLEX} inputs. This method will convert {@link #FLOAT} to {@link #DOUBLE}, or throw an exception if a + * {@link #COMPLEX} is encountered. + * + * @throws IllegalStateException + */ + public ValueType toExpressionType() + { + return toExpressionType(this); + } + @Nullable @JsonCreator public static ValueType fromString(@Nullable String name) @@ -127,13 +141,46 @@ public static ValueType fromString(@Nullable String name) return valueOf(StringUtils.toUpperCase(name)); } + /** + * Type is a numeric type, not including numeric array types + */ public static boolean isNumeric(ValueType type) { return type == ValueType.LONG || type == ValueType.FLOAT || type == ValueType.DOUBLE; } + /** + * Type is an array type + */ public static boolean isArray(ValueType type) { return type == ValueType.DOUBLE_ARRAY || type == ValueType.LONG_ARRAY || type == ValueType.STRING_ARRAY; } + + /** + * The expression system does not distinguish between {@link #FLOAT} and {@link #DOUBLE}, and cannot currently handle + * {@link #COMPLEX} inputs. This method will convert {@link #FLOAT} to {@link #DOUBLE}, or throw an exception if a + * {@link #COMPLEX} is encountered. + * + * @throws IllegalStateException + */ + public static ValueType toExpressionType(@Nullable ValueType valueType) + { + if (valueType == null) { + throw new IllegalStateException("No expression compatible type for unknown value type"); + } + switch (valueType) { + case STRING: + case STRING_ARRAY: + case LONG: + case LONG_ARRAY: + case DOUBLE: + case DOUBLE_ARRAY: + return valueType; + case FLOAT: + return ValueType.DOUBLE; + default: + throw new ISE("No expression compatible type for value type[%s]", valueType); + } + } } diff --git a/core/src/test/java/org/apache/druid/math/expr/EvalTest.java b/core/src/test/java/org/apache/druid/math/expr/EvalTest.java index 732e744fd8fe..8baa0d877b34 100644 --- a/core/src/test/java/org/apache/druid/math/expr/EvalTest.java +++ b/core/src/test/java/org/apache/druid/math/expr/EvalTest.java @@ -21,6 +21,7 @@ import com.google.common.collect.ImmutableMap; import org.apache.druid.common.config.NullHandling; +import org.apache.druid.segment.column.ValueType; import org.apache.druid.testing.InitializedNullHandlingTest; import org.junit.Assert; import org.junit.Test; @@ -32,14 +33,14 @@ public class EvalTest extends InitializedNullHandlingTest private long evalLong(String x, Expr.ObjectBinding bindings) { ExprEval ret = eval(x, bindings); - Assert.assertEquals(ExprType.LONG, ret.type()); + Assert.assertEquals(ValueType.LONG, ret.type()); return ret.asLong(); } private double evalDouble(String x, Expr.ObjectBinding bindings) { ExprEval ret = eval(x, bindings); - Assert.assertEquals(ExprType.DOUBLE, ret.type()); + Assert.assertEquals(ValueType.DOUBLE, ret.type()); return ret.asDouble(); } @@ -152,26 +153,26 @@ public void testBooleanReturn() ); ExprEval eval = Parser.parse("x==y", ExprMacroTable.nil()).eval(bindings); Assert.assertTrue(eval.asBoolean()); - Assert.assertEquals(ExprType.LONG, eval.type()); + Assert.assertEquals(ValueType.LONG, eval.type()); eval = Parser.parse("x!=y", ExprMacroTable.nil()).eval(bindings); Assert.assertFalse(eval.asBoolean()); - Assert.assertEquals(ExprType.LONG, eval.type()); + Assert.assertEquals(ValueType.LONG, eval.type()); eval = Parser.parse("x==z", ExprMacroTable.nil()).eval(bindings); Assert.assertTrue(eval.asBoolean()); - Assert.assertEquals(ExprType.DOUBLE, eval.type()); + Assert.assertEquals(ValueType.DOUBLE, eval.type()); eval = Parser.parse("x!=z", ExprMacroTable.nil()).eval(bindings); Assert.assertFalse(eval.asBoolean()); - Assert.assertEquals(ExprType.DOUBLE, eval.type()); + Assert.assertEquals(ValueType.DOUBLE, eval.type()); eval = Parser.parse("z==w", ExprMacroTable.nil()).eval(bindings); Assert.assertTrue(eval.asBoolean()); - Assert.assertEquals(ExprType.DOUBLE, eval.type()); + Assert.assertEquals(ValueType.DOUBLE, eval.type()); eval = Parser.parse("z!=w", ExprMacroTable.nil()).eval(bindings); Assert.assertFalse(eval.asBoolean()); - Assert.assertEquals(ExprType.DOUBLE, eval.type()); + Assert.assertEquals(ValueType.DOUBLE, eval.type()); } } diff --git a/extensions-core/druid-bloom-filter/src/main/java/org/apache/druid/query/expressions/BloomFilterExprMacro.java b/extensions-core/druid-bloom-filter/src/main/java/org/apache/druid/query/expressions/BloomFilterExprMacro.java index dcc3a16ccad6..6cbcfd16bc66 100644 --- a/extensions-core/druid-bloom-filter/src/main/java/org/apache/druid/query/expressions/BloomFilterExprMacro.java +++ b/extensions-core/druid-bloom-filter/src/main/java/org/apache/druid/query/expressions/BloomFilterExprMacro.java @@ -25,7 +25,6 @@ import org.apache.druid.math.expr.Expr; import org.apache.druid.math.expr.ExprEval; import org.apache.druid.math.expr.ExprMacroTable; -import org.apache.druid.math.expr.ExprType; import org.apache.druid.query.filter.BloomKFilter; import javax.annotation.Nonnull; @@ -108,7 +107,7 @@ public ExprEval eval(final ObjectBinding bindings) break; } - return ExprEval.of(matches, ExprType.LONG); + return ExprEval.ofLongBoolean(matches); } private boolean nullMatch() diff --git a/processing/src/main/java/org/apache/druid/query/expression/IPv4AddressMatchExprMacro.java b/processing/src/main/java/org/apache/druid/query/expression/IPv4AddressMatchExprMacro.java index 5e9cc85fe545..05d510e2d3f6 100644 --- a/processing/src/main/java/org/apache/druid/query/expression/IPv4AddressMatchExprMacro.java +++ b/processing/src/main/java/org/apache/druid/query/expression/IPv4AddressMatchExprMacro.java @@ -25,7 +25,6 @@ import org.apache.druid.math.expr.Expr; import org.apache.druid.math.expr.ExprEval; import org.apache.druid.math.expr.ExprMacroTable; -import org.apache.druid.math.expr.ExprType; import javax.annotation.Nonnull; import java.util.List; @@ -98,7 +97,7 @@ public ExprEval eval(final ObjectBinding bindings) default: match = false; } - return ExprEval.of(match, ExprType.LONG); + return ExprEval.ofLongBoolean(match); } private boolean isStringMatch(String stringValue) diff --git a/processing/src/main/java/org/apache/druid/query/expression/LikeExprMacro.java b/processing/src/main/java/org/apache/druid/query/expression/LikeExprMacro.java index 2332b2858eaf..a124722e8f8b 100644 --- a/processing/src/main/java/org/apache/druid/query/expression/LikeExprMacro.java +++ b/processing/src/main/java/org/apache/druid/query/expression/LikeExprMacro.java @@ -25,7 +25,6 @@ import org.apache.druid.math.expr.Expr; import org.apache.druid.math.expr.ExprEval; import org.apache.druid.math.expr.ExprMacroTable; -import org.apache.druid.math.expr.ExprType; import org.apache.druid.query.filter.LikeDimFilter; import javax.annotation.Nonnull; @@ -81,7 +80,7 @@ private LikeExtractExpr(Expr arg) @Override public ExprEval eval(final ObjectBinding bindings) { - return ExprEval.of(likeMatcher.matches(arg.eval(bindings).asString()), ExprType.LONG); + return ExprEval.ofLongBoolean(likeMatcher.matches(arg.eval(bindings).asString())); } @Override diff --git a/processing/src/main/java/org/apache/druid/query/expression/RegexpLikeExprMacro.java b/processing/src/main/java/org/apache/druid/query/expression/RegexpLikeExprMacro.java index 83735e863494..a4909194d484 100644 --- a/processing/src/main/java/org/apache/druid/query/expression/RegexpLikeExprMacro.java +++ b/processing/src/main/java/org/apache/druid/query/expression/RegexpLikeExprMacro.java @@ -25,7 +25,6 @@ import org.apache.druid.math.expr.Expr; import org.apache.druid.math.expr.ExprEval; import org.apache.druid.math.expr.ExprMacroTable; -import org.apache.druid.math.expr.ExprType; import javax.annotation.Nonnull; import java.util.List; @@ -76,10 +75,10 @@ public ExprEval eval(final ObjectBinding bindings) if (s == null) { // True nulls do not match anything. Note: this branch only executes in SQL-compatible null handling mode. - return ExprEval.of(false, ExprType.LONG); + return ExprEval.ofLongBoolean(false); } else { final Matcher matcher = pattern.matcher(s); - return ExprEval.of(matcher.find(), ExprType.LONG); + return ExprEval.ofLongBoolean(matcher.find()); } } diff --git a/processing/src/main/java/org/apache/druid/segment/virtual/ExpressionSelectors.java b/processing/src/main/java/org/apache/druid/segment/virtual/ExpressionSelectors.java index 5dd6a9970432..860e47b0f6c0 100644 --- a/processing/src/main/java/org/apache/druid/segment/virtual/ExpressionSelectors.java +++ b/processing/src/main/java/org/apache/druid/segment/virtual/ExpressionSelectors.java @@ -376,16 +376,13 @@ private static Expr.ObjectBinding createBindings( final Supplier supplier; if (nativeType == ValueType.FLOAT) { - ColumnValueSelector selector = columnSelectorFactory - .makeColumnValueSelector(columnName); + ColumnValueSelector selector = columnSelectorFactory.makeColumnValueSelector(columnName); supplier = makeNullableNumericSupplier(selector, selector::getFloat); } else if (nativeType == ValueType.LONG) { - ColumnValueSelector selector = columnSelectorFactory - .makeColumnValueSelector(columnName); + ColumnValueSelector selector = columnSelectorFactory.makeColumnValueSelector(columnName); supplier = makeNullableNumericSupplier(selector, selector::getLong); } else if (nativeType == ValueType.DOUBLE) { - ColumnValueSelector selector = columnSelectorFactory - .makeColumnValueSelector(columnName); + ColumnValueSelector selector = columnSelectorFactory.makeColumnValueSelector(columnName); supplier = makeNullableNumericSupplier(selector, selector::getDouble); } else if (nativeType == ValueType.STRING) { supplier = supplierFromDimensionSelector( diff --git a/processing/src/test/java/org/apache/druid/query/expression/RegexpLikeExprMacroTest.java b/processing/src/test/java/org/apache/druid/query/expression/RegexpLikeExprMacroTest.java index a6bdfb36a03a..b57db64b2327 100644 --- a/processing/src/test/java/org/apache/druid/query/expression/RegexpLikeExprMacroTest.java +++ b/processing/src/test/java/org/apache/druid/query/expression/RegexpLikeExprMacroTest.java @@ -22,7 +22,6 @@ import com.google.common.collect.ImmutableMap; import org.apache.druid.common.config.NullHandling; import org.apache.druid.math.expr.ExprEval; -import org.apache.druid.math.expr.ExprType; import org.apache.druid.math.expr.Parser; import org.junit.Assert; import org.junit.Test; @@ -53,7 +52,7 @@ public void testMatch() { final ExprEval result = eval("regexp_like(a, 'f.o')", Parser.withMap(ImmutableMap.of("a", "foo"))); Assert.assertEquals( - ExprEval.of(true, ExprType.LONG).value(), + ExprEval.ofLongBoolean(true).value(), result.value() ); } @@ -63,7 +62,7 @@ public void testNoMatch() { final ExprEval result = eval("regexp_like(a, 'f.x')", Parser.withMap(ImmutableMap.of("a", "foo"))); Assert.assertEquals( - ExprEval.of(false, ExprType.LONG).value(), + ExprEval.ofLongBoolean(false).value(), result.value() ); } @@ -77,7 +76,7 @@ public void testNullPattern() final ExprEval result = eval("regexp_like(a, null)", Parser.withMap(ImmutableMap.of("a", "foo"))); Assert.assertEquals( - ExprEval.of(true, ExprType.LONG).value(), + ExprEval.ofLongBoolean(true).value(), result.value() ); } @@ -87,7 +86,7 @@ public void testEmptyStringPattern() { final ExprEval result = eval("regexp_like(a, '')", Parser.withMap(ImmutableMap.of("a", "foo"))); Assert.assertEquals( - ExprEval.of(true, ExprType.LONG).value(), + ExprEval.ofLongBoolean(true).value(), result.value() ); } @@ -101,7 +100,7 @@ public void testNullPatternOnEmptyString() final ExprEval result = eval("regexp_like(a, null)", Parser.withMap(ImmutableMap.of("a", ""))); Assert.assertEquals( - ExprEval.of(true, ExprType.LONG).value(), + ExprEval.ofLongBoolean(true).value(), result.value() ); } @@ -111,7 +110,7 @@ public void testEmptyStringPatternOnEmptyString() { final ExprEval result = eval("regexp_like(a, '')", Parser.withMap(ImmutableMap.of("a", ""))); Assert.assertEquals( - ExprEval.of(true, ExprType.LONG).value(), + ExprEval.ofLongBoolean(true).value(), result.value() ); } @@ -125,7 +124,7 @@ public void testNullPatternOnNull() final ExprEval result = eval("regexp_like(a, null)", Parser.withSuppliers(ImmutableMap.of("a", () -> null))); Assert.assertEquals( - ExprEval.of(true, ExprType.LONG).value(), + ExprEval.ofLongBoolean(true).value(), result.value() ); } @@ -135,7 +134,7 @@ public void testEmptyStringPatternOnNull() { final ExprEval result = eval("regexp_like(a, '')", Parser.withSuppliers(ImmutableMap.of("a", () -> null))); Assert.assertEquals( - ExprEval.of(NullHandling.replaceWithDefault(), ExprType.LONG).value(), + ExprEval.ofLongBoolean(NullHandling.replaceWithDefault()).value(), result.value() ); } diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/Expressions.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/Expressions.java index e45647435b6a..3ddddd9957ff 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/expression/Expressions.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/Expressions.java @@ -37,7 +37,6 @@ import org.apache.druid.java.util.common.granularity.Granularity; import org.apache.druid.math.expr.Expr; import org.apache.druid.math.expr.ExprMacroTable; -import org.apache.druid.math.expr.ExprType; import org.apache.druid.math.expr.Parser; import org.apache.druid.query.aggregation.PostAggregator; import org.apache.druid.query.expression.TimestampFloorExprMacro; @@ -54,7 +53,6 @@ import org.apache.druid.segment.VirtualColumn; import org.apache.druid.segment.column.ColumnHolder; import org.apache.druid.segment.column.RowSignature; -import org.apache.druid.segment.column.ValueType; import org.apache.druid.sql.calcite.filtration.BoundRefKey; import org.apache.druid.sql.calcite.filtration.Bounds; import org.apache.druid.sql.calcite.filtration.Filtration; @@ -666,21 +664,6 @@ private static DimFilter toExpressionLeafFilter( : null; } - 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); - } - } - /** * Converts an expression to a Granularity, if possible. This is possible if, and only if, the expression * is a timestamp_floor function on the __time column with literal parameters for period, origin, and timeZone. diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/CastOperatorConversion.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/CastOperatorConversion.java index 51a42b41140a..1d32a79c794f 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/CastOperatorConversion.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/CastOperatorConversion.java @@ -29,8 +29,8 @@ import org.apache.druid.java.util.common.ISE; import org.apache.druid.java.util.common.StringUtils; import org.apache.druid.java.util.common.granularity.PeriodGranularity; -import org.apache.druid.math.expr.ExprType; import org.apache.druid.segment.column.RowSignature; +import org.apache.druid.segment.column.ValueType; import org.apache.druid.sql.calcite.expression.DruidExpression; import org.apache.druid.sql.calcite.expression.Expressions; import org.apache.druid.sql.calcite.expression.SqlOperatorConversion; @@ -42,36 +42,36 @@ public class CastOperatorConversion implements SqlOperatorConversion { - private static final Map EXPRESSION_TYPES; + private static final Map EXPRESSION_TYPES; static { - final ImmutableMap.Builder builder = ImmutableMap.builder(); + final ImmutableMap.Builder builder = ImmutableMap.builder(); for (SqlTypeName type : SqlTypeName.FRACTIONAL_TYPES) { - builder.put(type, ExprType.DOUBLE); + builder.put(type, ValueType.DOUBLE); } for (SqlTypeName type : SqlTypeName.INT_TYPES) { - builder.put(type, ExprType.LONG); + builder.put(type, ValueType.LONG); } for (SqlTypeName type : SqlTypeName.STRING_TYPES) { - builder.put(type, ExprType.STRING); + builder.put(type, ValueType.STRING); } // Booleans are treated as longs in Druid expressions, using two-value logic (positive = true, nonpositive = false). - builder.put(SqlTypeName.BOOLEAN, ExprType.LONG); + builder.put(SqlTypeName.BOOLEAN, ValueType.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); + builder.put(SqlTypeName.TIMESTAMP, ValueType.LONG); + builder.put(SqlTypeName.DATE, ValueType.LONG); for (SqlTypeName type : SqlTypeName.DAY_INTERVAL_TYPES) { - builder.put(type, ExprType.LONG); + builder.put(type, ValueType.LONG); } for (SqlTypeName type : SqlTypeName.YEAR_INTERVAL_TYPES) { - builder.put(type, ExprType.LONG); + builder.put(type, ValueType.LONG); } EXPRESSION_TYPES = builder.build(); @@ -110,8 +110,8 @@ public DruidExpression toDruidExpression( return castDateTimeToChar(plannerContext, operandExpression, fromType); } else { // Handle other casts. - final ExprType fromExprType = EXPRESSION_TYPES.get(fromType); - final ExprType toExprType = EXPRESSION_TYPES.get(toType); + final ValueType fromExprType = EXPRESSION_TYPES.get(fromType); + final ValueType toExprType = EXPRESSION_TYPES.get(toType); if (fromExprType == null || toExprType == null) { // We have no runtime type for these SQL types. diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/planner/DruidRexExecutor.java b/sql/src/main/java/org/apache/druid/sql/calcite/planner/DruidRexExecutor.java index c181cb8c885a..0b6dad99b70a 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/planner/DruidRexExecutor.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/planner/DruidRexExecutor.java @@ -28,9 +28,9 @@ import org.apache.druid.java.util.common.IAE; import org.apache.druid.math.expr.Expr; import org.apache.druid.math.expr.ExprEval; -import org.apache.druid.math.expr.ExprType; import org.apache.druid.math.expr.Parser; import org.apache.druid.segment.column.RowSignature; +import org.apache.druid.segment.column.ValueType; import org.apache.druid.sql.calcite.expression.DruidExpression; import org.apache.druid.sql.calcite.expression.Expressions; @@ -119,7 +119,7 @@ public void reduce( if (exprResult.isNumericNull()) { literal = rexBuilder.makeNullLiteral(constExp.getType()); } else { - if (exprResult.type() == ExprType.LONG) { + if (exprResult.type() == ValueType.LONG) { bigDecimal = BigDecimal.valueOf(exprResult.asLong()); } else { diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/rel/Projection.java b/sql/src/main/java/org/apache/druid/sql/calcite/rel/Projection.java index d353a4ae116c..0bdd408d6574 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/rel/Projection.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/rel/Projection.java @@ -27,7 +27,6 @@ import org.apache.calcite.sql.SqlKind; import org.apache.druid.java.util.common.IAE; import org.apache.druid.java.util.common.ISE; -import org.apache.druid.math.expr.ExprType; import org.apache.druid.query.aggregation.PostAggregator; import org.apache.druid.query.aggregation.post.ExpressionPostAggregator; import org.apache.druid.query.aggregation.post.FieldAccessPostAggregator; @@ -316,10 +315,8 @@ private static boolean postAggregatorDirectColumnIsOk( } // Check if a cast is necessary. - final ExprType toExprType = Expressions.exprTypeForValueType(columnValueType); - final ExprType fromExprType = Expressions.exprTypeForValueType( - Calcites.getValueTypeForRelDataType(rexNode.getType()) - ); + final ValueType toExprType = columnValueType.toExpressionType(); + final ValueType fromExprType = ValueType.toExpressionType(Calcites.getValueTypeForRelDataType(rexNode.getType())); return toExprType.equals(fromExprType); }