This is an automated email from the ASF dual-hosted git repository.
mbudiu 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 3ea7799649 [CALCITE-6936] Table function parameter matching should
always be case-insensitive
3ea7799649 is described below
commit 3ea77996491de99067abfdeeb18dc8ed123a40d2
Author: Mihai Budiu <[email protected]>
AuthorDate: Mon Apr 7 17:22:48 2025 -0700
[CALCITE-6936] Table function parameter matching should always be
case-insensitive
Signed-off-by: Mihai Budiu <[email protected]>
---
.../org/apache/calcite/sql/SqlCallBinding.java | 15 ++--------
.../org/apache/calcite/test/SqlValidatorTest.java | 35 +++++++++-------------
2 files changed, 17 insertions(+), 33 deletions(-)
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlCallBinding.java
b/core/src/main/java/org/apache/calcite/sql/SqlCallBinding.java
index bf0bee7743..08e593c530 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlCallBinding.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlCallBinding.java
@@ -32,6 +32,7 @@
import org.apache.calcite.sql.validate.SelectScope;
import org.apache.calcite.sql.validate.SqlMonotonicity;
import org.apache.calcite.sql.validate.SqlNameMatcher;
+import org.apache.calcite.sql.validate.SqlNameMatchers;
import org.apache.calcite.sql.validate.SqlValidator;
import org.apache.calcite.sql.validate.SqlValidatorException;
import org.apache.calcite.sql.validate.SqlValidatorNamespace;
@@ -39,7 +40,6 @@
import org.apache.calcite.sql.validate.SqlValidatorUtil;
import org.apache.calcite.util.ImmutableNullableList;
import org.apache.calcite.util.NlsString;
-import org.apache.calcite.util.Pair;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
@@ -183,10 +183,10 @@ private List<SqlNode> permutedOperands(final SqlCall
call) {
+ ", operator " + operator);
final List<String> paramNames = operandMetadata.paramNames();
final List<SqlNode> permuted = new ArrayList<>();
+ // Always use case-insensitive lookup for parameter names
final SqlNameMatcher nameMatcher =
- validator.getCatalogReader().nameMatcher();
+ SqlNameMatchers.withCaseSensitive(false);
for (final String paramName : paramNames) {
- Pair<String, SqlIdentifier> args = null;
for (int j = 0; j < call.getOperandList().size(); j++) {
final SqlCall call2 = call.operand(j);
assert call2.getKind() == SqlKind.ARGUMENT_ASSIGNMENT;
@@ -195,18 +195,9 @@ private List<SqlNode> permutedOperands(final SqlCall call)
{
if (nameMatcher.matches(operandName, paramName)) {
permuted.add(call2.operand(0));
break;
- } else if (args == null
- && nameMatcher.isCaseSensitive()
- && operandName.equalsIgnoreCase(paramName)) {
- args = Pair.of(paramName, operandID);
}
// the last operand, there is still no match.
if (j == call.getOperandList().size() - 1) {
- if (args != null) {
- throw SqlUtil.newContextException(args.right.getParserPosition(),
-
RESOURCE.paramNotFoundInFunctionDidYouMean(args.right.getSimple(),
- operator.getName(), args.left));
- }
if (operandMetadata.isFixedParameters()) {
// Not like user defined functions, we do not patch up the operands
// with DEFAULT and then convert to nulls during sql-to-rel
conversion.
diff --git a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
index ea5485b0b8..0b45476964 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
@@ -11059,13 +11059,6 @@ private void checkCustomColumnResolving(String table) {
+ "(TIMESTAMP\\(0\\) ROWTIME, INTEGER PRODUCTID, INTEGER
ORDERID\\)>, <COLUMN_LIST>, "
+ "<INTERVAL HOUR>\\)'\\. Supported form\\(s\\): TUMBLE\\(TABLE
table_name, "
+ "DESCRIPTOR\\(timecol\\), datetime interval\\[, datetime
interval\\]\\)");
- sql("select rowtime, productid, orderid, 'window_start', 'window_end'\n"
- + "from table(\n"
- + "tumble(\n"
- + "^\"data\"^ => table orders,\n"
- + "TIMECOL => descriptor(rowtime),\n"
- + "SIZE => interval '2' hour))")
- .fails("Param 'data' not found in function 'TUMBLE'; did you mean
'DATA'\\?");
sql("select rowtime, productid, orderid, 'window_start', 'window_end'\n"
+ "from table(\n"
+ "^tumble(\n"
@@ -11099,6 +11092,20 @@ private void checkCustomColumnResolving(String table) {
sql("select * from table(\n"
+ "tumble(TABLE ^tabler_not_exist^, descriptor(rowtime), interval '2'
hour))")
.fails("Object 'TABLER_NOT_EXIST' not found");
+ // Test case for <a
href="https://issues.apache.org/jira/browse/CALCITE-6936">[CALCITE-6936]
+ // Table function parameter matching should always be case-insensitive</a>.
+ // We cannot use table "orders", since lookup fails with
unquotedCasing.TO_LOWER,
+ // so we make up a new table o.
+ sql("with o as "
+ + "(select TIMESTAMP '2020-01-01 00:00:00' as rowtime, 2 as productid,
3 as orderid)"
+ + "select rowtime, productid, orderid, 'window_start', 'window_end'\n"
+ + "from table(\n"
+ + "tumble(\n"
+ + "data => table o,\n"
+ + "timecol => descriptor(rowtime),\n"
+ + "size => interval '2' hour))")
+ .withUnquotedCasing(Casing.TO_LOWER)
+ .ok();
}
@Test void testHopTableFunction() {
@@ -11142,13 +11149,6 @@ private void checkCustomColumnResolving(String table) {
+ "<INTERVAL HOUR>, <INTERVAL HOUR>\\)'\\. Supported form\\(s\\): "
+ "HOP\\(TABLE table_name, DESCRIPTOR\\(timecol\\), "
+ "datetime interval, datetime interval\\[, datetime
interval\\]\\)");
- sql("select * from table(\n"
- + "hop(\n"
- + "^\"data\"^ => table orders,\n"
- + "timecol => descriptor(rowtime),\n"
- + "slide => interval '2' hour,\n"
- + "size => interval '1' hour))")
- .fails("Param 'data' not found in function 'HOP'; did you mean
'DATA'\\?");
sql("select * from table(\n"
+ "^hop(\n"
+ "data => table orders,\n"
@@ -11218,13 +11218,6 @@ private void checkCustomColumnResolving(String table) {
+ "0\\) ROWTIME, INTEGER PRODUCTID, INTEGER ORDERID\\)>,
<COLUMN_LIST>, "
+ "<INTERVAL HOUR>\\)'. Supported form\\(s\\): SESSION\\(TABLE
table_name, DESCRIPTOR\\("
+ "timecol\\), DESCRIPTOR\\(key\\) optional, datetime
interval\\)");
- sql("select * from table(\n"
- + "session(\n"
- + "^\"data\"^ => table orders,\n"
- + "timecol => descriptor(rowtime),\n"
- + "key => descriptor(productid),\n"
- + "size => interval '1' hour))")
- .fails("Param 'data' not found in function 'SESSION'; did you mean
'DATA'\\?");
sql("select * from table(\n"
+ "^session(table orders, descriptor(rowtime), descriptor(productid),
'test')^)")
.fails("Cannot apply 'SESSION' to arguments of type
'SESSION\\(<RECORDTYPE\\(TIMESTAMP\\("