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() {}

Reply via email to