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";

Reply via email to