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 af4391db0d [CALCITE-6645] Support user-defined function without
parentheses when db dialect's allowNiladicParentheses property is false
af4391db0d is described below
commit af4391db0db8c820d99a8907e940dcdb1812a6e1
Author: Zhengqiang Duan <[email protected]>
AuthorDate: Sun Oct 27 14:36:52 2024 +0800
[CALCITE-6645] Support user-defined function without parentheses when db
dialect's allowNiladicParentheses property is false
---
.../calcite/prepare/CalciteCatalogReader.java | 14 ++++---
.../sql/validate/SqlUserDefinedFunction.java | 22 ++++++++++-
.../sql/validate/SqlUserDefinedTableFunction.java | 3 +-
.../calcite/sql/validate/SqlValidatorImpl.java | 2 +-
.../test/java/org/apache/calcite/test/UdfTest.java | 44 ++++++++++++++++++++--
.../calcite/piglet/PigUserDefinedFunction.java | 3 +-
.../calcite/chinook/CalciteConnectionProvider.java | 1 +
.../org/apache/calcite/test/CalciteAssert.java | 9 +++++
.../main/java/org/apache/calcite/util/Smalls.java | 9 +++++
9 files changed, 94 insertions(+), 13 deletions(-)
diff --git
a/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java
b/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java
index 170bb1d1c0..264d6e88c8 100644
--- a/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java
+++ b/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java
@@ -275,7 +275,7 @@ public class CalciteCatalogReader implements
Prepare.CatalogReader {
getFunctionsFrom(opName.names)
.stream()
.filter(predicate)
- .map(function -> toOp(opName, function))
+ .map(function -> toOp(opName, function, config))
.forEachOrdered(operatorList::add);
}
@@ -296,7 +296,7 @@ public class CalciteCatalogReader implements
Prepare.CatalogReader {
for (String name : schema.getFunctionNames()) {
schema.getFunctions(name, true).forEach(function -> {
final SqlIdentifier id = new SqlIdentifier(name, SqlParserPos.ZERO);
- list.add(toOp(id, function));
+ list.add(toOp(id, function, CalciteConnectionConfig.DEFAULT));
});
}
return SqlOperatorTables.of(list);
@@ -304,7 +304,7 @@ public class CalciteCatalogReader implements
Prepare.CatalogReader {
/** Converts a function to a {@link org.apache.calcite.sql.SqlOperator}. */
private static SqlOperator toOp(SqlIdentifier name,
- final org.apache.calcite.schema.Function function) {
+ final org.apache.calcite.schema.Function function,
CalciteConnectionConfig config) {
final Function<RelDataTypeFactory, List<RelDataType>> argTypesFactory =
typeFactory -> function.getParameters()
.stream()
@@ -344,8 +344,12 @@ public class CalciteCatalogReader implements
Prepare.CatalogReader {
if (function instanceof ScalarFunction) {
final SqlReturnTypeInference returnTypeInference =
infer((ScalarFunction) function);
+ SqlSyntax syntax = function.getParameters().isEmpty()
+ && !config.conformance().allowNiladicParentheses()
+ ? SqlSyntax.FUNCTION_ID
+ : SqlSyntax.FUNCTION;
return new SqlUserDefinedFunction(name, kind, returnTypeInference,
- operandTypeInference, operandMetadata, function);
+ operandTypeInference, operandMetadata, function, syntax);
} else if (function instanceof AggregateFunction) {
final SqlReturnTypeInference returnTypeInference =
infer((AggregateFunction) function);
@@ -423,7 +427,7 @@ public class CalciteCatalogReader implements
Prepare.CatalogReader {
if (schema != null) {
for (String name : schema.getFunctionNames()) {
schema.getFunctions(name, true).forEach(f ->
- builder.add(toOp(new SqlIdentifier(name, SqlParserPos.ZERO),
f)));
+ builder.add(toOp(new SqlIdentifier(name, SqlParserPos.ZERO), f,
config)));
}
}
}
diff --git
a/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedFunction.java
b/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedFunction.java
index 1ff463eef6..d75514ca61 100644
---
a/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedFunction.java
+++
b/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedFunction.java
@@ -23,6 +23,7 @@ import org.apache.calcite.sql.SqlFunction;
import org.apache.calcite.sql.SqlFunctionCategory;
import org.apache.calcite.sql.SqlIdentifier;
import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.SqlSyntax;
import org.apache.calcite.sql.type.SqlOperandMetadata;
import org.apache.calcite.sql.type.SqlOperandTypeChecker;
import org.apache.calcite.sql.type.SqlOperandTypeInference;
@@ -42,6 +43,8 @@ import java.util.List;
public class SqlUserDefinedFunction extends SqlFunction {
public final Function function;
+ public final SqlSyntax syntax;
+
@Deprecated // to be removed before 2.0
public SqlUserDefinedFunction(SqlIdentifier opName,
SqlReturnTypeInference returnTypeInference,
@@ -63,7 +66,17 @@ public class SqlUserDefinedFunction extends SqlFunction {
@Nullable SqlOperandMetadata operandMetadata,
Function function) {
this(opName, kind, returnTypeInference, operandTypeInference,
- operandMetadata, function, SqlFunctionCategory.USER_DEFINED_FUNCTION);
+ operandMetadata, function, SqlFunctionCategory.USER_DEFINED_FUNCTION,
SqlSyntax.FUNCTION);
+ }
+
+ /** Creates a {@link SqlUserDefinedFunction} with sql syntax. */
+ public SqlUserDefinedFunction(SqlIdentifier opName, SqlKind kind,
+ SqlReturnTypeInference returnTypeInference,
+ SqlOperandTypeInference operandTypeInference,
+ @Nullable SqlOperandMetadata operandMetadata,
+ Function function, SqlSyntax syntax) {
+ this(opName, kind, returnTypeInference, operandTypeInference,
+ operandMetadata, function, SqlFunctionCategory.USER_DEFINED_FUNCTION,
syntax);
}
/** Constructor used internally and by derived classes. */
@@ -72,16 +85,21 @@ public class SqlUserDefinedFunction extends SqlFunction {
SqlOperandTypeInference operandTypeInference,
@Nullable SqlOperandMetadata operandMetadata,
Function function,
- SqlFunctionCategory category) {
+ SqlFunctionCategory category, SqlSyntax syntax) {
super(Util.last(opName.names), opName, kind, returnTypeInference,
operandTypeInference, operandMetadata, category);
this.function = function;
+ this.syntax = syntax;
}
@Override public @Nullable SqlOperandMetadata getOperandTypeChecker() {
return (@Nullable SqlOperandMetadata) super.getOperandTypeChecker();
}
+ @Override public SqlSyntax getSyntax() {
+ return syntax;
+ }
+
/**
* Returns function that implements given operator call.
*
diff --git
a/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedTableFunction.java
b/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedTableFunction.java
index 818e9fff52..5b524daa77 100644
---
a/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedTableFunction.java
+++
b/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedTableFunction.java
@@ -22,6 +22,7 @@ import org.apache.calcite.sql.SqlFunctionCategory;
import org.apache.calcite.sql.SqlIdentifier;
import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.sql.SqlOperatorBinding;
+import org.apache.calcite.sql.SqlSyntax;
import org.apache.calcite.sql.SqlTableFunction;
import org.apache.calcite.sql.type.SqlOperandMetadata;
import org.apache.calcite.sql.type.SqlOperandTypeChecker;
@@ -64,7 +65,7 @@ public class SqlUserDefinedTableFunction extends
SqlUserDefinedFunction
TableFunction function) {
super(opName, kind, returnTypeInference, operandTypeInference,
operandMetadata, function,
- SqlFunctionCategory.USER_DEFINED_TABLE_FUNCTION);
+ SqlFunctionCategory.USER_DEFINED_TABLE_FUNCTION, SqlSyntax.FUNCTION);
}
/**
diff --git
a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
index ba4e3203a9..0a80a414da 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
@@ -1959,7 +1959,7 @@ public class SqlValidatorImpl implements
SqlValidatorWithHints {
}
@Override public @Nullable SqlCall makeNullaryCall(SqlIdentifier id) {
- if (id.names.size() == 1 && !id.isComponentQuoted(0)) {
+ if (!id.isComponentQuoted(id.names.size() - 1)) {
final List<SqlOperator> list = new ArrayList<>();
opTab.lookupOperatorOverloads(id, null, SqlSyntax.FUNCTION, list,
catalogReader.nameMatcher());
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 56216a55e1..b796a0c7bd 100644
--- a/core/src/test/java/org/apache/calcite/test/UdfTest.java
+++ b/core/src/test/java/org/apache/calcite/test/UdfTest.java
@@ -34,6 +34,7 @@ import org.apache.calcite.schema.impl.AbstractSchema;
import org.apache.calcite.schema.impl.ScalarFunctionImpl;
import org.apache.calcite.schema.impl.ViewTable;
import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.sql.validate.SqlConformanceEnum;
import org.apache.calcite.test.schemata.hr.HrSchema;
import org.apache.calcite.util.Smalls;
@@ -122,6 +123,12 @@ class UdfTest {
+ "'\n"
+ " },\n"
+ " {\n"
+ + " name: 'MY_NILADIC_PARENTHESES',\n"
+ + " className: '"
+ + Smalls.MyNiladicParenthesesFunction.class.getName()
+ + "'\n"
+ + " },\n"
+ + " {\n"
+ " name: 'COUNT_ARGS',\n"
+ " className: '"
+ Smalls.CountArgs0Function.class.getName()
@@ -407,14 +414,43 @@ class UdfTest {
.returns("P=null\n");
}
+ /** Test case for
+ * <a
href="https://issues.apache.org/jira/browse/CALCITE-6645">[CALCITE-6645]
+ * User-defined function with niladic parentheses</a>. */
+ @Test void testUserDefinedFunctionWithNiladicParentheses() {
+ final CalciteAssert.AssertThat with = withUdf();
+ with.with(SqlConformanceEnum.MYSQL_5)
+ .query("select \"adhoc\".my_niladic_parentheses() as p\n"
+ + "from \"adhoc\".EMPLOYEES limit 1")
+ .returns("P=foo\n");
+ with.with(SqlConformanceEnum.ORACLE_10)
+ .query("select \"adhoc\".my_niladic_parentheses as p\n"
+ + "from \"adhoc\".EMPLOYEES limit 1")
+ .returns("P=foo\n");
+ // wrong niladic function with mysql_5 conformance
+ with.with(SqlConformanceEnum.MYSQL_5)
+ .query("select \"adhoc\".my_niladic_parentheses as p\n"
+ + "from \"adhoc\".EMPLOYEES limit 1")
+ .throws_("Table 'adhoc' not found");
+ // wrong niladic function with oracle_10 conformance
+ with.with(SqlConformanceEnum.ORACLE_10)
+ .query("select \"adhoc\".my_niladic_parentheses() as p\n"
+ + "from \"adhoc\".EMPLOYEES limit 1")
+ .throws_("No match found for function signature
MY_NILADIC_PARENTHESES()");
+ }
+
/** Tests a user-defined function that has multiple overloads. */
@Test void testUdfOverloaded() {
final CalciteAssert.AssertThat with = withUdf();
- with.query("values (\"adhoc\".count_args(),\n"
+ // MYSQL_5 conformance support niladic function with parentheses
+ with.with(SqlConformanceEnum.MYSQL_5)
+ .query("values (\"adhoc\".count_args(),\n"
+ " \"adhoc\".count_args(0),\n"
+ " \"adhoc\".count_args(0, 0))")
.returns("EXPR$0=0; EXPR$1=1; EXPR$2=2\n");
- with.query("select max(\"adhoc\".count_args()) as p0,\n"
+ // MYSQL_5 conformance support niladic function with parentheses
+ with.with(SqlConformanceEnum.MYSQL_5)
+ .query("select max(\"adhoc\".count_args()) as p0,\n"
+ " min(\"adhoc\".count_args(0)) as p1,\n"
+ " max(\"adhoc\".count_args(0, 0)) as p2\n"
+ "from \"adhoc\".EMPLOYEES limit 1")
@@ -423,7 +459,9 @@ class UdfTest {
@Test void testUdfOverloadedNullable() {
final CalciteAssert.AssertThat with = withUdf();
- with.query("values (\"adhoc\".count_args(),\n"
+ // MYSQL_5 conformance support niladic function with parentheses
+ with.with(SqlConformanceEnum.MYSQL_5)
+ .query("values (\"adhoc\".count_args(),\n"
+ " \"adhoc\".count_args(cast(null as smallint)),\n"
+ " \"adhoc\".count_args(0, 0))")
.returns("EXPR$0=0; EXPR$1=-1; EXPR$2=2\n");
diff --git
a/piglet/src/main/java/org/apache/calcite/piglet/PigUserDefinedFunction.java
b/piglet/src/main/java/org/apache/calcite/piglet/PigUserDefinedFunction.java
index 720a3d99e5..2a33b87b20 100644
--- a/piglet/src/main/java/org/apache/calcite/piglet/PigUserDefinedFunction.java
+++ b/piglet/src/main/java/org/apache/calcite/piglet/PigUserDefinedFunction.java
@@ -20,6 +20,7 @@ import org.apache.calcite.schema.Function;
import org.apache.calcite.sql.SqlFunctionCategory;
import org.apache.calcite.sql.SqlIdentifier;
import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.SqlSyntax;
import org.apache.calcite.sql.parser.SqlParserPos;
import org.apache.calcite.sql.type.SqlOperandMetadata;
import org.apache.calcite.sql.type.SqlOperandTypeInference;
@@ -42,7 +43,7 @@ public class PigUserDefinedFunction extends
SqlUserDefinedFunction {
FuncSpec funcSpec) {
super(opName, SqlKind.OTHER_FUNCTION, returnTypeInference,
operandTypeInference, operandMetadata, function,
- SqlFunctionCategory.USER_DEFINED_CONSTRUCTOR);
+ SqlFunctionCategory.USER_DEFINED_CONSTRUCTOR, SqlSyntax.FUNCTION);
this.funcSpec = funcSpec;
}
diff --git
a/plus/src/main/java/org/apache/calcite/chinook/CalciteConnectionProvider.java
b/plus/src/main/java/org/apache/calcite/chinook/CalciteConnectionProvider.java
index f0eeabba12..f4b9425276 100644
---
a/plus/src/main/java/org/apache/calcite/chinook/CalciteConnectionProvider.java
+++
b/plus/src/main/java/org/apache/calcite/chinook/CalciteConnectionProvider.java
@@ -44,6 +44,7 @@ public class CalciteConnectionProvider {
Properties info = new Properties();
info.setProperty("lex", "MYSQL");
info.setProperty("model", "inline:" + provideSchema());
+ info.setProperty("conformance", "MYSQL_5");
return info;
}
diff --git a/testkit/src/main/java/org/apache/calcite/test/CalciteAssert.java
b/testkit/src/main/java/org/apache/calcite/test/CalciteAssert.java
index 62dbc54b24..b8bd4cd2a6 100644
--- a/testkit/src/main/java/org/apache/calcite/test/CalciteAssert.java
+++ b/testkit/src/main/java/org/apache/calcite/test/CalciteAssert.java
@@ -205,6 +205,10 @@ public class CalciteAssert {
return this;
}
+ @Override public AssertThat with(SqlConformanceEnum conformance) {
+ return this;
+ }
+
@Override public AssertThat with(
ConnectionPostProcessor postProcessor) {
return this;
@@ -1214,6 +1218,11 @@ public class CalciteAssert {
return with(CalciteConnectionProperty.LEX, lex);
}
+ /** Sets the conformance property. */
+ public AssertThat with(SqlConformanceEnum conformance) {
+ return with(CalciteConnectionProperty.CONFORMANCE, conformance);
+ }
+
/** Sets the default schema to a given schema. */
public AssertThat withSchema(String name, Schema schema) {
return with(ConnectionFactories.add(name, schema));
diff --git a/testkit/src/main/java/org/apache/calcite/util/Smalls.java
b/testkit/src/main/java/org/apache/calcite/util/Smalls.java
index 53cdbb8a64..963b11590f 100644
--- a/testkit/src/main/java/org/apache/calcite/util/Smalls.java
+++ b/testkit/src/main/java/org/apache/calcite/util/Smalls.java
@@ -683,6 +683,15 @@ public class Smalls {
}
}
+ /** User-defined function with niladic parentheses. */
+ public static class MyNiladicParenthesesFunction {
+ // This is a constant function that tests for niladic parentheses,
+ // and it returns the constant value foo
+ public String eval() {
+ return "foo";
+ }
+ }
+
/** Example of a UDF that has overloaded UDFs (same name, different args). */
public abstract static class CountArgs0Function {
private CountArgs0Function() {}