Skip to content

Commit c26a10f

Browse files
committed
Fix UDFOperandMetadata type checking for variable-arity UDFs in SQL path
Calcite's FamilyOperandTypeChecker.checkOperandTypes requires exact operand count match against the family list size, which fails for SQL calls with fewer optional args. Both wrap() overloads now derive List<RelDataType> from SqlCallBinding and delegate to PPL's existing type checkers (PPLFamilyTypeCheckerWrapper, PPLCompositeTypeChecker) which correctly handle optional parameters by checking count range first, then validating only the provided operands. Signed-off-by: Chen Dai <daichen@amazon.com>
1 parent 8a7524c commit c26a10f

1 file changed

Lines changed: 36 additions & 2 deletions

File tree

core/src/main/java/org/opensearch/sql/expression/function/UDFOperandMetadata.java

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
package org.opensearch.sql.expression.function;
77

8+
import java.util.ArrayList;
89
import java.util.Collections;
910
import java.util.List;
1011
import org.apache.calcite.rel.type.RelDataType;
@@ -48,7 +49,19 @@ public List<String> paramNames() {
4849

4950
@Override
5051
public boolean checkOperandTypes(SqlCallBinding callBinding, boolean throwOnFailure) {
51-
return typeChecker.checkOperandTypesWithoutTypeCoercion(callBinding, throwOnFailure);
52+
// Derive operand types and validate via PPLFamilyTypeCheckerWrapper which correctly
53+
// handles optional parameters. Calcite's FamilyOperandTypeChecker.checkOperandTypes
54+
// requires exact count match against the family list size, rejecting valid calls with
55+
// fewer optional args.
56+
List<RelDataType> argTypes = deriveOperandTypes(callBinding, callBinding.getOperandCount());
57+
if (new PPLTypeChecker.PPLFamilyTypeCheckerWrapper(typeChecker)
58+
.checkOperandTypes(argTypes)) {
59+
return true;
60+
}
61+
if (throwOnFailure) {
62+
throw callBinding.newValidationSignatureError();
63+
}
64+
return false;
5265
}
5366

5467
@Override
@@ -91,7 +104,18 @@ public List<String> paramNames() {
91104

92105
@Override
93106
public boolean checkOperandTypes(SqlCallBinding callBinding, boolean throwOnFailure) {
94-
return typeChecker.checkOperandTypes(callBinding, throwOnFailure);
107+
// Derive operand types and validate via PPLCompositeTypeChecker which correctly handles
108+
// variable-arity functions with optional parameters. Calcite's built-in
109+
// FamilyOperandTypeChecker.checkOperandTypes requires exact operand count match against
110+
// the family list size, rejecting valid calls with fewer optional args.
111+
List<RelDataType> argTypes = deriveOperandTypes(callBinding, callBinding.getOperandCount());
112+
if (new PPLTypeChecker.PPLCompositeTypeChecker(typeChecker).checkOperandTypes(argTypes)) {
113+
return true;
114+
}
115+
if (throwOnFailure) {
116+
throw callBinding.newValidationSignatureError();
117+
}
118+
return false;
95119
}
96120

97121
@Override
@@ -106,6 +130,16 @@ public String getAllowedSignatures(SqlOperator op, String opName) {
106130
};
107131
}
108132

133+
/** Derives operand types from a SqlCallBinding for use with PPL type checkers. */
134+
private static List<RelDataType> deriveOperandTypes(SqlCallBinding callBinding, int count) {
135+
List<RelDataType> types = new ArrayList<>(count);
136+
for (int i = 0; i < count; i++) {
137+
types.add(
138+
callBinding.getValidator().deriveType(callBinding.getScope(), callBinding.operand(i)));
139+
}
140+
return types;
141+
}
142+
109143
static UDFOperandMetadata wrapUDT(List<List<ExprType>> allowSignatures) {
110144
return new UDTOperandMetadata(allowSignatures);
111145
}

0 commit comments

Comments
 (0)