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 4c1382af994 refactor vectorized nvl implementation into more general 
coalesce implementation (#18537)
4c1382af994 is described below

commit 4c1382af994fa1cc606e28a780029d8b784c9ba5
Author: Clint Wylie <[email protected]>
AuthorDate: Tue Sep 16 22:51:50 2025 -0700

    refactor vectorized nvl implementation into more general coalesce 
implementation (#18537)
---
 .../java/org/apache/druid/math/expr/Function.java  |  4 +-
 .../expr/vector/CoalesceDoubleVectorProcessor.java | 83 ++++++++++++++++++++
 ...r.java => CoalesceFunctionVectorProcessor.java} | 17 ++---
 .../expr/vector/CoalesceLongVectorProcessor.java   | 82 ++++++++++++++++++++
 .../expr/vector/CoalesceVectorObjectProcessor.java | 72 ++++++++++++++++++
 .../math/expr/vector/NvlDoubleVectorProcessor.java | 88 ----------------------
 .../math/expr/vector/NvlLongVectorProcessor.java   | 88 ----------------------
 .../math/expr/vector/NvlVectorObjectProcessor.java | 63 ----------------
 .../expr/vector/VectorConditionalProcessors.java   | 30 ++++----
 .../math/expr/VectorExprResultConsistencyTest.java | 31 +++++++-
 10 files changed, 288 insertions(+), 270 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 8f51a50fd90..16a46cbda9b 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
@@ -2673,13 +2673,13 @@ public interface Function extends NamedFunction
     @Override
     public boolean canVectorize(Expr.InputBindingInspector inspector, 
List<Expr> args)
     {
-      return args.size() == 2 && inspector.canVectorize(args);
+      return inspector.canVectorize(args) && inspector.areSameTypes(args);
     }
 
     @Override
     public <T> ExprVectorProcessor<T> 
asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args)
     {
-      return VectorConditionalProcessors.nvl(inspector, args.get(0), 
args.get(1));
+      return VectorConditionalProcessors.coalesce(inspector, args);
     }
   }
 
diff --git 
a/processing/src/main/java/org/apache/druid/math/expr/vector/CoalesceDoubleVectorProcessor.java
 
