You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@druid.apache.org by GitBox <gi...@apache.org> on 2021/07/15 05:15:25 UTC

[GitHub] [druid] suneet-s commented on a change in pull request #11184: vectorize logical operators and boolean functions

suneet-s commented on a change in pull request #11184:
URL: https://github.com/apache/druid/pull/11184#discussion_r670128506



##########
File path: core/src/test/java/org/apache/druid/math/expr/EvalTest.java
##########
@@ -372,4 +372,111 @@ public void testBooleanReturn()
     Assert.assertFalse(eval.asBoolean());
     Assert.assertEquals(ExprType.DOUBLE, eval.type());
   }
+
+  @Test
+  public void testLogicalOperators()
+  {
+    Expr.ObjectBinding bindings = InputBindings.withMap(
+        ImmutableMap.of()
+    );
+
+    Assert.assertEquals("true", eval("'true' && 'true'", bindings).value());
+    Assert.assertEquals("false", eval("'true' && 'false'", bindings).value());
+    Assert.assertEquals("false", eval("'false' && 'true'", bindings).value());
+    Assert.assertEquals("false", eval("'troo' && 'true'", bindings).value());
+    Assert.assertEquals("false", eval("'false' && 'false'", bindings).value());
+
+    Assert.assertEquals("true", eval("'true' || 'true'", bindings).value());
+    Assert.assertEquals("true", eval("'true' || 'false'", bindings).value());
+    Assert.assertEquals("true", eval("'false' || 'true'", bindings).value());
+    Assert.assertEquals("true", eval("'troo' || 'true'", bindings).value());
+    Assert.assertEquals("false", eval("'false' || 'false'", bindings).value());
+
+    Assert.assertEquals(1L, eval("1 && 1", bindings).value());
+    Assert.assertEquals(1L, eval("100 && 11", bindings).value());
+    Assert.assertEquals(0L, eval("1 && 0", bindings).value());
+    Assert.assertEquals(0L, eval("0 && 1", bindings).value());
+    Assert.assertEquals(0L, eval("0 && 0", bindings).value());
+
+    Assert.assertEquals(1L, eval("1 || 1", bindings).value());
+    Assert.assertEquals(1L, eval("100 || 11", bindings).value());
+    Assert.assertEquals(1L, eval("1 || 0", bindings).value());
+    Assert.assertEquals(1L, eval("0 || 1", bindings).value());
+    Assert.assertEquals(1L, eval("111 || 0", bindings).value());
+    Assert.assertEquals(1L, eval("0 || 111", bindings).value());
+    Assert.assertEquals(0L, eval("0 || 0", bindings).value());
+
+    Assert.assertEquals(1.0, eval("1.0 && 1.0", bindings).value());
+    Assert.assertEquals(1.0, eval("0.100 && 1.1", bindings).value());
+    Assert.assertEquals(0.0, eval("1.0 && 0.0", bindings).value());
+    Assert.assertEquals(0.0, eval("0.0 && 1.0", bindings).value());
+    Assert.assertEquals(0.0, eval("0.0 && 0.0", bindings).value());
+
+    Assert.assertEquals(1.0, eval("1.0 || 1.0", bindings).value());
+    Assert.assertEquals(1.0, eval("0.2 || 0.3", bindings).value());
+    Assert.assertEquals(1.0, eval("1.0 || 0.0", bindings).value());
+    Assert.assertEquals(1.0, eval("0.0 || 1.0", bindings).value());
+    Assert.assertEquals(1.0, eval("1.11 || 0.0", bindings).value());
+    Assert.assertEquals(1.0, eval("0.0 || 0.111", bindings).value());
+    Assert.assertEquals(0.0, eval("0.0 || 0.0", bindings).value());
+
+    Assert.assertEquals(1L, eval("null || 1", bindings).value());
+    Assert.assertEquals(1L, eval("1 || null", bindings).value());
+    Assert.assertEquals(NullHandling.defaultLongValue(), eval("null || 0", bindings).value());
+    Assert.assertEquals(NullHandling.defaultLongValue(), eval("0 || null", bindings).value());
+    // null/null is evaluated as string typed
+    Assert.assertEquals(null, eval("null || null", bindings).value());
+
+    Assert.assertEquals(NullHandling.defaultLongValue(), eval("null && 1", bindings).value());
+    Assert.assertEquals(NullHandling.defaultLongValue(), eval("1 && null", bindings).value());
+    Assert.assertEquals(0L, eval("null && 0", bindings).value());
+    Assert.assertEquals(0L, eval("0 && null", bindings).value());
+    // null/null is evaluated as string typed
+    Assert.assertEquals(null, eval("null && null", bindings).value());
+
+
+    // turn on legacy insanity mode
+    NullHandling.initializeForLegacyLogicalOperationsTests(true);
+    Assert.assertEquals(1L, eval("1 && 1", bindings).value());
+    Assert.assertEquals(11L, eval("100 && 11", bindings).value());
+    Assert.assertEquals(0L, eval("1 && 0", bindings).value());
+    Assert.assertEquals(0L, eval("0 && 1", bindings).value());
+    Assert.assertEquals(0L, eval("0 && 0", bindings).value());
+
+    Assert.assertEquals(1L, eval("1 || 1", bindings).value());
+    Assert.assertEquals(100L, eval("100 || 11", bindings).value());
+    Assert.assertEquals(1L, eval("1 || 0", bindings).value());
+    Assert.assertEquals(1L, eval("0 || 1", bindings).value());
+    Assert.assertEquals(111L, eval("111 || 0", bindings).value());
+    Assert.assertEquals(111L, eval("0 || 111", bindings).value());
+    Assert.assertEquals(0L, eval("0 || 0", bindings).value());
+
+    Assert.assertEquals(1.0, eval("1.0 && 1.0", bindings).value());
+    Assert.assertEquals(1.1, eval("0.100 && 1.1", bindings).value());
+    Assert.assertEquals(0.0, eval("1.0 && 0.0", bindings).value());
+    Assert.assertEquals(0.0, eval("0.0 && 1.0", bindings).value());
+    Assert.assertEquals(0.0, eval("0.0 && 0.0", bindings).value());
+
+    Assert.assertEquals(1.0, eval("1.0 || 1.0", bindings).value());
+    Assert.assertEquals(0.2, eval("0.2 || 0.3", bindings).value());
+    Assert.assertEquals(1.0, eval("1.0 || 0.0", bindings).value());
+    Assert.assertEquals(1.0, eval("0.0 || 1.0", bindings).value());
+    Assert.assertEquals(1.11, eval("1.11 || 0.0", bindings).value());
+    Assert.assertEquals(0.111, eval("0.0 || 0.111", bindings).value());
+    Assert.assertEquals(0.0, eval("0.0 || 0.0", bindings).value());
+
+    Assert.assertEquals(1L, eval("null || 1", bindings).value());
+    Assert.assertEquals(1L, eval("1 || null", bindings).value());
+    Assert.assertEquals(0L, eval("null || 0", bindings).value());
+    Assert.assertEquals(null, eval("0 || null", bindings).value());
+    Assert.assertEquals(null, eval("null || null", bindings).value());
+
+    Assert.assertEquals(null, eval("null && 1", bindings).value());
+    Assert.assertEquals(null, eval("1 && null", bindings).value());
+    Assert.assertEquals(null, eval("null && 0", bindings).value());
+    Assert.assertEquals(0L, eval("0 && null", bindings).value());
+    Assert.assertEquals(null, eval("null && null", bindings).value());
+    // reset
+    NullHandling.initializeForTests();

Review comment:
       I think you will want this in a try - finally block so that if 1 test fails here, it doesn't throw off any other tests that run in the JVM

##########
File path: core/src/main/java/org/apache/druid/math/expr/Expr.java
##########
@@ -222,6 +222,25 @@ default boolean areNumeric(Expr... args)
       return areNumeric(Arrays.asList(args));
     }
 
