xiangfu0 commented on code in PR #18115:
URL: https://github.com/apache/pinot/pull/18115#discussion_r3047917597


##########
pinot-common/src/main/java/org/apache/pinot/common/function/scalar/bitwise/BitAndScalarFunction.java:
##########
@@ -0,0 +1,70 @@
+/**
+ * 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.pinot.common.function.scalar.bitwise;

Review Comment:
   Fixed — updated the PR description to reference the correct package 
`org.apache.pinot.common.function.scalar.bitwise`.



##########
pinot-common/src/main/java/org/apache/pinot/common/function/scalar/bitwise/PolymorphicUnaryIntegralScalarFunction.java:
##########
@@ -0,0 +1,65 @@
+/**
+ * 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.pinot.common.function.scalar.bitwise;
+
+import javax.annotation.Nullable;
+import org.apache.pinot.common.function.FunctionInfo;
+import org.apache.pinot.common.function.PinotScalarFunction;
+import org.apache.pinot.common.function.sql.PinotSqlFunction;
+import org.apache.pinot.common.utils.DataSchema.ColumnDataType;
+
+
+/**
+ * Base class for polymorphic unary integral scalar functions.
+ *
+ * <p>Implementations are stateless and thread-safe.
+ */
+abstract class PolymorphicUnaryIntegralScalarFunction implements 
PinotScalarFunction {
+
+  @Nullable
+  @Override
+  public FunctionInfo getFunctionInfo(ColumnDataType[] argumentTypes) {
+    if (argumentTypes.length != 1) {
+      return null;
+    }
+    ColumnDataType argumentType = argumentTypes[0].getStoredType();
+    if (argumentType == ColumnDataType.INT) {
+      return intFunctionInfo();
+    }
+    if (argumentType == ColumnDataType.LONG) {
+      return longFunctionInfo();
+    }
+    return null;
+  }
+
+  @Nullable
+  @Override
+  public FunctionInfo getFunctionInfo(int numArguments) {
+    return numArguments == 1 ? longFunctionInfo() : null;

Review Comment:
   These polymorphic functions already return `null` from 
`getFunctionInfo(int)` — the ingestion evaluator (`InbuiltFunctionEvaluator`) 
now resolves the correct overload by runtime argument types with caching. 
Non-polymorphic functions short-circuit via `_fallbackFunction` and skip type 
inference entirely, so there is no per-row overhead for existing functions. See 
commit 6d462ad.



##########
pinot-common/src/main/java/org/apache/pinot/common/function/scalar/bitwise/PolymorphicShiftScalarFunction.java:
##########
@@ -0,0 +1,69 @@
+/**
+ * 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.pinot.common.function.scalar.bitwise;
+
+import javax.annotation.Nullable;
+import org.apache.pinot.common.function.FunctionInfo;
+import org.apache.pinot.common.function.PinotScalarFunction;
+import org.apache.pinot.common.function.sql.PinotSqlFunction;
+import org.apache.pinot.common.utils.DataSchema.ColumnDataType;
+
+
+/**
+ * Base class for polymorphic integral shift scalar functions.
+ *
+ * <p>Implementations are stateless and thread-safe.
+ */
+abstract class PolymorphicShiftScalarFunction implements PinotScalarFunction {
+
+  @Nullable
+  @Override
+  public FunctionInfo getFunctionInfo(ColumnDataType[] argumentTypes) {
+    if (argumentTypes.length != 2) {
+      return null;
+    }
+    ColumnDataType valueType = argumentTypes[0].getStoredType();
+    ColumnDataType shiftType = argumentTypes[1].getStoredType();
+    if (!BitFunctionUtils.isIntegral(shiftType)) {
+      return null;
+    }
+    if (valueType == ColumnDataType.INT) {
+      return intFunctionInfo();
+    }
+    if (valueType == ColumnDataType.LONG) {
+      return longFunctionInfo();
+    }
+    return null;
+  }
+
+  @Nullable
+  @Override
+  public FunctionInfo getFunctionInfo(int numArguments) {
+    return numArguments == 2 ? longFunctionInfo() : null;

Review Comment:
   Addressed — `getFunctionInfo(int)` already returns `null` here, and the 
ingestion evaluator now resolves by runtime argument types with caching. See 
commit 6d462ad.



##########
pinot-common/src/main/java/org/apache/pinot/common/function/scalar/bitwise/PolymorphicBinaryIntegralScalarFunction.java:
##########
@@ -0,0 +1,66 @@
+/**
+ * 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.pinot.common.function.scalar.bitwise;
+
+import javax.annotation.Nullable;
+import org.apache.pinot.common.function.FunctionInfo;
+import org.apache.pinot.common.function.PinotScalarFunction;
+import org.apache.pinot.common.function.sql.PinotSqlFunction;
+import org.apache.pinot.common.utils.DataSchema.ColumnDataType;
+
+
+/**
+ * Base class for polymorphic binary integral scalar functions.
+ *
+ * <p>Implementations are stateless and thread-safe.
+ */
+abstract class PolymorphicBinaryIntegralScalarFunction implements 
PinotScalarFunction {
+
+  @Nullable
+  @Override
+  public FunctionInfo getFunctionInfo(ColumnDataType[] argumentTypes) {
+    if (argumentTypes.length != 2) {
+      return null;
+    }
+    ColumnDataType leftType = argumentTypes[0].getStoredType();
+    ColumnDataType rightType = argumentTypes[1].getStoredType();
+    if (leftType == ColumnDataType.INT && rightType == ColumnDataType.INT) {
+      return intFunctionInfo();
+    }
+    if (BitFunctionUtils.isIntegral(leftType) && 
BitFunctionUtils.isIntegral(rightType)) {
+      return longFunctionInfo();
+    }
+    return null;
+  }
+
+  @Nullable
+  @Override
+  public FunctionInfo getFunctionInfo(int numArguments) {
+    return numArguments == 2 ? longFunctionInfo() : null;

Review Comment:
   Addressed — `getFunctionInfo(int)` already returns `null` here, and the 
ingestion evaluator now resolves by runtime argument types with caching. See 
commit 6d462ad.



##########
pinot-common/src/main/java/org/apache/pinot/common/function/scalar/bitwise/BitExtractScalarFunction.java:
##########
@@ -0,0 +1,89 @@
+/**
+ * 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.pinot.common.function.scalar.bitwise;
+
+import javax.annotation.Nullable;
+import org.apache.pinot.common.function.FunctionInfo;
+import org.apache.pinot.common.function.PinotScalarFunction;
+import org.apache.pinot.common.function.sql.PinotSqlFunction;
+import org.apache.pinot.common.utils.DataSchema.ColumnDataType;
+import org.apache.pinot.spi.annotations.ScalarFunction;
+
+
+/**
+ * Polymorphic bit extraction scalar function.
+ *
+ * <p>This implementation is stateless and thread-safe.
+ */
+@ScalarFunction(names = {"bitExtract", "extractBit"})
+public class BitExtractScalarFunction implements PinotScalarFunction {
+  private static final FunctionInfo INT_FUNCTION_INFO;
+  private static final FunctionInfo LONG_FUNCTION_INFO;
+
+  static {
+    try {
+      INT_FUNCTION_INFO =
+          new 
FunctionInfo(BitExtractScalarFunction.class.getMethod("intBitExtract", 
int.class, int.class),
+              BitExtractScalarFunction.class, false);
+      LONG_FUNCTION_INFO =
+          new 
FunctionInfo(BitExtractScalarFunction.class.getMethod("longBitExtract", 
long.class, int.class),
+              BitExtractScalarFunction.class, false);
+    } catch (NoSuchMethodException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  @Override
+  public String getName() {
+    return "bitExtract";
+  }
+
+  @Override
+  public PinotSqlFunction toPinotSqlFunction() {
+    return BitFunctionUtils.extractSqlFunction(getName());
+  }
+
+  @Nullable
+  @Override
+  public FunctionInfo getFunctionInfo(ColumnDataType[] argumentTypes) {
+    if (argumentTypes.length != 2) {
+      return null;
+    }
+    ColumnDataType valueType = argumentTypes[0].getStoredType();
+    ColumnDataType bitType = argumentTypes[1].getStoredType();
+    if (!BitFunctionUtils.isIntegral(valueType) || 
!BitFunctionUtils.isIntegral(bitType)) {
+      return null;
+    }
+    return valueType == ColumnDataType.INT ? INT_FUNCTION_INFO : 
LONG_FUNCTION_INFO;
+  }
+
+  @Nullable
+  @Override
+  public FunctionInfo getFunctionInfo(int numArguments) {
+    return numArguments == 2 ? LONG_FUNCTION_INFO : null;

Review Comment:
   Addressed — `getFunctionInfo(int)` already returns `null` here, and the 
ingestion evaluator now resolves by runtime argument types with caching. See 
commit 6d462ad.



##########
pinot-segment-local/src/main/java/org/apache/pinot/segment/local/function/InbuiltFunctionEvaluator.java:
##########
@@ -290,32 +284,133 @@ public Object execute(GenericRow row) {
     @Override
     public Object execute(Object[] values) {
       try {
-        int numArguments = _argumentNodes.length;
-        for (int i = 0; i < numArguments; i++) {
+        for (int i = 0; i < _argumentNodes.length; i++) {
           _arguments[i] = _argumentNodes[i].execute(values);
         }
-        if (!_functionInfo.hasNullableParameters()) {
-          // Preserve null values during ingestion transformation if function 
is an inbuilt
-          // scalar function that cannot handle nulls, and invoked with null 
parameter(s).
-          for (Object argument : _arguments) {
-            if (argument == null) {
-              return null;
-            }
+        return invokeResolvedFunction();
+      } catch (Exception e) {
+        throw new RuntimeException("Caught exception while executing function: 
" + this + ": " + e.getMessage(), e);
+      }
+    }
+
+    private Object invokeResolvedFunction() {
+      ResolvedFunction resolvedFunction = resolveFunction();
+      if (resolvedFunction == null) {
+        if (hasNullArgument()) {
+          return null;
+        }
+        throw new IllegalStateException(String.format("Unsupported function: 
%s with argument types: %s",
+            _functionName, Arrays.toString(getArgumentTypeNames())));
+      }
+      if (!resolvedFunction._functionInfo.hasNullableParameters() && 
hasNullArgument()) {
+        return null;
+      }
+      if (resolvedFunction._functionInvoker.getMethod().isVarArgs()) {
+        return resolvedFunction._functionInvoker.invoke(new 
Object[]{_arguments});
+      }
+      resolvedFunction._functionInvoker.convertTypes(_arguments);
+      return resolvedFunction._functionInvoker.invoke(_arguments);
+    }
+
+    private boolean hasNullArgument() {
+      for (Object argument : _arguments) {
+        if (argument == null) {
+          return true;
+        }
+      }
+      return false;
+    }
+
+    private String[] getArgumentTypeNames() {
+      String[] argumentTypeNames = new String[_arguments.length];
+      for (int i = 0; i < _arguments.length; i++) {
+        Object argument = _arguments[i];
+        argumentTypeNames[i] = argument != null ? 
argument.getClass().getSimpleName() : "null";
+      }
+      return argumentTypeNames;
+    }
+
+    private ResolvedFunction resolveFunction() {
+      ColumnDataType[] argumentTypes = getArgumentTypes();
+      if (argumentTypes != null) {
+        ArgumentTypesKey argumentTypesKey = new 
ArgumentTypesKey(argumentTypes);
+        ResolvedFunction resolvedFunction = 
_resolvedFunctionCache.get(argumentTypesKey);
+        if (resolvedFunction == null) {
+          FunctionInfo functionInfo = 
FunctionRegistry.lookupFunctionInfo(_canonicalName, argumentTypes);
+          if (functionInfo != null) {
+            resolvedFunction = new ResolvedFunction(functionInfo);
+            _resolvedFunctionCache.put(argumentTypesKey, resolvedFunction);
           }
         }
-        if (_functionInvoker.getMethod().isVarArgs()) {
-          return _functionInvoker.invoke(new Object[]{_arguments});
+        if (resolvedFunction != null) {
+          _lastResolvedFunction = resolvedFunction;
+          return resolvedFunction;
         }
-        _functionInvoker.convertTypes(_arguments);
-        return _functionInvoker.invoke(_arguments);
-      } catch (Exception e) {
-        throw new RuntimeException("Caught exception while executing function: 
" + this + ": " + e.getMessage(), e);
       }
+      if (_lastResolvedFunction != null) {
+        return _lastResolvedFunction;
+      }
+      if (_fallbackFunction != null) {
+        _lastResolvedFunction = _fallbackFunction;

Review Comment:
   Fixed — `resolveFunction()` now returns `null` immediately when argument 
types are known but no overload matches (fail fast). It only falls back to 
`_lastResolvedFunction` when argument types cannot be determined (e.g. null 
values). See commit 6d462ad.



##########
pinot-segment-local/src/main/java/org/apache/pinot/segment/local/function/InbuiltFunctionEvaluator.java:
##########
@@ -290,32 +284,133 @@ public Object execute(GenericRow row) {
     @Override
     public Object execute(Object[] values) {
       try {
-        int numArguments = _argumentNodes.length;
-        for (int i = 0; i < numArguments; i++) {
+        for (int i = 0; i < _argumentNodes.length; i++) {
           _arguments[i] = _argumentNodes[i].execute(values);
         }
-        if (!_functionInfo.hasNullableParameters()) {
-          // Preserve null values during ingestion transformation if function 
is an inbuilt
-          // scalar function that cannot handle nulls, and invoked with null 
parameter(s).
-          for (Object argument : _arguments) {
-            if (argument == null) {
-              return null;
-            }
+        return invokeResolvedFunction();
+      } catch (Exception e) {
+        throw new RuntimeException("Caught exception while executing function: 
" + this + ": " + e.getMessage(), e);
+      }
+    }
+
+    private Object invokeResolvedFunction() {
+      ResolvedFunction resolvedFunction = resolveFunction();
+      if (resolvedFunction == null) {

Review Comment:
   Fixed — `invokeResolvedFunction()` now short-circuits on `_fallbackFunction` 
before calling `resolveFunction()`, so non-polymorphic functions skip runtime 
type inference entirely. `resolveFunction()` is only invoked for polymorphic 
functions where `_fallbackFunction` is null. See commit 6d462ad.



-- 
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: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


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

Reply via email to