This is an automated email from the ASF dual-hosted git repository.
gian pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/druid.git
The following commit(s) were added to refs/heads/master by this push:
new 3ae5e978012 Add IS [NOT] TRUE, IS [NOT] FALSE native functions.
(#14977)
3ae5e978012 is described below
commit 3ae5e978012e7a553e6ad3007eb210ab8da5b6fc
Author: Gian Merlino <[email protected]>
AuthorDate: Thu Sep 14 09:19:09 2023 -0700
Add IS [NOT] TRUE, IS [NOT] FALSE native functions. (#14977)
They are not quite the same as "x == true", "x != true", etc. These
functions never return null, even when "x" itself is null.
---
.../java/org/apache/druid/math/expr/Function.java | 155 +++++++++++++++++++++
.../java/org/apache/druid/math/expr/EvalTest.java | 120 ++++++++++++++++
.../expression/UnarySuffixOperatorConversion.java | 64 ---------
.../sql/calcite/planner/DruidOperatorTable.java | 21 +--
.../apache/druid/sql/calcite/CalciteQueryTest.java | 2 +-
5 files changed, 280 insertions(+), 82 deletions(-)
diff --git a/processing/src/main/java/org/apache/druid/math/expr/Function.java
b/processing/src/main/java/org/apache/druid/math/expr/Function.java
index 632425d1c75..406ffac1ea7 100644
--- a/processing/src/main/java/org/apache/druid/math/expr/Function.java
+++ b/processing/src/main/java/org/apache/druid/math/expr/Function.java
@@ -27,6 +27,7 @@ import org.apache.druid.java.util.common.StringUtils;
import org.apache.druid.java.util.common.UOE;
import org.apache.druid.math.expr.vector.CastToTypeVectorProcessor;
import org.apache.druid.math.expr.vector.ExprVectorProcessor;
+import org.apache.druid.math.expr.vector.VectorComparisonProcessors;
import org.apache.druid.math.expr.vector.VectorMathProcessors;
import org.apache.druid.math.expr.vector.VectorProcessors;
import org.apache.druid.math.expr.vector.VectorStringProcessors;
@@ -2224,6 +2225,160 @@ public interface Function extends NamedFunction
}
}
+ /**
+ * SQL function "IS NOT FALSE". Different from "IS TRUE" in that it returns
true for NULL as well.
+ */
+ class IsNotFalseFunc implements Function
+ {
+ @Override
+ public String name()
+ {
+ return "notfalse";
+ }
+
+ @Override
+ public ExprEval apply(List<Expr> args, Expr.ObjectBinding bindings)
+ {
+ final ExprEval arg = args.get(0).eval(bindings);
+ return ExprEval.ofLongBoolean(arg.value() == null || arg.asBoolean());
+ }
+
+ @Override
+ public void validateArguments(List<Expr> args)
+ {
+ validationHelperCheckArgumentCount(args, 1);
+ }
+
+ @Nullable
+ @Override
+ public ExpressionType getOutputType(Expr.InputBindingInspector inspector,
List<Expr> args)
+ {
+ return ExpressionType.LONG;
+ }
+ }
+
+ /**
+ * SQL function "IS NOT TRUE". Different from "IS FALSE" in that it returns
true for NULL as well.
+ */
+ class IsNotTrueFunc implements Function
+ {
+ @Override
+ public String name()
+ {
+ return "nottrue";
+ }
+
+ @Override
+ public ExprEval apply(List<Expr> args, Expr.ObjectBinding bindings)
+ {
+ final ExprEval arg = args.get(0).eval(bindings);
+ return ExprEval.ofLongBoolean(arg.value() == null || !arg.asBoolean());
+ }
+
+ @Override
+ public void validateArguments(List<Expr> args)
+ {
+ validationHelperCheckArgumentCount(args, 1);
+ }
+
+ @Nullable
+ @Override
+ public ExpressionType getOutputType(Expr.InputBindingInspector inspector,
List<Expr> args)
+ {
+ return ExpressionType.LONG;
+ }
+ }
+
+ /**
+ * SQL function "IS FALSE".
+ */
+ class IsFalseFunc implements Function
+ {
+ @Override
+ public String name()
+ {
+ return "isfalse";
+ }
+
+ @Override
+ public ExprEval apply(List<Expr> args, Expr.ObjectBinding bindings)
+ {
+ final ExprEval arg = args.get(0).eval(bindings);
+ return ExprEval.ofLongBoolean(arg.value() != null && !arg.asBoolean());
+ }
+
+ @Override
+ public void validateArguments(List<Expr> args)
+ {
+ validationHelperCheckArgumentCount(args, 1);
+ }
+
+ @Nullable
+ @Override
+ public ExpressionType getOutputType(Expr.InputBindingInspector inspector,
List<Expr> args)
+ {
+ return ExpressionType.LONG;
+ }
+
+ @Override
+ public boolean canVectorize(Expr.InputBindingInspector inspector,
List<Expr> args)
+ {
+ final Expr expr = args.get(0);
+ return inspector.areNumeric(expr) && expr.canVectorize(inspector);
+ }
+
+ @Override
+ public <T> ExprVectorProcessor<T>
asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args)
+ {
+ return VectorComparisonProcessors.lessThanOrEqual(inspector,
args.get(0), ExprEval.of(0L).toExpr());
+ }
+ }
+
+ /**
+ * SQL function "IS TRUE".
+ */
+ class IsTrueFunc implements Function
+ {
+ @Override
+ public String name()
+ {
+ return "istrue";
+ }
+
+ @Override
+ public ExprEval apply(List<Expr> args, Expr.ObjectBinding bindings)
+ {
+ final ExprEval arg = args.get(0).eval(bindings);
+ return ExprEval.ofLongBoolean(arg.asBoolean());
+ }
+
+ @Override
+ public void validateArguments(List<Expr> args)
+ {
+ validationHelperCheckArgumentCount(args, 1);
+ }
+
+ @Nullable
+ @Override
+ public ExpressionType getOutputType(Expr.InputBindingInspector inspector,
List<Expr> args)
+ {
+ return ExpressionType.LONG;
+ }
+
+ @Override
+ public boolean canVectorize(Expr.InputBindingInspector inspector,
List<Expr> args)
+ {
+ final Expr expr = args.get(0);
+ return inspector.areNumeric(expr) && expr.canVectorize(inspector);
+ }
+
+ @Override
+ public <T> ExprVectorProcessor<T>
asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args)
+ {
+ return VectorComparisonProcessors.greaterThan(inspector, args.get(0),
ExprEval.of(0L).toExpr());
+ }
+ }
+
class IsNullFunc implements Function
{
@Override
diff --git a/processing/src/test/java/org/apache/druid/math/expr/EvalTest.java
b/processing/src/test/java/org/apache/druid/math/expr/EvalTest.java
index 97a91cb3969..c49959aa1ae 100644
--- a/processing/src/test/java/org/apache/druid/math/expr/EvalTest.java
+++ b/processing/src/test/java/org/apache/druid/math/expr/EvalTest.java
@@ -99,6 +99,16 @@ public class EvalTest extends InitializedNullHandlingTest
Assert.assertTrue(evalDouble("2.0 == 2.0", bindings) > 0.0);
Assert.assertTrue(evalDouble("2.0 != 1.0", bindings) > 0.0);
+ Assert.assertEquals(0L, evalLong("istrue(0.0)", bindings));
+ Assert.assertEquals(1L, evalLong("isfalse(0.0)", bindings));
+ Assert.assertEquals(1L, evalLong("nottrue(0.0)", bindings));
+ Assert.assertEquals(0L, evalLong("notfalse(0.0)", bindings));
+
+ Assert.assertEquals(1L, evalLong("istrue(1.0)", bindings));
+ Assert.assertEquals(0L, evalLong("isfalse(1.0)", bindings));
+ Assert.assertEquals(0L, evalLong("nottrue(1.0)", bindings));
+ Assert.assertEquals(1L, evalLong("notfalse(1.0)", bindings));
+
Assert.assertTrue(evalDouble("!-1.0", bindings) > 0.0);
Assert.assertTrue(evalDouble("!0.0", bindings) > 0.0);
Assert.assertFalse(evalDouble("!2.0", bindings) > 0.0);
@@ -121,6 +131,16 @@ public class EvalTest extends InitializedNullHandlingTest
Assert.assertEquals(1L, evalLong("2.0 == 2.0", bindings));
Assert.assertEquals(1L, evalLong("2.0 != 1.0", bindings));
+ Assert.assertEquals(0L, evalLong("istrue(0.0)", bindings));
+ Assert.assertEquals(1L, evalLong("isfalse(0.0)", bindings));
+ Assert.assertEquals(1L, evalLong("nottrue(0.0)", bindings));
+ Assert.assertEquals(0L, evalLong("notfalse(0.0)", bindings));
+
+ Assert.assertEquals(1L, evalLong("istrue(1.0)", bindings));
+ Assert.assertEquals(0L, evalLong("isfalse(1.0)", bindings));
+ Assert.assertEquals(0L, evalLong("nottrue(1.0)", bindings));
+ Assert.assertEquals(1L, evalLong("notfalse(1.0)", bindings));
+
Assert.assertEquals(1L, evalLong("!-1.0", bindings));
Assert.assertEquals(1L, evalLong("!0.0", bindings));
Assert.assertEquals(0L, evalLong("!2.0", bindings));
@@ -201,6 +221,106 @@ public class EvalTest extends InitializedNullHandlingTest
assertEquals("x", eval("nvl(if(x == 9223372036854775806, '', 'x'),
'NULL')", bindings).asString());
}
+ @Test
+ public void testIsFalse()
+ {
+ assertEquals(
+ 0L,
+ new Function.IsFalseFunc()
+ .apply(ImmutableList.of(new NullLongExpr()),
InputBindings.nilBindings())
+ .value()
+ );
+
+ assertEquals(
+ 1L,
+ new Function.IsFalseFunc()
+ .apply(ImmutableList.of(new LongExpr(0L)),
InputBindings.nilBindings())
+ .value()
+ );
+
+ assertEquals(
+ 0L,
+ new Function.IsFalseFunc()
+ .apply(ImmutableList.of(new LongExpr(1L)),
InputBindings.nilBindings())
+ .value()
+ );
+ }
+
+ @Test
+ public void testIsTrue()
+ {
+ assertEquals(
+ 0L,
+ new Function.IsTrueFunc()
+ .apply(ImmutableList.of(new NullLongExpr()),
InputBindings.nilBindings())
+ .value()
+ );
+
+ assertEquals(
+ 0L,
+ new Function.IsTrueFunc()
+ .apply(ImmutableList.of(new LongExpr(0L)),
InputBindings.nilBindings())
+ .value()
+ );
+
+ assertEquals(
+ 1L,
+ new Function.IsTrueFunc()
+ .apply(ImmutableList.of(new LongExpr(1L)),
InputBindings.nilBindings())
+ .value()
+ );
+ }
+
+ @Test
+ public void testIsNotFalse()
+ {
+ assertEquals(
+ 1L,
+ new Function.IsNotFalseFunc()
+ .apply(ImmutableList.of(new NullLongExpr()),
InputBindings.nilBindings())
+ .value()
+ );
+
+ assertEquals(
+ 0L,
+ new Function.IsNotFalseFunc()
+ .apply(ImmutableList.of(new LongExpr(0L)),
InputBindings.nilBindings())
+ .value()
+ );
+
+ assertEquals(
+ 1L,
+ new Function.IsNotFalseFunc()
+ .apply(ImmutableList.of(new LongExpr(1L)),
InputBindings.nilBindings())
+ .value()
+ );
+ }
+
+ @Test
+ public void testIsNotTrue()
+ {
+ assertEquals(
+ 1L,
+ new Function.IsNotTrueFunc()
+ .apply(ImmutableList.of(new NullLongExpr()),
InputBindings.nilBindings())
+ .value()
+ );
+
+ assertEquals(
+ 1L,
+ new Function.IsNotTrueFunc()
+ .apply(ImmutableList.of(new LongExpr(0L)),
InputBindings.nilBindings())
+ .value()
+ );
+
+ assertEquals(
+ 0L,
+ new Function.IsNotTrueFunc()
+ .apply(ImmutableList.of(new LongExpr(1L)),
InputBindings.nilBindings())
+ .value()
+ );
+ }
+
@Test
public void testArrayToScalar()
{
diff --git
a/sql/src/main/java/org/apache/druid/sql/calcite/expression/UnarySuffixOperatorConversion.java
b/sql/src/main/java/org/apache/druid/sql/calcite/expression/UnarySuffixOperatorConversion.java
deleted file mode 100644
index 8a38fbe9c93..00000000000
---
a/sql/src/main/java/org/apache/druid/sql/calcite/expression/UnarySuffixOperatorConversion.java
+++ /dev/null
@@ -1,64 +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.sql.calcite.expression;
-
-import com.google.common.collect.Iterables;
-import org.apache.calcite.rex.RexNode;
-import org.apache.calcite.sql.SqlOperator;
-import org.apache.druid.java.util.common.StringUtils;
-import org.apache.druid.segment.column.RowSignature;
-import org.apache.druid.sql.calcite.planner.PlannerContext;
-
-public class UnarySuffixOperatorConversion implements SqlOperatorConversion
-{
- private final SqlOperator operator;
- private final String druidOperator;
-
- public UnarySuffixOperatorConversion(final SqlOperator operator, final
String druidOperator)
- {
- this.operator = operator;
- this.druidOperator = druidOperator;
- }
-
- @Override
- public SqlOperator calciteOperator()
- {
- return operator;
- }
-
- @Override
- public DruidExpression toDruidExpression(
- final PlannerContext plannerContext,
- final RowSignature rowSignature,
- final RexNode rexNode
- )
- {
- return OperatorConversions.convertCallBuilder(
- plannerContext,
- rowSignature,
- rexNode,
- operands -> StringUtils.format(
- "(%s %s)",
- Iterables.getOnlyElement(operands).getExpression(),
- druidOperator
- )
- );
- }
-}
diff --git
a/sql/src/main/java/org/apache/druid/sql/calcite/planner/DruidOperatorTable.java
b/sql/src/main/java/org/apache/druid/sql/calcite/planner/DruidOperatorTable.java
index e8ab0cc71d8..16748b0b6ab 100644
---
a/sql/src/main/java/org/apache/druid/sql/calcite/planner/DruidOperatorTable.java
+++
b/sql/src/main/java/org/apache/druid/sql/calcite/planner/DruidOperatorTable.java
@@ -54,7 +54,6 @@ import
org.apache.druid.sql.calcite.expression.OperatorConversions;
import org.apache.druid.sql.calcite.expression.SqlOperatorConversion;
import org.apache.druid.sql.calcite.expression.UnaryFunctionOperatorConversion;
import org.apache.druid.sql.calcite.expression.UnaryPrefixOperatorConversion;
-import org.apache.druid.sql.calcite.expression.UnarySuffixOperatorConversion;
import org.apache.druid.sql.calcite.expression.WindowSqlAggregate;
import
org.apache.druid.sql.calcite.expression.builtin.ArrayAppendOperatorConversion;
import
org.apache.druid.sql.calcite.expression.builtin.ArrayConcatOperatorConversion;
@@ -372,22 +371,10 @@ public class DruidOperatorTable implements
SqlOperatorTable
.add(new
UnaryPrefixOperatorConversion(SqlStdOperatorTable.UNARY_MINUS, "-"))
.add(new
UnaryFunctionOperatorConversion(SqlStdOperatorTable.IS_NULL, "isnull"))
.add(new
UnaryFunctionOperatorConversion(SqlStdOperatorTable.IS_NOT_NULL, "notnull"))
- .add(new UnarySuffixOperatorConversion(
- SqlStdOperatorTable.IS_FALSE,
- "<= 0"
- )) // Matches Evals.asBoolean
- .add(new UnarySuffixOperatorConversion(
- SqlStdOperatorTable.IS_NOT_TRUE,
- "<= 0"
- )) // Matches Evals.asBoolean
- .add(new UnarySuffixOperatorConversion(
- SqlStdOperatorTable.IS_TRUE,
- "> 0"
- )) // Matches Evals.asBoolean
- .add(new UnarySuffixOperatorConversion(
- SqlStdOperatorTable.IS_NOT_FALSE,
- "> 0"
- )) // Matches Evals.asBoolean
+ .add(new
DirectOperatorConversion(SqlStdOperatorTable.IS_FALSE, "isfalse"))
+ .add(new
DirectOperatorConversion(SqlStdOperatorTable.IS_TRUE, "istrue"))
+ .add(new
DirectOperatorConversion(SqlStdOperatorTable.IS_NOT_FALSE, "notfalse"))
+ .add(new
DirectOperatorConversion(SqlStdOperatorTable.IS_NOT_TRUE, "nottrue"))
.add(new
BinaryOperatorConversion(SqlStdOperatorTable.MULTIPLY, "*"))
.add(new BinaryOperatorConversion(SqlStdOperatorTable.MOD,
"%"))
.add(new
BinaryOperatorConversion(SqlStdOperatorTable.DIVIDE, "/"))
diff --git
a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java
b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java
index 58240800371..499dd8faeb8 100644
--- a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java
+++ b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java
@@ -2461,7 +2461,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
"v0",
NullHandling.replaceWithDefault()
? "(\"cnt\" == 1)"
- : "((\"cnt\" == 1) > 0)",
+ : "istrue((\"cnt\" == 1))",
ColumnType.LONG
))
.setDimensions(dimensions(
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]