+    default boolean areSameTypes(List<Expr> args)
+    {
+      ExprType currentType = null;
+      boolean allSame = true;
+      for (Expr arg : args) {
+        ExprType argType = arg.getOutputType(this);
+        if (currentType == null) {
+          currentType = argType;
+        }
+        allSame &= argType == currentType;
+      }
+      return allSame;

Review comment:
       if I'm understanding this correctly - a constant expression of a string and a constant expression of null will not be considered the same type. Is this the behavior we want?
   
   I was thinking that a null could in theory be any type so `null && String` coulbe the same type

##########
File path: core/src/main/java/org/apache/druid/math/expr/vector/VectorProcessors.java
##########
@@ -135,6 +178,660 @@ public void processIndex(String[] strings, long[] longs, boolean[] outputNulls,
     return (ExprVectorProcessor<T>) processor;
   }
 
+  public static <T> ExprVectorProcessor<T> isNull(Expr.VectorInputBindingInspector inspector, Expr expr)
+  {
+
+    final ExprType type = expr.getOutputType(inspector);
+
+    if (type == null) {
+      return constant(1L, inspector.getMaxVectorSize());
+    }
+    final long[] outputValues = new long[inspector.getMaxVectorSize()];
+
+    ExprVectorProcessor<?> processor = null;
+    if (ExprType.STRING == type) {
+      final ExprVectorProcessor<String[]> input = expr.buildVectorized(inspector);
+      processor = new ExprVectorProcessor<long[]>()
+      {
+        @Override
+        public ExprEvalVector<long[]> evalVector(Expr.VectorInputBinding bindings)
+        {
+          final ExprEvalVector<String[]> inputEval = input.evalVector(bindings);
+
+          final int currentSize = bindings.getCurrentVectorSize();
+          final String[] values = inputEval.values();
+          for (int i = 0; i < currentSize; i++) {
+            if (values[i] == null) {
+              outputValues[i] = 1L;
+            } else {
+              outputValues[i] = 0L;
+            }
+          }
+          return new ExprEvalLongVector(outputValues, null);
+        }
+
+        @Override
+        public ExprType getOutputType()
+        {
+          return ExprType.LONG;
+        }
+      };
+    } else if (ExprType.LONG == type) {
+      final ExprVectorProcessor<long[]> input = expr.buildVectorized(inspector);
+      processor = new ExprVectorProcessor<long[]>()
+      {
+        @Override
+        public ExprEvalVector<long[]> evalVector(Expr.VectorInputBinding bindings)
+        {
+          final ExprEvalVector<long[]> inputEval = input.evalVector(bindings);
+
+          final int currentSize = bindings.getCurrentVectorSize();
+          final boolean[] nulls = inputEval.getNullVector();
+          for (int i = 0; i < currentSize; i++) {
+            if (nulls != null && nulls[i]) {
+              outputValues[i] = 1L;
+            } else {
+              outputValues[i] = 0L;
+            }
+          }
+          return new ExprEvalLongVector(outputValues, null);
+        }
+
+        @Override
+        public ExprType getOutputType()
+        {
+          return ExprType.LONG;
+        }
+      };
+    } else if (ExprType.DOUBLE == type) {
+      final ExprVectorProcessor<double[]> input = expr.buildVectorized(inspector);
+      processor = new ExprVectorProcessor<long[]>()
+      {
+        @Override
+        public ExprEvalVector<long[]> evalVector(Expr.VectorInputBinding bindings)
+        {
+          final ExprEvalVector<double[]> inputEval = input.evalVector(bindings);
+
+          final int currentSize = bindings.getCurrentVectorSize();
+          final boolean[] nulls = inputEval.getNullVector();
+          for (int i = 0; i < currentSize; i++) {
+            if (nulls != null && nulls[i]) {
+              outputValues[i] = 1L;
+            } else {
+              outputValues[i] = 0L;
+            }
+          }
+          return new ExprEvalLongVector(outputValues, null);
+        }
+
+        @Override
+        public ExprType getOutputType()
+        {
+          return ExprType.LONG;
+        }
+      };
+    }
+
+    if (processor == null) {
+      throw Exprs.cannotVectorize();
+    }
+    return (ExprVectorProcessor<T>) processor;
+  }
+
+  public static <T> ExprVectorProcessor<T> isNotNull(Expr.VectorInputBindingInspector inspector, Expr expr)
+  {
+
+    final ExprType type = expr.getOutputType(inspector);
+    if (type == null) {
+      return constant(0L, inspector.getMaxVectorSize());
+    }
+
+    final long[] outputValues = new long[inspector.getMaxVectorSize()];
+
+    ExprVectorProcessor<?> processor = null;
+    if (ExprType.STRING == type) {
+      final ExprVectorProcessor<String[]> input = expr.buildVectorized(inspector);
+      processor = new ExprVectorProcessor<long[]>()
+      {
+        @Override
+        public ExprEvalVector<long[]> evalVector(Expr.VectorInputBinding bindings)
+        {
+          final ExprEvalVector<String[]> inputEval = input.evalVector(bindings);
+
+          final int currentSize = bindings.getCurrentVectorSize();
+          final String[] values = inputEval.values();
+          for (int i = 0; i < currentSize; i++) {
+            if (values[i] == null) {
+              outputValues[i] = 0L;
+            } else {
+              outputValues[i] = 1L;
+            }
+          }
+          return new ExprEvalLongVector(outputValues, null);
+        }
+
+        @Override
+        public ExprType getOutputType()
+        {
+          return ExprType.LONG;
+        }
+      };
+    } else if (ExprType.LONG == type) {
+      final ExprVectorProcessor<long[]> input = expr.buildVectorized(inspector);
+      processor = new ExprVectorProcessor<long[]>()
+      {
+        @Override
+        public ExprEvalVector<long[]> evalVector(Expr.VectorInputBinding bindings)
+        {
+          final ExprEvalVector<long[]> inputEval = input.evalVector(bindings);
+
+          final int currentSize = bindings.getCurrentVectorSize();
+          final boolean[] nulls = inputEval.getNullVector();
+          for (int i = 0; i < currentSize; i++) {
+            if (nulls != null && nulls[i]) {
+              outputValues[i] = 0L;
+            } else {
+              outputValues[i] = 1L;
+            }
+          }
+          return new ExprEvalLongVector(outputValues, null);
+        }
+
+        @Override
+        public ExprType getOutputType()
+        {
+          return ExprType.LONG;
+        }
+      };
+    } else if (ExprType.DOUBLE == type) {
+      final ExprVectorProcessor<double[]> input = expr.buildVectorized(inspector);
+      processor = new ExprVectorProcessor<long[]>()
+      {
+        @Override
+        public ExprEvalVector<long[]> evalVector(Expr.VectorInputBinding bindings)
+        {
+          final ExprEvalVector<double[]> inputEval = input.evalVector(bindings);
+
+          final int currentSize = bindings.getCurrentVectorSize();
+          final boolean[] nulls = inputEval.getNullVector();
+          for (int i = 0; i < currentSize; i++) {
+            if (nulls != null && nulls[i]) {
+              outputValues[i] = 0L;
+            } else {
+              outputValues[i] = 1L;
+            }
+          }
+          return new ExprEvalLongVector(outputValues, null);
+        }
+
+        @Override
+        public ExprType getOutputType()
+        {
+          return ExprType.LONG;
+        }
+      };
+    }
+
+    if (processor == null) {
+      throw Exprs.cannotVectorize();
+    }
+    return (ExprVectorProcessor<T>) processor;
+  }
+
+  public static <T> ExprVectorProcessor<T> nvl(Expr.VectorInputBindingInspector inspector, Expr left, Expr right)
+  {
+    final int maxVectorSize = inspector.getMaxVectorSize();
+
+    return makeSymmetricalProcessor(
+        inspector,
+        left,
+        right,
+        () -> new SymmetricalBivariateFunctionVectorProcessor<long[]>(
+            ExprType.LONG,
+            left.buildVectorized(inspector),
+            right.buildVectorized(inspector)
+        )
+        {
+          final long[] output = new long[maxVectorSize];
+          final boolean[] outputNulls = new boolean[maxVectorSize];
+
+          @Override
+          public void processIndex(
+              long[] leftInput,
+              @Nullable boolean[] leftNulls,
+              long[] rightInput,
+              @Nullable boolean[] rightNulls,
+              int i
+          )
+          {
+            if (leftNulls != null && leftNulls[i]) {
+              if (rightNulls != null) {
+                output[i] = rightNulls[i] ? 0L : rightInput[i];
+                outputNulls[i] = rightNulls[i];
+              } else {
+                output[i] = rightInput[i];
+              }
+            } else {
+              output[i] = leftInput[i];
+            }
+          }
+
+          @Override
+          public ExprEvalVector<long[]> asEval()
+          {
+            return new ExprEvalLongVector(output, outputNulls);
+          }
+        },
+        () -> new SymmetricalBivariateFunctionVectorProcessor<double[]>(
+            ExprType.DOUBLE,
+            left.buildVectorized(inspector),
+            right.buildVectorized(inspector)
+        )
+        {
+          final double[] output = new double[maxVectorSize];
+          final boolean[] outputNulls = new boolean[maxVectorSize];
+
+          @Override
+          public void processIndex(
+              double[] leftInput,
+              @Nullable boolean[] leftNulls,
+              double[] rightInput,
+              @Nullable boolean[] rightNulls,
+              int i
+          )
+          {
+            if (leftNulls != null && leftNulls[i]) {
+              if (rightNulls != null) {
+                output[i] = rightNulls[i] ? 0.0 : rightInput[i];
+                outputNulls[i] = rightNulls[i];
+              } else {
+                output[i] = rightInput[i];
+              }
+            } else {
+              output[i] = leftInput[i];
+            }
+          }
+
+          @Override
+          public ExprEvalVector<double[]> asEval()
+          {
+            return new ExprEvalDoubleVector(output, outputNulls);
+          }
+        },
+        () -> new SymmetricalBivariateFunctionVectorProcessor<String[]>(
+            ExprType.STRING,
+            left.buildVectorized(inspector),
+            right.buildVectorized(inspector)
+        )
+        {
+          final String[] output = new String[maxVectorSize];
+
+          @Override
+          public void processIndex(
+              String[] leftInput,
+              @Nullable boolean[] leftNulls,
+              String[] rightInput,
+              @Nullable boolean[] rightNulls,
+              int i
+          )
+          {
+            output[i] = leftInput[i] != null ? leftInput[i] : rightInput[i];
+          }
+
+          @Override
+          public ExprEvalVector<String[]> asEval()
+          {
+            return new ExprEvalStringVector(output);
+          }
+        }
+    );
+  }
+
+  public static <T> ExprVectorProcessor<T> not(Expr.VectorInputBindingInspector inspector, Expr expr)
+  {
+    final ExprType inputType = expr.getOutputType(inspector);
+    final int maxVectorSize = inspector.getMaxVectorSize();
+    ExprVectorProcessor<?> processor = null;
+    if (ExprType.STRING.equals(inputType)) {
+      processor = new LongOutStringInFunctionVectorProcessor(expr.buildVectorized(inspector), maxVectorSize)
+      {
+        @Override
+        public void processIndex(String[] strings, long[] longs, boolean[] outputNulls, int i)
+        {
+          outputNulls[i] = strings[i] == null;
+          if (!outputNulls[i]) {
+            longs[i] = Evals.asLong(!Evals.asBoolean(strings[i]));
+          }
+        }
+      };
+    } else if (ExprType.LONG.equals(inputType)) {
+      processor = new LongOutLongInFunctionVectorValueProcessor(expr.buildVectorized(inspector), maxVectorSize)
+      {
+        @Override
+        public long apply(long input)
+        {
+          return Evals.asLong(!Evals.asBoolean(input));
+        }
+      };
+    } else if (ExprType.DOUBLE.equals(inputType)) {
+      processor = new DoubleOutDoubleInFunctionVectorValueProcessor(expr.buildVectorized(inspector), maxVectorSize)
+      {
+        @Override
+        public double apply(double input)
+        {
+          return Evals.asDouble(!Evals.asBoolean(input));
+        }
+      };
+    }
+    if (processor == null) {
+      throw Exprs.cannotVectorize();
+    }
+    return (ExprVectorProcessor<T>) processor;
+  }
+
+  public static <T> ExprVectorProcessor<T> or(Expr.VectorInputBindingInspector inspector, Expr left, Expr right)
+  {
+    final int maxVectorSize = inspector.getMaxVectorSize();
+    return makeSymmetricalProcessor(
+        inspector,
+        left,
+        right,
+        () -> new SymmetricalBivariateFunctionVectorProcessor<long[]>(
+            ExprType.LONG,
+            left.buildVectorized(inspector),
+            right.buildVectorized(inspector)
+        )
+        {
+          final long[] output = new long[maxVectorSize];
+          final boolean[] outputNulls = new boolean[maxVectorSize];
+
+          @Override
+          public void processIndex(
+              long[] leftInput,
+              @Nullable boolean[] leftNulls,
+              long[] rightInput,
+              @Nullable boolean[] rightNulls,
+              int i
+          )
+          {
+            if (NullHandling.sqlCompatible()) {
+              // true/null, null/true, null/null -> true
+              // false/null, null/false -> null

Review comment:
       It seems like this comment doesn't match the implementation
   
   `null /null -> null` which is what's described in `docs/misc/math-expr.md` but this comment says it should be true.

##########
File path: docs/misc/math-expr.md
##########
@@ -254,7 +256,34 @@ supported features:
 * constants and identifiers are supported for any column type
 * `cast` is supported for numeric and string types
 * math operators: `+`,`-`,`*`,`/`,`%`,`^` are supported for numeric types
-* comparison operators: `=`, `!=`, `>`, `>=`, `<`, `<=` are supported for numeric types
+* logical operators: `!`, `&&`, `||`, are supported for string and numeric types
+* comparison operators: `=`, `!=`, `>`, `>=`, `<`, `<=` are supported for string and numeric types
 * math functions: `abs`, `acos`, `asin`, `atan`, `cbrt`, `ceil`, `cos`, `cosh`, `cot`, `exp`, `expm1`, `floor`, `getExponent`, `log`, `log10`, `log1p`, `nextUp`, `rint`, `signum`, `sin`, `sinh`, `sqrt`, `tan`, `tanh`, `toDegrees`, `toRadians`, `ulp`, `atan2`, `copySign`, `div`, `hypot`, `max`, `min`, `nextAfter`,  `pow`, `remainder`, `scalb` are supported for numeric types
 * time functions: `timestamp_floor` (with constant granularity argument) is supported for numeric types
+* boolean functions: `isnull`, `notnull` are supported for string and numeric types
+* conditional functions: `nvl` is supported for string and numeric types
+* string functions: the concatenation operator (`+`) and `concat` function are supported for string and numeric types
 * other: `parse_long` is supported for numeric and string types
+
+## Legacy logical operator mode
+In earlier releases of Druid, the logical 'and' and 'or' operators behaved in a non-standard manner, but this behavior has been changed so that these operations output 'homogeneous' boolean values.

Review comment:
       nit: Perhaps we can be more specific about which version.
   ```suggestion
   Prior to the 0.22 release of Apache Druid, the logical 'and' and 'or' operators behaved in a non-standard manner, but this behavior has been changed so that these operations output 'homogeneous' boolean values.
   ```

##########
File path: core/src/main/java/org/apache/druid/math/expr/vector/VectorProcessors.java
##########
@@ -135,6 +178,660 @@ public void processIndex(String[] strings, long[] longs, boolean[] outputNulls,
     return (ExprVectorProcessor<T>) processor;
   }
 
+  public static <T> ExprVectorProcessor<T> isNull(Expr.VectorInputBindingInspector inspector, Expr expr)
+  {
+
+    final ExprType type = expr.getOutputType(inspector);
+
+    if (type == null) {
+      return constant(1L, inspector.getMaxVectorSize());
+    }
+    final long[] outputValues = new long[inspector.getMaxVectorSize()];
+
+    ExprVectorProcessor<?> processor = null;
+    if (ExprType.STRING == type) {
+      final ExprVectorProcessor<String[]> input = expr.buildVectorized(inspector);
+      processor = new ExprVectorProcessor<long[]>()
+      {
+        @Override
+        public ExprEvalVector<long[]> evalVector(Expr.VectorInputBinding bindings)
+        {
+          final ExprEvalVector<String[]> inputEval = input.evalVector(bindings);
+
+          final int currentSize = bindings.getCurrentVectorSize();
+          final String[] values = inputEval.values();
+          for (int i = 0; i < currentSize; i++) {
+            if (values[i] == null) {
+              outputValues[i] = 1L;
+            } else {
+              outputValues[i] = 0L;
+            }
+          }
+          return new ExprEvalLongVector(outputValues, null);
+        }
+
+        @Override
+        public ExprType getOutputType()
+        {
+          return ExprType.LONG;
+        }
+      };
+    } else if (ExprType.LONG == type) {
+      final ExprVectorProcessor<long[]> input = expr.buildVectorized(inspector);
+      processor = new ExprVectorProcessor<long[]>()
+      {
+        @Override
+        public ExprEvalVector<long[]> evalVector(Expr.VectorInputBinding bindings)
+        {
+          final ExprEvalVector<long[]> inputEval = input.evalVector(bindings);
+
+          final int currentSize = bindings.getCurrentVectorSize();
+          final boolean[] nulls = inputEval.getNullVector();
+          for (int i = 0; i < currentSize; i++) {
+            if (nulls != null && nulls[i]) {

Review comment:
       nit: is the optimizer smart enough to optimize this to
   ```suggestion
             for (int i = 0; nulls !=null && i < currentSize; i++) {
               if (nulls[i]) {
   ```
   
   since if `nulls == null` we don't have to iterate over the list because long arrays default to 0L iirc.
   Similar comment elsewhere this pattern is used




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@druid.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@druid.apache.org
For additional commands, e-mail: commits-help@druid.apache.org