b/processing/src/main/java/org/apache/druid/math/expr/vector/CoalesceDoubleVectorProcessor.java
new file mode 100644
index 00000000000..b04c0605351
--- /dev/null
+++ 
b/processing/src/main/java/org/apache/druid/math/expr/vector/CoalesceDoubleVectorProcessor.java
@@ -0,0 +1,83 @@
+/*
+ * 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;
+import org.apache.druid.query.filter.vector.VectorMatch;
+
+public final class CoalesceDoubleVectorProcessor extends 
CoalesceFunctionVectorProcessor<double[]>
+{
+  private final double[] output;
+  private final boolean[] outputNulls;
+
+  public CoalesceDoubleVectorProcessor(
+      ExprVectorProcessor<double[]>[] processors
+  )
+  {
+    super(ExpressionType.DOUBLE, processors);
+    this.output = new double[processors[0].maxVectorSize()];
+    this.outputNulls = new boolean[processors[0].maxVectorSize()];
+  }
+
+  @Override
+  public ExprEvalVector<double[]> evalVector(Expr.VectorInputBinding bindings)
+  {
+    inputBindingFilterer.setBindings(bindings);
+    
inputBindingFilterer.getVectorMatch().copyFrom(VectorMatch.allTrue(bindings.getCurrentVectorSize()));
+    final int[] selection = 
inputBindingFilterer.getVectorMatch().getSelection();
+
+    ExprEvalVector<double[]> currentVector;
+    int currentProcessor = 0;
+    int notNull = 0;
+    while (notNull < bindings.getCurrentVectorSize() && currentProcessor < 
processors.length) {
+      currentVector = 
processors[currentProcessor].evalVector(inputBindingFilterer);
+      final double[] currentValues = currentVector.getDoubleVector();
+      final boolean[] currentNulls = currentVector.getNullVector();
+      if (currentProcessor == 0 && currentNulls == null) {
+        // this one has no nulls, bail early
+        return currentVector;
+      }
+      currentProcessor++;
+
+      int nulls = 0;
+      for (int i = 0; i < inputBindingFilterer.getCurrentVectorSize(); i++) {
+        final int outIndex = selection[i];
+        if (currentNulls != null && currentNulls[i]) {
+          if (currentProcessor < processors.length) {
+            selection[nulls++] = selection[i];
+          } else {
+            outputNulls[outIndex] = true;
+            output[outIndex] = 0;
+          }
+        } else {
+          notNull++;
+          outputNulls[outIndex] = false;
+          output[outIndex] = currentValues[i];
+        }
+      }
+      if (notNull == bindings.getCurrentVectorSize()) {
+        break;
+      }
+      inputBindingFilterer.getVectorMatch().setSelectionSize(nulls);
+    }
+    return new ExprEvalDoubleVector(output, outputNulls);
+  }
+}
diff --git 
a/processing/src/main/java/org/apache/druid/math/expr/vector/NvlFunctionVectorProcessor.java
 
b/processing/src/main/java/org/apache/druid/math/expr/vector/CoalesceFunctionVectorProcessor.java
similarity index 69%
rename from 
processing/src/main/java/org/apache/druid/math/expr/vector/NvlFunctionVectorProcessor.java
rename to 
processing/src/main/java/org/apache/druid/math/expr/vector/CoalesceFunctionVectorProcessor.java
index 2053fd5b929..c551c966353 100644
--- 
a/processing/src/main/java/org/apache/druid/math/expr/vector/NvlFunctionVectorProcessor.java
+++ 
b/processing/src/main/java/org/apache/druid/math/expr/vector/CoalesceFunctionVectorProcessor.java
@@ -21,23 +21,20 @@ package org.apache.druid.math.expr.vector;
 
 import org.apache.druid.math.expr.ExpressionType;
 
-public abstract class NvlFunctionVectorProcessor<T> implements 
ExprVectorProcessor<T>
+public abstract class CoalesceFunctionVectorProcessor<T> implements 
ExprVectorProcessor<T>
 {
   final ExpressionType outputType;
-  final ExprVectorProcessor<T> inputProcessor;
-  final ExprVectorProcessor<T> elseProcessor;
+  final ExprVectorProcessor<T>[] processors;
   final FilteredVectorInputBinding inputBindingFilterer;
 
-  public NvlFunctionVectorProcessor(
+  public CoalesceFunctionVectorProcessor(
       ExpressionType outputType,
-      ExprVectorProcessor<T> inputProcessor,
-      ExprVectorProcessor<T> elseProcessor
+      ExprVectorProcessor<T>[] processors
   )
   {
     this.outputType = outputType;
-    this.inputProcessor = inputProcessor;
-    this.elseProcessor = elseProcessor;
-    this.inputBindingFilterer = new 
FilteredVectorInputBinding(inputProcessor.maxVectorSize());
+    this.processors = processors;
+    this.inputBindingFilterer = new 
FilteredVectorInputBinding(processors[0].maxVectorSize());
   }
 
   @Override
@@ -49,6 +46,6 @@ public abstract class NvlFunctionVectorProcessor<T> 
implements ExprVectorProcess
   @Override
   public int maxVectorSize()
   {
-    return inputProcessor.maxVectorSize();
+    return processors[0].maxVectorSize();
   }
 }
diff --git 
a/processing/src/main/java/org/apache/druid/math/expr/vector/CoalesceLongVectorProcessor.java
 
b/processing/src/main/java/org/apache/druid/math/expr/vector/CoalesceLongVectorProcessor.java
new file mode 100644
index 00000000000..7524e9507aa
--- /dev/null
+++ 
b/processing/src/main/java/org/apache/druid/math/expr/vector/CoalesceLongVectorProcessor.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;
+import org.apache.druid.query.filter.vector.VectorMatch;
+
+public final class CoalesceLongVectorProcessor extends 
CoalesceFunctionVectorProcessor<long[]>
+{
+  private final long[] output;
+  private final boolean[] outputNulls;
+
+  public CoalesceLongVectorProcessor(
+      ExprVectorProcessor<long[]>[] processors
+  )
+  {
+    super(ExpressionType.LONG, processors);
+    this.output = new long[processors[0].maxVectorSize()];
+    this.outputNulls = new boolean[processors[0].maxVectorSize()];
+  }
+
+  @Override
+  public ExprEvalVector<long[]> evalVector(Expr.VectorInputBinding bindings)
+  {
+    inputBindingFilterer.setBindings(bindings);
+    
inputBindingFilterer.getVectorMatch().copyFrom(VectorMatch.allTrue(bindings.getCurrentVectorSize()));
+    final int[] selection = 
inputBindingFilterer.getVectorMatch().getSelection();
+
+    ExprEvalVector<long[]> currentVector;
+    int currentProcessor = 0;
+    int notNull = 0;
+    while (notNull < bindings.getCurrentVectorSize() && currentProcessor < 
processors.length) {
+      currentVector = 
processors[currentProcessor].evalVector(inputBindingFilterer);
+      final long[] currentValues = currentVector.getLongVector();
+      final boolean[] currentNulls = currentVector.getNullVector();
+      if (currentProcessor == 0 && currentNulls == null) {
+        // this one has no nulls, bail early
+        return currentVector;
+      }
+      currentProcessor++;
+      int nulls = 0;
+      for (int i = 0; i < inputBindingFilterer.getCurrentVectorSize(); i++) {
+        final int outIndex = selection[i];
+        if (currentNulls != null && currentNulls[i]) {
+          if (currentProcessor < processors.length) {
+            selection[nulls++] = selection[i];
+          } else {
+            outputNulls[outIndex] = true;
+            output[outIndex] = 0;
+          }
+        } else {
+          notNull++;
+          outputNulls[outIndex] = false;
+          output[outIndex] = currentValues[i];
+        }
+      }
+      if (notNull == bindings.getCurrentVectorSize()) {
+        break;
+      }
+      inputBindingFilterer.getVectorMatch().setSelectionSize(nulls);
+    }
+    return new ExprEvalLongVector(output, outputNulls);
+  }
+}
diff --git 
a/processing/src/main/java/org/apache/druid/math/expr/vector/CoalesceVectorObjectProcessor.java
 
b/processing/src/main/java/org/apache/druid/math/expr/vector/CoalesceVectorObjectProcessor.java
new file mode 100644
index 00000000000..12cddbcad93
--- /dev/null
+++ 
b/processing/src/main/java/org/apache/druid/math/expr/vector/CoalesceVectorObjectProcessor.java
@@ -0,0 +1,72 @@
+/*
+ * 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;
+import org.apache.druid.query.filter.vector.VectorMatch;
+
+public final class CoalesceVectorObjectProcessor extends 
CoalesceFunctionVectorProcessor<Object[]>
+{
+  private final Object[] output;
+
+  public CoalesceVectorObjectProcessor(
+      ExpressionType outputType,
+      ExprVectorProcessor<Object[]>[] processors
+  )
+  {
+    super(outputType, processors);
+    this.output = new Object[processors[0].maxVectorSize()];
+  }
+
+  @Override
+  public ExprEvalVector<Object[]> evalVector(Expr.VectorInputBinding bindings)
+  {
+    inputBindingFilterer.setBindings(bindings);
+    
inputBindingFilterer.getVectorMatch().copyFrom(VectorMatch.allTrue(bindings.getCurrentVectorSize()));
+    final int[] selection = 
inputBindingFilterer.getVectorMatch().getSelection();
+
+    int currentProcessor = 0;
+    int notNull = 0;
+    while (notNull < bindings.getCurrentVectorSize() && currentProcessor < 
processors.length) {
+      final ExprEvalVector<Object[]> inputVector = 
processors[currentProcessor++].evalVector(inputBindingFilterer);
+      final Object[] inputValues = inputVector.getObjectVector();
+      int nulls = 0;
+      for (int i = 0; i < inputBindingFilterer.getCurrentVectorSize(); i++) {
+        final int outIndex = selection[i];
+        if (inputValues[i] == null) {
+          if (currentProcessor < processors.length) {
+            selection[nulls++] = selection[i];
+          } else {
+            output[outIndex] = null;
+          }
+        } else {
+          notNull++;
+          output[outIndex] = inputValues[i];
+        }
+      }
+      if (notNull == bindings.getCurrentVectorSize()) {
+        break;
+      }
+      inputBindingFilterer.getVectorMatch().setSelectionSize(nulls);
+    }
+    return new ExprEvalObjectVector(output, outputType);
+  }
+}
diff --git 
a/processing/src/main/java/org/apache/druid/math/expr/vector/NvlDoubleVectorProcessor.java
 
b/processing/src/main/java/org/apache/druid/math/expr/vector/NvlDoubleVectorProcessor.java
deleted file mode 100644
index 1e2f87987ef..00000000000
--- 
a/processing/src/main/java/org/apache/druid/math/expr/vector/NvlDoubleVectorProcessor.java
+++ /dev/null
@@ -1,88 +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.vector;
-
-import org.apache.druid.math.expr.Expr;
-import org.apache.druid.math.expr.ExpressionType;
-
-public final class NvlDoubleVectorProcessor extends 
NvlFunctionVectorProcessor<double[]>
-{
-  private final double[] output;
-  private final boolean[] outputNulls;
-
-  public NvlDoubleVectorProcessor(
-      ExprVectorProcessor<double[]> inputProcessor,
-      ExprVectorProcessor<double[]> elseProcessor
-  )
-  {
-    super(ExpressionType.DOUBLE, inputProcessor, elseProcessor);
-    this.output = new double[inputProcessor.maxVectorSize()];
-    this.outputNulls = new boolean[inputProcessor.maxVectorSize()];
-  }
-
-  @Override
-  public ExprEvalVector<double[]> evalVector(Expr.VectorInputBinding bindings)
-  {
-    inputBindingFilterer.setBindings(bindings);
-    final ExprEvalVector<double[]> inputVector = 
inputProcessor.evalVector(bindings);
-
-    if (inputVector.getNullVector() == null) {
-      // this one has no nulls, just spit it out
-      return inputVector;
-    }
-
-
-    final double[] inputValues = inputVector.getDoubleVector();
-    final boolean[] inputNulls = inputVector.getNullVector();
-    final int[] selection = 
inputBindingFilterer.getVectorMatch().getSelection();
-    int nulls = 0;
-    for (int i = 0; i < bindings.getCurrentVectorSize(); i++) {
-      if (inputNulls[i]) {
-        selection[nulls++] = i;
-      } else {
-        outputNulls[i] = false;
-        output[i] = inputValues[i];
-      }
-    }
-    if (nulls == 0) {
-      return new ExprEvalDoubleVector(output, outputNulls);
-    }
-    inputBindingFilterer.getVectorMatch().setSelectionSize(nulls);
-
-    if (nulls == bindings.getCurrentVectorSize()) {
-      // all nulls, just return the other
-      return elseProcessor.evalVector(bindings);
-    }
-
-    final ExprEvalVector<double[]> elseVector = 
elseProcessor.evalVector(inputBindingFilterer);
-    final double[] elseValues = elseVector.getDoubleVector();
-    final boolean[] elseNulls = elseVector.getNullVector();
-    for (int i = 0; i < nulls; i++) {
-      final int outIndex = selection[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/NvlLongVectorProcessor.java
 
b/processing/src/main/java/org/apache/druid/math/expr/vector/NvlLongVectorProcessor.java
deleted file mode 100644
index 9f709005f02..00000000000
--- 
a/processing/src/main/java/org/apache/druid/math/expr/vector/NvlLongVectorProcessor.java
+++ /dev/null
@@ -1,88 +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.vector;
-
-import org.apache.druid.math.expr.Expr;
-import org.apache.druid.math.expr.ExpressionType;
-
-public final class NvlLongVectorProcessor extends 
NvlFunctionVectorProcessor<long[]>
-{
-  private final long[] output;
-  private final boolean[] outputNulls;
-
-  public NvlLongVectorProcessor(
-      ExprVectorProcessor<long[]> inputProcessor,
-      ExprVectorProcessor<long[]> elseProcessor
-  )
-  {
-    super(ExpressionType.LONG, inputProcessor, elseProcessor);
-    this.output = new long[inputProcessor.maxVectorSize()];
-    this.outputNulls = new boolean[inputProcessor.maxVectorSize()];
-  }
-
-  @Override
-  public ExprEvalVector<long[]> evalVector(Expr.VectorInputBinding bindings)
-  {
-    inputBindingFilterer.setBindings(bindings);
-    final ExprEvalVector<long[]> inputVector = 
inputProcessor.evalVector(bindings);
-
-    if (inputVector.getNullVector() == null) {
-      // this one has no nulls, just spit it out
-      return inputVector;
-    }
-
-
-    final long[] inputValues = inputVector.getLongVector();
-    final boolean[] inputNulls = inputVector.getNullVector();
-    final int[] selection = 
inputBindingFilterer.getVectorMatch().getSelection();
-    int nulls = 0;
-    for (int i = 0; i < bindings.getCurrentVectorSize(); i++) {
-      if (inputNulls[i]) {
-        selection[nulls++] = i;
-      } else {
-        outputNulls[i] = false;
-        output[i] = inputValues[i];
-      }
-    }
-    inputBindingFilterer.getVectorMatch().setSelectionSize(nulls);
-    if (nulls == 0) {
-      return new ExprEvalLongVector(output, outputNulls);
-    }
-
-    if (nulls == bindings.getCurrentVectorSize()) {
-      // all nulls, just return the other
-      return elseProcessor.evalVector(bindings);
-    }
-
-    final ExprEvalVector<long[]> elseVector = 
elseProcessor.evalVector(inputBindingFilterer);
-    final long[] elseValues = elseVector.getLongVector();
-    final boolean[] elseNulls = elseVector.getNullVector();
-    for (int i = 0; i < nulls; i++) {
-      final int outIndex = selection[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/NvlVectorObjectProcessor.java
 
b/processing/src/main/java/org/apache/druid/math/expr/vector/NvlVectorObjectProcessor.java
deleted file mode 100644
index 061a66b0827..00000000000
--- 
a/processing/src/main/java/org/apache/druid/math/expr/vector/NvlVectorObjectProcessor.java
+++ /dev/null
@@ -1,63 +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.vector;
-
-import org.apache.druid.math.expr.Expr;
-import org.apache.druid.math.expr.ExpressionType;
-
-public final class NvlVectorObjectProcessor extends 
NvlFunctionVectorProcessor<Object[]>
-{
-  private final Object[] output;
-
-  public NvlVectorObjectProcessor(
-      ExpressionType outputType,
-      ExprVectorProcessor<Object[]> inputProcessor,
-      ExprVectorProcessor<Object[]> elseProcessor
-  )
-  {
-    super(outputType, inputProcessor, elseProcessor);
-    this.output = new Object[inputProcessor.maxVectorSize()];
-  }
-
-  @Override
-  public ExprEvalVector<Object[]> evalVector(Expr.VectorInputBinding bindings)
-  {
-    inputBindingFilterer.setBindings(bindings);
-    final ExprEvalVector<Object[]> inputVector = 
inputProcessor.evalVector(bindings);
-
-    Object[] inputValues = inputVector.getObjectVector();
-    final int[] selection = 
inputBindingFilterer.getVectorMatch().getSelection();
-    int nulls = 0;
-    for (int i = 0; i < bindings.getCurrentVectorSize(); i++) {
-      if (inputValues[i] == null) {
-        selection[nulls++] = i;
-      } else {
-        output[i] = inputValues[i];
-      }
-    }
-    inputBindingFilterer.getVectorMatch().setSelectionSize(nulls);
-    final ExprEvalVector<Object[]> elseVector = 
elseProcessor.evalVector(inputBindingFilterer);
-    final Object[] elseValues = elseVector.getObjectVector();
-    for (int i = 0; i < nulls; i++) {
-      output[selection[i]] = 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 b093f52da6e..5ab6dcac51c 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
@@ -29,34 +29,30 @@ import java.util.List;
 
 public class VectorConditionalProcessors
 {
-  public static <T> ExprVectorProcessor<T> 
nvl(Expr.VectorInputBindingInspector inspector, Expr left, Expr right)
+  public static <T> ExprVectorProcessor<T> 
coalesce(Expr.VectorInputBindingInspector inspector, List<Expr> args)
   {
-    final ExpressionType leftType = left.getOutputType(inspector);
-    final ExpressionType rightType = right.getOutputType(inspector);
-    final ExpressionType outputType = 
ExpressionTypeConversion.leastRestrictiveType(leftType, rightType);
+    ExpressionType outputType = null;
+    for (Expr arg : args) {
+      outputType = ExpressionTypeConversion.leastRestrictiveType(outputType, 
arg.getOutputType(inspector));
+    }
 
     final ExprVectorProcessor<?> processor;
     if (outputType == null) {
       // if output type is null, it means all the input types were null 
(non-existent), and nvl(null, null) is null
       return VectorProcessors.constant((Long) null, 
inspector.getMaxVectorSize());
     }
+
+    ExprVectorProcessor[] processors = new ExprVectorProcessor[args.size()];
+    for (int i = 0; i < args.size(); i++) {
+      processors[i] = 
CastToTypeVectorProcessor.cast(args.get(i).asVectorProcessor(inspector), 
outputType);
+    }
     if (outputType.is(ExprType.LONG)) {
       // long is most restrictive so both processors are definitely long typed 
if output is long
-      processor = new NvlLongVectorProcessor(
-          left.asVectorProcessor(inspector),
-          right.asVectorProcessor(inspector)
-      );
+      processor = new CoalesceLongVectorProcessor(processors);
     } else if (outputType.is(ExprType.DOUBLE)) {
-      processor = new NvlDoubleVectorProcessor(
-          CastToTypeVectorProcessor.cast(left.asVectorProcessor(inspector), 
ExpressionType.DOUBLE),
-          CastToTypeVectorProcessor.cast(right.asVectorProcessor(inspector), 
ExpressionType.DOUBLE)
-      );
+      processor = new CoalesceDoubleVectorProcessor(processors);
     } else {
-      processor = new NvlVectorObjectProcessor(
-          outputType,
-          CastToTypeVectorProcessor.cast(left.asVectorProcessor(inspector), 
outputType),
-          CastToTypeVectorProcessor.cast(right.asVectorProcessor(inspector), 
outputType)
-      );
+      processor = new CoalesceVectorObjectProcessor(outputType, processors);
     }
     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 c54f01472bc..bb199f04ee5 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
@@ -65,7 +65,7 @@ public class VectorExprResultConsistencyTest extends 
InitializedNullHandlingTest
 {
   private static final Logger log = new 
Logger(VectorExprResultConsistencyTest.class);
   private static final int NUM_ITERATIONS = 10;
-  private static final int VECTOR_SIZE = 512;
+  private static final int VECTOR_SIZE = 4;
 
 
   private static final Map<String, String> LOOKUP = Map.of(
@@ -135,12 +135,16 @@ public class VectorExprResultConsistencyTest extends 
InitializedNullHandlingTest
   final Map<String, ExpressionType> types = ImmutableMap.<String, 
ExpressionType>builder()
       .put("l1", ExpressionType.LONG)
       .put("l2", ExpressionType.LONG)
+      .put("l3", ExpressionType.LONG)
       .put("d1", ExpressionType.DOUBLE)
       .put("d2", ExpressionType.DOUBLE)
+      .put("d3", ExpressionType.DOUBLE)
       .put("s1", ExpressionType.STRING)
       .put("s2", ExpressionType.STRING)
+      .put("s3", ExpressionType.STRING)
       .put("boolString1", ExpressionType.STRING)
       .put("boolString2", ExpressionType.STRING)
+      .put("boolString3", ExpressionType.STRING)
       .build();
 
 
@@ -339,7 +343,8 @@ public class VectorExprResultConsistencyTest extends 
InitializedNullHandlingTest
   public void testSymmetricalBivariateFunctions()
   {
     final List<String> functions = List.of(
-        "nvl"
+        "nvl",
+        "coalesce"
     );
     final List<String> templates = List.of(
         "%s(d1, d2)",
@@ -380,8 +385,11 @@ public class VectorExprResultConsistencyTest extends 
InitializedNullHandlingTest
   public void testCaseSearchedFunction()
   {
     testExpression("case_searched(boolString1, s1, boolString2, s2, s1)", 
types);
+    testExpression("case_searched(boolString1, s1, boolString2, s2, 
boolString3, s3, s1)", types);
     testExpression("case_searched(boolString1, l1, boolString2, l2, l2)", 
types);
+    testExpression("case_searched(boolString1, l1, boolString2, l2, 
boolString3, l3, l2)", types);
     testExpression("case_searched(boolString1, d1, boolString2, d2, d1)", 
types);
+    testExpression("case_searched(boolString1, d1, boolString2, d2, 
boolString3, d3, d1)", types);
     testExpression("case_searched(l1 % 2 == 0, -1, l1 % 2 == 1, l2 / (l1 % 
2))", types);
     Assertions.assertFalse(
         Parser.parse("case_searched(boolString1, d1, boolString2, d2, l1)", 
MACRO_TABLE)
@@ -407,6 +415,25 @@ public class VectorExprResultConsistencyTest extends 
InitializedNullHandlingTest
     );
   }
 
+  @Test
+  public void testCoalesceFunction()
+  {
+    final List<String> functions = List.of(
+        "coalesce"
+    );
+    final List<String> templates = List.of(
+        "%s(nonexistent, d1, d2, d3)",
+        "%s(nonexistent, d1, nonexistent2, d2, nonexistent3, d3)",
+        "%s(nonexistent, nonexistent2, l1, l2, nonexistent, l3)",
+        "%s(nonexistent, s1, nonexistent2, s2)"
+    );
+    testFunctions(types, templates, functions);
+    // cannot vectorize mixed arg types
+    Assertions.assertFalse(
+        Parser.parse("coalesce(s1, d1, s1, l1, d1)", MACRO_TABLE)
+              .canVectorize(InputBindings.inspectorFromTypeMap(types))
+    );
+  }
 
   @Test
   public void testStringFns()


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

Reply via email to