This is an automated email from the ASF dual-hosted git repository.
xuzifu666 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/calcite.git
The following commit(s) were added to refs/heads/main by this push:
new fe3450e816 [CALCITE-6245] A ValidationError occurs when a named
parameter is used on a ScalarFunction or AggregateFunction
fe3450e816 is described below
commit fe3450e816e127c5f02da13c1de19dcbd740024d
Author: Yu Xu <[email protected]>
AuthorDate: Sun Oct 18 14:09:02 2026 +0800
[CALCITE-6245] A ValidationError occurs when a named parameter is used on a
ScalarFunction or AggregateFunction
---
.../main/java/org/apache/calcite/sql/SqlUtil.java | 25 +++++++-
.../sql/validate/implicit/TypeCoercionImpl.java | 2 +-
.../test/java/org/apache/calcite/test/UdfTest.java | 72 ++++++++++++++++++++++
3 files changed, 95 insertions(+), 4 deletions(-)
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlUtil.java
b/core/src/main/java/org/apache/calcite/sql/SqlUtil.java
index e6bccfe51e..e1e6e23e4e 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlUtil.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlUtil.java
@@ -41,6 +41,7 @@
import org.apache.calcite.sql.util.SqlBasicVisitor;
import org.apache.calcite.sql.util.SqlVisitor;
import org.apache.calcite.sql.validate.SqlNameMatcher;
+import org.apache.calcite.sql.validate.SqlNameMatchers;
import org.apache.calcite.sql.validate.SqlValidatorUtil;
import org.apache.calcite.util.BarfingInvocationHandler;
import org.apache.calcite.util.ConversionUtil;
@@ -754,7 +755,7 @@ private static Iterator<SqlOperator>
filterRoutinesByParameterTypeAndName(
// parameters of all of these names.
Map<Integer, Integer> map = new HashMap<>();
for (Ord<String> argName : Ord.zip(argNames)) {
- int i = paramNames.indexOf(argName.e);
+ int i = lookupParamNameCaseInsensitive(paramNames, argName.e);
if (i < 0) {
return null;
}
@@ -766,6 +767,24 @@ private static Iterator<SqlOperator>
filterRoutinesByParameterTypeAndName(
});
}
+ /** Looks up a parameter name in a list of formal parameter names,
+ * case-insensitively.
+ *
+ * @param paramNames list of formal parameter names
+ * @param argName argument name to look up
+ * @return index of the first matching parameter, or -1 if not found
+ */
+ public static int lookupParamNameCaseInsensitive(List<String> paramNames,
+ String argName) {
+ for (int i = 0; i < paramNames.size(); i++) {
+ if (SqlNameMatchers.withCaseSensitive(false)
+ .matches(paramNames.get(i), argName)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
/**
* Filters an iterator of routines, keeping only those with the best match
for
* the actual argument types.
@@ -802,7 +821,7 @@ private static Iterator<SqlOperator>
filterRoutinesByTypePrecedence(
final List<RelDataType> paramTypes =
operandMetadata.paramTypes(typeFactory);
int index = argNames != null
- ? paramNames.indexOf(argNames.get(argType.i))
+ ? lookupParamNameCaseInsensitive(paramNames,
argNames.get(argType.i))
: argType.i;
final RelDataType paramType = paramTypes.get(index);
return precList.compareTypePrecedence(paramType, bestMatch) >= 0;
@@ -828,7 +847,7 @@ private static Iterator<SqlOperator>
filterRoutinesByTypePrecedence(
operandMetadata.paramTypes(typeFactory);
final List<String> paramNames = operandMetadata.paramNames();
final RelDataType paramType = argNames != null
- ? paramTypes.get(paramNames.indexOf(argNames.get(i)))
+ ? paramTypes.get(lookupParamNameCaseInsensitive(paramNames,
argNames.get(i)))
: paramTypes.get(i);
if (bestMatch == null) {
bestMatch = paramType;
diff --git
a/core/src/main/java/org/apache/calcite/sql/validate/implicit/TypeCoercionImpl.java
b/core/src/main/java/org/apache/calcite/sql/validate/implicit/TypeCoercionImpl.java
index 46dc3af5ed..7f4d0d7356 100644
---
a/core/src/main/java/org/apache/calcite/sql/validate/implicit/TypeCoercionImpl.java
+++
b/core/src/main/java/org/apache/calcite/sql/validate/implicit/TypeCoercionImpl.java
@@ -798,7 +798,7 @@ && coerceOperandType(binding.getScope(), binding.getCall(),
i, implicitType)
final List<SqlNode> operandList = ((SqlCall) operand).getOperandList();
String name = ((SqlIdentifier) operandList.get(1)).getSimple();
final List<String> paramNames = operandMetadata.paramNames();
- int formalIndex = paramNames.indexOf(name);
+ int formalIndex = SqlUtil.lookupParamNameCaseInsensitive(paramNames,
name);
if (formalIndex < 0) {
return false;
}
diff --git a/core/src/test/java/org/apache/calcite/test/UdfTest.java
b/core/src/test/java/org/apache/calcite/test/UdfTest.java
index 282cc0ae21..ec641a6e9a 100644
--- a/core/src/test/java/org/apache/calcite/test/UdfTest.java
+++ b/core/src/test/java/org/apache/calcite/test/UdfTest.java
@@ -247,6 +247,78 @@ private CalciteAssert.AssertThat withUdf() {
assertThat(after, is(before + 1));
}
+ /** Test case for
+ * <a
href="https://issues.apache.org/jira/browse/CALCITE-6245">[CALCITE-6245]
+ * A ValidationError occurs when a named parameter is used on a
+ * ScalarFunction or AggregateFunction</a>. */
+ @Test void testScalarFunctionWithParameter() {
+ final String sql = "select \"adhoc\".my_det_plus(x => \"deptno\", y =>
100) as p\n"
+ + "from \"adhoc\".EMPLOYEES";
+ final AtomicInteger c =
Smalls.MyDeterministicPlusFunction.INSTANCE_COUNT.get();
+ final int before = c.get();
+ withUdf()
+ .with("caseSensitive", false)
+ .query(sql).returnsUnordered("P=110",
+ "P=120",
+ "P=110",
+ "P=110");
+ final int after = c.get();
+ assertThat(after, is(before + 1));
+ // Unquoted argument names in reverse order: the parser upper-cases 'x' and
+ // 'y' to 'X' and 'Y', and the arguments must still be permuted into the
+ // formal parameter order (x first, y second).
+ final String reverseSql = "select \"adhoc\".my_det_plus(y => 100, x =>
\"deptno\") as p\n"
+ + "from \"adhoc\".EMPLOYEES";
+ withUdf()
+ .with("caseSensitive", false)
+ .query(reverseSql).returnsUnordered("P=110",
+ "P=120",
+ "P=110",
+ "P=110");
+ }
+
+ /** Test case for
+ * <a
href="https://issues.apache.org/jira/browse/CALCITE-6245">[CALCITE-6245]
+ * A ValidationError occurs when a named parameter is used on a
+ * ScalarFunction or AggregateFunction</a>. */
+ @Test void testAggFunctionWithParameter() {
+ final String empDept = JdbcTest.EmpDeptTableFactory.class.getName();
+ final String sum = Smalls.MyStaticSumFunction.class.getName();
+ final String sum2 = Smalls.MySumFunction.class.getName();
+ final CalciteAssert.AssertThat with = CalciteAssert.model("{\n"
+ + " version: '1.0',\n"
+ + " schemas: [\n"
+ + " {\n"
+ + " name: 'adhoc',\n"
+ + " tables: [\n"
+ + " {\n"
+ + " name: 'EMPLOYEES',\n"
+ + " type: 'custom',\n"
+ + " factory: '" + empDept + "',\n"
+ + " operand: {'foo': true, 'bar': 345}\n"
+ + " }\n"
+ + " ],\n"
+ + " functions: [\n"
+ + " {\n"
+ + " name: 'MY_SUM',\n"
+ + " className: '" + sum + "'\n"
+ + " },\n"
+ + " {\n"
+ + " name: 'MY_SUM2',\n"
+ + " className: '" + sum2 + "'\n"
+ + " }\n"
+ + " ]\n"
+ + " }\n"
+ + " ]\n"
+ + "}")
+ .withDefaultSchema("adhoc")
+ .with("caseSensitive", false);
+ with.withDefaultSchema(null)
+ .query(
+ "select \"adhoc\".my_sum(arg1 => \"deptno\") as p from
\"adhoc\".EMPLOYEES\n")
+ .returns("P=50\n");
+ }
+
@Test void testUserDefinedFunctionB() {
final String sql = "select \"adhoc\".my_double(\"deptno\") as p\n"
+ "from \"adhoc\".EMPLOYEES";