Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 22 additions & 3 deletions core/src/main/java/org/apache/calcite/sql/SqlUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand All @@ -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.
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
72 changes: 72 additions & 0 deletions core/src/test/java/org/apache/calcite/test/UdfTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
Loading