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 17eaeb003f9 vectorize if function when then and else expressions have 
the same output type (#18507)
17eaeb003f9 is described below

commit 17eaeb003f9638afa7cefbaca276da988992f124
Author: Clint Wylie <[email protected]>
AuthorDate: Wed Sep 10 11:38:37 2025 -0700

    vectorize if function when then and else expressions have the same output 
type (#18507)
    
    * vectorize if function when then and else expressions have the same output 
type
    
    * fix style
    
    * fix test
    
    * add comments, more test
    
    * return false
---
 .../java/org/apache/druid/math/expr/Function.java  |  15 +++
 .../math/expr/vector/ExprEvalBindingVector.java    |  58 +++++++++---
 .../math/expr/vector/ExprEvalDoubleVector.java     |  10 ++
 .../druid/math/expr/vector/ExprEvalLongVector.java |  10 ++
 .../math/expr/vector/ExprEvalObjectVector.java     |   9 ++
 .../druid/math/expr/vector/ExprEvalVector.java     |   2 +
 .../math/expr/vector/IfDoubleVectorProcessor.java  | 100 ++++++++++++++++++++
 .../expr/vector/IfFunctionVectorProcessor.java     |  59 ++++++++++++
 .../math/expr/vector/IfLongVectorProcessor.java    | 101 +++++++++++++++++++++
 .../math/expr/vector/IfObjectVectorProcessor.java  |  82 +++++++++++++++++
 .../expr/vector/VectorConditionalProcessors.java   |  40 ++++++++
 .../math/expr/VectorExprResultConsistencyTest.java |  25 +++++
 .../timeseries/TimeseriesQueryRunnerTest.java      |   1 -
 13 files changed, 500 insertions(+), 12 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 3f2a357b674..c5b530192a5 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
@@ -2140,6 +2140,21 @@ public interface Function extends NamedFunction
     {
       return ExpressionTypeConversion.conditional(inspector, args.subList(1, 
3));
     }
+
+    @Override
+    public boolean canVectorize(Expr.InputBindingInspector inspector, 
List<Expr> args)
+    {
+      // vector engine requires consistent typing, but native if function does 
not coerce then and else expressions,
+      // so for now we can only vectorize if both args have the same output 
type to not have a behavior change
+      final ExpressionType thenType = args.get(1).getOutputType(inspector);
+      return Objects.equals(thenType, args.get(2).getOutputType(inspector));
+    }
+
+    @Override
+    public <T> ExprVectorProcessor<T> 
asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args)
+    {
+      return VectorConditionalProcessors.ifFunction(inspector, args.get(0), 
args.get(1), args.get(2));
+    }
   }
 
   /**
diff --git 
a/processing/src/main/java/org/apache/druid/math/expr/vector/ExprEvalBindingVector.java
 
b/processing/src/main/java/org/apache/druid/math/expr/vector/ExprEvalBindingVector.java
index 2a0a43e4edb..72750c977e0 100644
--- 
a/processing/src/main/java/org/apache/druid/math/expr/vector/ExprEvalBindingVector.java
+++ 
b/processing/src/main/java/org/apache/druid/math/expr/vector/ExprEvalBindingVector.java
@@ -43,6 +43,7 @@ public class ExprEvalBindingVector<T> implements 
ExprEvalVector<T>
 
   @Nullable
   private boolean[] numericNulls;
+  private Object[] objects;
 
   public ExprEvalBindingVector(
       ExpressionType expressionType,
@@ -76,9 +77,10 @@ public class ExprEvalBindingVector<T> implements 
ExprEvalVector<T>
   public long[] getLongVector()
   {
     if (expressionType.isNumeric()) {
-      return bindings.getLongVector(bindingName);
+      longs = bindings.getLongVector(bindingName);
+    } else {
+      computeNumbers();
     }
-    computeNumbers();
     return longs;
   }
 
@@ -86,9 +88,10 @@ public class ExprEvalBindingVector<T> implements 
ExprEvalVector<T>
   public double[] getDoubleVector()
   {
     if (expressionType.isNumeric()) {
-      return bindings.getDoubleVector(bindingName);
+      doubles = bindings.getDoubleVector(bindingName);
+    } else {
+      computeNumbers();
     }
-    computeNumbers();
     return doubles;
   }
 
@@ -97,9 +100,10 @@ public class ExprEvalBindingVector<T> implements 
ExprEvalVector<T>
   public boolean[] getNullVector()
   {
     if (expressionType.isNumeric()) {
-      return bindings.getNullVector(bindingName);
+      numericNulls = bindings.getNullVector(bindingName);
+    } else {
+      computeNumbers();
     }
-    computeNumbers();
     return numericNulls;
   }
 
@@ -109,7 +113,7 @@ public class ExprEvalBindingVector<T> implements 
ExprEvalVector<T>
     if (expressionType.is(ExprType.LONG)) {
       final long[] values = bindings.getLongVector(bindingName);
       final boolean[] nulls = bindings.getNullVector(bindingName);
-      final Long[] objects = new Long[values.length];
+      objects = new Long[values.length];
       if (nulls != null) {
         for (int i = 0; i < values.length; i++) {
           objects[i] = nulls[i] ? null : values[i];
@@ -119,11 +123,10 @@ public class ExprEvalBindingVector<T> implements 
ExprEvalVector<T>
           objects[i] = values[i];
         }
       }
-      return objects;
     } else if (expressionType.is(ExprType.DOUBLE)) {
       final double[] values = bindings.getDoubleVector(bindingName);
       final boolean[] nulls = bindings.getNullVector(bindingName);
-      Double[] objects = new Double[values.length];
+      objects = new Double[values.length];
       if (nulls != null) {
         for (int i = 0; i < values.length; i++) {
           objects[i] = nulls[i] ? null : values[i];
@@ -133,9 +136,42 @@ public class ExprEvalBindingVector<T> implements 
ExprEvalVector<T>
           objects[i] = values[i];
         }
       }
-      return objects;
+    } else {
+      objects = bindings.getObjectVector(bindingName);
+    }
+    return objects;
+  }
+
+  @Override
+  public boolean elementAsBoolean(int index)
+  {
+    if (expressionType.is(ExprType.LONG)) {
+      if (longs == null) {
+        // populate stuff
+        getLongVector();
+      }
+      if (numericNulls != null && numericNulls[index]) {
+        return Evals.asBoolean(0L);
+      }
+      return Evals.asBoolean(longs[index]);
+    } else if (expressionType.is(ExprType.DOUBLE)) {
+      if (doubles == null) {
+        getDoubleVector();
+      }
+      if (numericNulls != null && numericNulls[index]) {
+        return Evals.asBoolean(0.0);
+      }
+      return Evals.asBoolean(doubles[index]);
+    } else {
+      if (objects == null) {
+        getObjectVector();
+      }
+      if (expressionType.is(ExprType.STRING)) {
+        return Evals.asBoolean((String) objects[index]);
+      } else {
+        return ExprEval.ofType(expressionType, objects[index]).asBoolean();
+      }
     }
-    return bindings.getObjectVector(bindingName);
   }
 
   private void computeNumbers()
diff --git 
a/processing/src/main/java/org/apache/druid/math/expr/vector/ExprEvalDoubleVector.java
 
b/processing/src/main/java/org/apache/druid/math/expr/vector/ExprEvalDoubleVector.java
index 877a3a4f726..5f484a25041 100644
--- 
a/processing/src/main/java/org/apache/druid/math/expr/vector/ExprEvalDoubleVector.java
+++ 
b/processing/src/main/java/org/apache/druid/math/expr/vector/ExprEvalDoubleVector.java
@@ -19,6 +19,7 @@
 
 package org.apache.druid.math.expr.vector;
 
+import org.apache.druid.math.expr.Evals;
 import org.apache.druid.math.expr.ExpressionType;
 
 import java.util.Arrays;
@@ -69,4 +70,13 @@ public final class ExprEvalDoubleVector extends 
BaseExprEvalVector<double[]>
     }
     return objects;
   }
+
+  @Override
+  public boolean elementAsBoolean(int index)
+  {
+    if (nulls != null && nulls[index]) {
+      return false;
+    }
+    return Evals.asBoolean(values[index]);
+  }
 }
diff --git 
a/processing/src/main/java/org/apache/druid/math/expr/vector/ExprEvalLongVector.java
 
b/processing/src/main/java/org/apache/druid/math/expr/vector/ExprEvalLongVector.java
index b33eb968f17..1857694efe6 100644
--- 
a/processing/src/main/java/org/apache/druid/math/expr/vector/ExprEvalLongVector.java
+++ 
b/processing/src/main/java/org/apache/druid/math/expr/vector/ExprEvalLongVector.java
@@ -19,6 +19,7 @@
 
 package org.apache.druid.math.expr.vector;
 
+import org.apache.druid.math.expr.Evals;
 import org.apache.druid.math.expr.ExpressionType;
 
 import javax.annotation.Nullable;
@@ -65,4 +66,13 @@ public final class ExprEvalLongVector extends 
BaseExprEvalVector<long[]>
     return objects;
   }
 
+  @Override
+  public boolean elementAsBoolean(int index)
+  {
+    if (nulls != null && nulls[index]) {
+      return false;
+    }
+    return Evals.asBoolean(values[index]);
+  }
+
 }
diff --git 
a/processing/src/main/java/org/apache/druid/math/expr/vector/ExprEvalObjectVector.java
 
b/processing/src/main/java/org/apache/druid/math/expr/vector/ExprEvalObjectVector.java
index 888a4c59f8c..0bbabc12714 100644
--- 
a/processing/src/main/java/org/apache/druid/math/expr/vector/ExprEvalObjectVector.java
+++ 
b/processing/src/main/java/org/apache/druid/math/expr/vector/ExprEvalObjectVector.java
@@ -113,4 +113,13 @@ public final class ExprEvalObjectVector extends 
BaseExprEvalVector<Object[]>
   {
     return values;
   }
+
+  @Override
+  public boolean elementAsBoolean(int index)
+  {
+    if (type.is(ExprType.STRING)) {
+      return Evals.asBoolean((String) values[index]);
+    }
+    return ExprEval.ofType(type, values[index]).asBoolean();
+  }
 }
diff --git 
a/processing/src/main/java/org/apache/druid/math/expr/vector/ExprEvalVector.java
 
b/processing/src/main/java/org/apache/druid/math/expr/vector/ExprEvalVector.java
index 6f65ce31fdf..2121001066b 100644
--- 
a/processing/src/main/java/org/apache/druid/math/expr/vector/ExprEvalVector.java
+++ 
b/processing/src/main/java/org/apache/druid/math/expr/vector/ExprEvalVector.java
@@ -40,4 +40,6 @@ public interface ExprEvalVector<T>
   long[] getLongVector();
   double[] getDoubleVector();
   Object[] getObjectVector();
+
+  boolean elementAsBoolean(int index);
 }
diff --git 
a/processing/src/main/java/org/apache/druid/math/expr/vector/IfDoubleVectorProcessor.java
 
b/processing/src/main/java/org/apache/druid/math/expr/vector/IfDoubleVectorProcessor.java
new file mode 100644
index 00000000000..3919b180fa3
--- /dev/null
+++ 
b/processing/src/main/java/org/apache/druid/math/expr/vector/IfDoubleVectorProcessor.java
@@ -0,0 +1,100 @@
+/*
+ * 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.vector;
+
+import org.apache.druid.math.expr.Expr;
+import org.apache.druid.math.expr.ExpressionType;
+
+public class IfDoubleVectorProcessor extends 
IfFunctionVectorProcessor<double[]>
+{
+  private final double[] output;
+  private final boolean[] outputNulls;
+
+  public IfDoubleVectorProcessor(
+      ExprVectorProcessor<?> conditionProcessor,
+      ExprVectorProcessor<double[]> thenProcessor,
+      ExprVectorProcessor<double[]> elseProcessor
+  )
+  {
+    super(
+        ExpressionType.DOUBLE,
+        conditionProcessor,
+        CastToTypeVectorProcessor.cast(thenProcessor, ExpressionType.DOUBLE),
+        CastToTypeVectorProcessor.cast(elseProcessor, ExpressionType.DOUBLE)
+    );
+    this.output = new double[conditionProcessor.maxVectorSize()];
+    this.outputNulls = new boolean[conditionProcessor.maxVectorSize()];
+  }
+
+  @Override
+  public ExprEvalVector<double[]> evalVector(Expr.VectorInputBinding bindings)
+  {
+    thenBindingFilterer.setBindings(bindings);
+    elseBindingFilterer.setBindings(bindings);
+    final ExprEvalVector<?> conditionVector = 
conditionProcessor.evalVector(bindings);
+
+    final int[] thenSelection = 
thenBindingFilterer.getVectorMatch().getSelection();
+    final int[] elseSelection = 
elseBindingFilterer.getVectorMatch().getSelection();
+    int thens = 0;
+    int elses = 0;
+    for (int i = 0; i < bindings.getCurrentVectorSize(); i++) {
+      if (conditionVector.elementAsBoolean(i)) {
+        thenSelection[thens++] = i;
+      } else {
+        elseSelection[elses++] = i;
+      }
+    }
+    thenBindingFilterer.getVectorMatch().setSelectionSize(thens);
+    elseBindingFilterer.getVectorMatch().setSelectionSize(elses);
+
+    if (elses == 0) {
+      return thenProcessor.evalVector(bindings);
+    } else if (thens == 0) {
+      return elseProcessor.evalVector(bindings);
+    }
+
+    final ExprEvalVector<double[]> thenVector = 
thenProcessor.evalVector(thenBindingFilterer);
+    final double[] thenValues = thenVector.getDoubleVector();
+    final boolean[] thenNulls = thenVector.getNullVector();
+    for (int i = 0; i < thens; i++) {
+      final int outIndex = thenSelection[i];
+      if (thenNulls != null && thenNulls[i]) {
+        outputNulls[outIndex] = true;
+      } else {
+        output[outIndex] = thenValues[i];
+        outputNulls[outIndex] = false;
+      }
+    }
+
+    final ExprEvalVector<double[]> elseVector = 
elseProcessor.evalVector(elseBindingFilterer);
+    final double[] elseValues = elseVector.getDoubleVector();
+    final boolean[] elseNulls = elseVector.getNullVector();
+    for (int i = 0; i < elses; i++) {
+      final int outIndex = elseSelection[i];
+      if (elseNulls != null && elseNulls[i]) {
+        outputNulls[outIndex] = true;
+      } else {
+        output[outIndex] = elseValues[i];
+        outputNulls[outIndex] = false;
+      }
+    }
+    return new ExprEvalDoubleVector(output, outputNulls);
+  }
+}
diff --git 
a/processing/src/main/java/org/apache/druid/math/expr/vector/IfFunctionVectorProcessor.java
 
b/processing/src/main/java/org/apache/druid/math/expr/vector/IfFunctionVectorProcessor.java
new file mode 100644
index 00000000000..0cd08bf6c58
--- /dev/null
+++ 
b/processing/src/main/java/org/apache/druid/math/expr/vector/IfFunctionVectorProcessor.java
@@ -0,0 +1,59 @@
+/*
+ * 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.vector;
+
+import org.apache.druid.math.expr.ExpressionType;
+
+public abstract class IfFunctionVectorProcessor<T> implements 
ExprVectorProcessor<T>
+{
+  final ExpressionType outputType;
+  final ExprVectorProcessor<?> conditionProcessor;
+  final ExprVectorProcessor<T> thenProcessor;
+  final ExprVectorProcessor<T> elseProcessor;
+  final FilteredVectorInputBinding thenBindingFilterer;
+  final FilteredVectorInputBinding elseBindingFilterer;
+
+  public IfFunctionVectorProcessor(
+      ExpressionType outputType,
+      ExprVectorProcessor<?> conditionProcessor,
+      ExprVectorProcessor<T> thenProcessor,
+      ExprVectorProcessor<T> elseProcessor
+  )
+  {
+    this.outputType = outputType;
+    this.conditionProcessor = conditionProcessor;
+    this.thenProcessor = thenProcessor;
+    this.elseProcessor = elseProcessor;
+    this.thenBindingFilterer = new 
FilteredVectorInputBinding(conditionProcessor.maxVectorSize());
+    this.elseBindingFilterer = new 
FilteredVectorInputBinding(conditionProcessor.maxVectorSize());
+  }
+
+  @Override
+  public ExpressionType getOutputType()
+  {
+    return outputType;
+  }
+
+  @Override
+  public int maxVectorSize()
+  {
+    return conditionProcessor.maxVectorSize();
+  }
+}
diff --git 
a/processing/src/main/java/org/apache/druid/math/expr/vector/IfLongVectorProcessor.java
 
b/processing/src/main/java/org/apache/druid/math/expr/vector/IfLongVectorProcessor.java
new file mode 100644
index 00000000000..a8b6c7f171a
--- /dev/null
+++ 
b/processing/src/main/java/org/apache/druid/math/expr/vector/IfLongVectorProcessor.java
@@ -0,0 +1,101 @@
+/*
+ * 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.vector;
+
+import org.apache.druid.math.expr.Expr;
+import org.apache.druid.math.expr.ExpressionType;
+
+public class IfLongVectorProcessor extends IfFunctionVectorProcessor<long[]>
+{
+  private final long[] output;
+  private final boolean[] outputNulls;
+
+  public IfLongVectorProcessor(
+      ExprVectorProcessor<?> conditionProcessor,
+      ExprVectorProcessor<long[]> thenProcessor,
+      ExprVectorProcessor<long[]> elseProcessor
+  )
+  {
+    super(
+        ExpressionType.LONG,
+        conditionProcessor,
+        CastToTypeVectorProcessor.cast(thenProcessor, ExpressionType.LONG),
+        CastToTypeVectorProcessor.cast(elseProcessor, ExpressionType.LONG)
+    );
+    this.output = new long[conditionProcessor.maxVectorSize()];
+    this.outputNulls = new boolean[conditionProcessor.maxVectorSize()];
+  }
+
+  @Override
+  public ExprEvalVector<long[]> evalVector(Expr.VectorInputBinding bindings)
+  {
+    thenBindingFilterer.setBindings(bindings);
+    elseBindingFilterer.setBindings(bindings);
+    final ExprEvalVector<?> conditionVector = 
conditionProcessor.evalVector(bindings);
+
+    final int[] thenSelection = 
thenBindingFilterer.getVectorMatch().getSelection();
+    final int[] elseSelection = 
elseBindingFilterer.getVectorMatch().getSelection();
+    int thens = 0;
+    int elses = 0;
+    for (int i = 0; i < bindings.getCurrentVectorSize(); i++) {
+      if (conditionVector.elementAsBoolean(i)) {
+        thenSelection[thens++] = i;
+      } else {
+        elseSelection[elses++] = i;
+      }
+    }
+    thenBindingFilterer.getVectorMatch().setSelectionSize(thens);
+    elseBindingFilterer.getVectorMatch().setSelectionSize(elses);
+
+    if (elses == 0) {
+      return thenProcessor.evalVector(bindings);
+    } else if (thens == 0) {
+      return elseProcessor.evalVector(bindings);
+    }
+
+
+    final ExprEvalVector<long[]> thenVector = 
thenProcessor.evalVector(thenBindingFilterer);
+    final long[] thenValues = thenVector.getLongVector();
+    final boolean[] thenNulls = thenVector.getNullVector();
+    for (int i = 0; i < thens; i++) {
+      final int outIndex = thenSelection[i];
+      if (thenNulls != null && thenNulls[i]) {
+        outputNulls[outIndex] = true;
+      } else {
+        output[outIndex] = thenValues[i];
+        outputNulls[outIndex] = false;
+      }
+    }
+
+    final ExprEvalVector<long[]> elseVector = 
elseProcessor.evalVector(elseBindingFilterer);
+    final long[] elseValues = elseVector.getLongVector();
+    final boolean[] elseNulls = elseVector.getNullVector();
+    for (int i = 0; i < elses; i++) {
+      final int outIndex = elseSelection[i];
+      if (elseNulls != null && elseNulls[i]) {
+        outputNulls[outIndex] = true;
+      } else {
+        output[outIndex] = elseValues[i];
+        outputNulls[outIndex] = false;
+      }
+    }
+    return new ExprEvalLongVector(output, outputNulls);
+  }
+}
diff --git 
a/processing/src/main/java/org/apache/druid/math/expr/vector/IfObjectVectorProcessor.java
 
b/processing/src/main/java/org/apache/druid/math/expr/vector/IfObjectVectorProcessor.java
new file mode 100644
index 00000000000..269589a5030
--- /dev/null
+++ 
b/processing/src/main/java/org/apache/druid/math/expr/vector/IfObjectVectorProcessor.java
@@ -0,0 +1,82 @@
+/*
+ * 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.vector;
+
+import org.apache.druid.math.expr.Expr;
+import org.apache.druid.math.expr.ExpressionType;
+
+public class IfObjectVectorProcessor extends 
IfFunctionVectorProcessor<Object[]>
+{
+  private final Object[] output;
+
+  public IfObjectVectorProcessor(
+      ExpressionType outputType,
+      ExprVectorProcessor<?> conditionProcessor,
+      ExprVectorProcessor<Object[]> thenProcessor,
+      ExprVectorProcessor<Object[]> elseProcessor
+  )
+  {
+    super(outputType, conditionProcessor, thenProcessor, elseProcessor);
+    this.output = new Object[conditionProcessor.maxVectorSize()];
+  }
+
+  @Override
+  public ExprEvalVector<Object[]> evalVector(Expr.VectorInputBinding bindings)
+  {
+    thenBindingFilterer.setBindings(bindings);
+    elseBindingFilterer.setBindings(bindings);
+    final ExprEvalVector<?> conditionVector = 
conditionProcessor.evalVector(bindings);
+
+    final int[] thenSelection = 
thenBindingFilterer.getVectorMatch().getSelection();
+    final int[] elseSelection = 
elseBindingFilterer.getVectorMatch().getSelection();
+    int thens = 0;
+    int elses = 0;
+    for (int i = 0; i < bindings.getCurrentVectorSize(); i++) {
+      if (conditionVector.elementAsBoolean(i)) {
+        thenSelection[thens++] = i;
+      } else {
+        elseSelection[elses++] = i;
+      }
+    }
+    thenBindingFilterer.getVectorMatch().setSelectionSize(thens);
+    elseBindingFilterer.getVectorMatch().setSelectionSize(elses);
+
+    if (elses == 0) {
+      return thenProcessor.evalVector(bindings);
+    } else if (thens == 0) {
+      return elseProcessor.evalVector(bindings);
+    }
+
+    final ExprEvalVector<Object[]> thenVector = 
thenProcessor.evalVector(thenBindingFilterer);
+    final Object[] thenValues = thenVector.getObjectVector();
+    for (int i = 0; i < thens; i++) {
+      final int outIndex = thenSelection[i];
+      output[outIndex] = thenValues[i];
+    }
+
+    final ExprEvalVector<Object[]> elseVector = 
elseProcessor.evalVector(elseBindingFilterer);
+    final Object[] elseValues = elseVector.getObjectVector();
+    for (int i = 0; i < elses; i++) {
+      final int outIndex = elseSelection[i];
+      output[outIndex] = elseValues[i];
+    }
+    return new ExprEvalObjectVector(output, outputType);
+  }
+}
diff --git 
a/processing/src/main/java/org/apache/druid/math/expr/vector/VectorConditionalProcessors.java
 
b/processing/src/main/java/org/apache/druid/math/expr/vector/VectorConditionalProcessors.java
index 30b775f0236..3fbe711948e 100644
--- 
a/processing/src/main/java/org/apache/druid/math/expr/vector/VectorConditionalProcessors.java
+++ 
b/processing/src/main/java/org/apache/druid/math/expr/vector/VectorConditionalProcessors.java
@@ -57,4 +57,44 @@ public class VectorConditionalProcessors
     }
     return (ExprVectorProcessor<T>) processor;
   }
+
+  public static <T> ExprVectorProcessor<T> ifFunction(
+      Expr.VectorInputBindingInspector inspector,
+      Expr conditionExpr,
+      Expr thenExpr,
+      Expr elseExpr
+  )
+  {
+    // right now this function can only vectorize if then and else clause have 
same output type, if this changes then
+    // we'll need to switch this to use whatever output type logic that is 
using
+    final ExpressionType outputType = thenExpr.getOutputType(inspector);
+
+    final ExprVectorProcessor<?> processor;
+    if (outputType == null) {
+      // if output type is null, it means all the input types were null 
(non-existent), and if(null, null, null) is null
+      return VectorProcessors.constant((Long) null, 
inspector.getMaxVectorSize());
+    }
+    if (outputType.is(ExprType.LONG)) {
+      // long is most restrictive so both processors are definitely long typed 
if output is long
+      processor = new IfLongVectorProcessor(
+          conditionExpr.asVectorProcessor(inspector),
+          thenExpr.asVectorProcessor(inspector),
+          elseExpr.asVectorProcessor(inspector)
+      );
+    } else if (outputType.is(ExprType.DOUBLE)) {
+      processor = new IfDoubleVectorProcessor(
+          conditionExpr.asVectorProcessor(inspector),
+          thenExpr.asVectorProcessor(inspector),
+          elseExpr.asVectorProcessor(inspector)
+      );
+    } else {
+      processor = new IfObjectVectorProcessor(
+          outputType,
+          conditionExpr.asVectorProcessor(inspector),
+          thenExpr.asVectorProcessor(inspector),
+          elseExpr.asVectorProcessor(inspector)
+      );
+    }
+    return (ExprVectorProcessor<T>) processor;
+  }
 }
diff --git 
a/processing/src/test/java/org/apache/druid/math/expr/VectorExprResultConsistencyTest.java
 
b/processing/src/test/java/org/apache/druid/math/expr/VectorExprResultConsistencyTest.java
index 11580eb9f5b..64a955f2e6f 100644
--- 
a/processing/src/test/java/org/apache/druid/math/expr/VectorExprResultConsistencyTest.java
+++ 
b/processing/src/test/java/org/apache/druid/math/expr/VectorExprResultConsistencyTest.java
@@ -38,6 +38,7 @@ import org.apache.druid.testing.InitializedNullHandlingTest;
 import org.junit.Assert;
 import org.junit.Assume;
 import org.junit.Test;
+import org.junit.jupiter.api.Assertions;
 
 import javax.annotation.Nullable;
 import java.util.ArrayList;
@@ -348,6 +349,30 @@ public class VectorExprResultConsistencyTest extends 
InitializedNullHandlingTest
     testFunctions(types, templates, functions);
   }
 
+  @Test
+  public void testIfFunction()
+  {
+    testExpression("if(l1, l1, l2)", types);
+    testExpression("if(l1, s1, s2)", types);
+    testExpression("if(l1, d1, d2)", types);
+    testExpression("if(d1, l1, l2)", types);
+    testExpression("if(d1, s1, s2)", types);
+    testExpression("if(d1, d1, d2)", types);
+    testExpression("if(boolString1, s1, s2)", types);
+    testExpression("if(boolString1, l1, l2)", types);
+    testExpression("if(boolString1, d1, d2)", types);
+    // make sure eval of else is lazy, else this would be divide by zero error
+    testExpression("if(l1 % 2 == 0, -1, l2 / (l1 % 2))", types);
+    // cannot vectorize mixed types
+    Assertions.assertFalse(
+        Parser.parse("if(s1, l1, d2)", 
MACRO_TABLE).canVectorize(InputBindings.inspectorFromTypeMap(types))
+    );
+    Assertions.assertFalse(
+        Parser.parse("if(s1, d1, s2)", 
MACRO_TABLE).canVectorize(InputBindings.inspectorFromTypeMap(types))
+    );
+  }
+
+
   @Test
   public void testStringFns()
   {
diff --git 
a/processing/src/test/java/org/apache/druid/query/timeseries/TimeseriesQueryRunnerTest.java
 
b/processing/src/test/java/org/apache/druid/query/timeseries/TimeseriesQueryRunnerTest.java
index 16254aac72b..3bbd83c7720 100644
--- 
a/processing/src/test/java/org/apache/druid/query/timeseries/TimeseriesQueryRunnerTest.java
+++ 
b/processing/src/test/java/org/apache/druid/query/timeseries/TimeseriesQueryRunnerTest.java
@@ -2316,7 +2316,6 @@ public class TimeseriesQueryRunnerTest extends 
InitializedNullHandlingTest
   @Test
   public void testTimeSeriesWithFilteredAggAndExpressionFilteredAgg()
   {
-    cannotVectorizeUnlessFallback();
     TimeseriesQuery query = Druids
         .newTimeseriesQueryBuilder()
         .dataSource(QueryRunnerTestHelper.DATA_SOURCE)


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to