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 f438bf62e0 [CALCITE-6984] FamilyOperandTypeChecker with a Predicate 
describing optional arguments does not reject mistyped expressions
f438bf62e0 is described below

commit f438bf62e075b830fa1bd1f1aa28358123ffbe4a
Author: Mihai Budiu <[email protected]>
AuthorDate: Thu Apr 24 14:36:23 2025 -0700

    [CALCITE-6984] FamilyOperandTypeChecker with a Predicate describing 
optional arguments does not reject mistyped expressions
    
    Signed-off-by: Mihai Budiu <[email protected]>
---
 .../calcite/sql/fun/SqlJsonQueryFunction.java      | 19 +++--
 .../org/apache/calcite/sql/type/OperandTypes.java  | 92 ++++++++++------------
 .../org/apache/calcite/test/SqlOperatorTest.java   |  5 ++
 3 files changed, 62 insertions(+), 54 deletions(-)

diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonQueryFunction.java 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonQueryFunction.java
index fcb0e69a3a..ba3d0c31bf 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonQueryFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonQueryFunction.java
@@ -30,8 +30,8 @@
 import org.apache.calcite.sql.SqlOperatorBinding;
 import org.apache.calcite.sql.SqlWriter;
 import org.apache.calcite.sql.parser.SqlParserPos;
-import org.apache.calcite.sql.type.OperandTypes;
 import org.apache.calcite.sql.type.ReturnTypes;
+import org.apache.calcite.sql.type.SqlSingleOperandTypeChecker;
 import org.apache.calcite.sql.type.SqlTypeFamily;
 import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.calcite.sql.type.SqlTypeTransforms;
@@ -45,12 +45,24 @@
 import java.util.List;
 import java.util.Optional;
 
+import static org.apache.calcite.sql.type.OperandTypes.family;
+
 import static java.util.Objects.requireNonNull;
 
 /**
  * The <code>JSON_QUERY</code> function.
  */
 public class SqlJsonQueryFunction extends SqlFunction {
+  static final SqlSingleOperandTypeChecker TYPE_CHECKER =
+      family(
+          ImmutableList.of(
+              SqlTypeFamily.ANY, SqlTypeFamily.CHARACTER,
+              SqlTypeFamily.ANY, SqlTypeFamily.ANY, SqlTypeFamily.ANY))
+          .or(
+              family(
+                  ImmutableList.of(SqlTypeFamily.ANY, SqlTypeFamily.CHARACTER,
+                      SqlTypeFamily.ANY, SqlTypeFamily.ANY, SqlTypeFamily.ANY, 
SqlTypeFamily.ANY)));
+
   public SqlJsonQueryFunction() {
     super("JSON_QUERY", SqlKind.OTHER_FUNCTION,
         ReturnTypes.cascade(
@@ -60,10 +72,7 @@ public SqlJsonQueryFunction() {
                     .orElseGet(() -> getDefaultType(opBinding)),
             SqlTypeTransforms.FORCE_NULLABLE),
         null,
-        OperandTypes.family(
-            ImmutableList.of(SqlTypeFamily.ANY, SqlTypeFamily.CHARACTER,
-            SqlTypeFamily.ANY, SqlTypeFamily.ANY, SqlTypeFamily.ANY, 
SqlTypeFamily.ANY),
-            i -> i >= 5),
+        TYPE_CHECKER,
         SqlFunctionCategory.SYSTEM);
   }
 
diff --git a/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java 
b/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java
index 6e129f5888..be0e53eb94 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java
@@ -97,6 +97,10 @@ public static FamilyOperandTypeChecker 
family(SqlTypeFamily... families) {
   /**
    * Creates a checker that passes if each operand is a member of a
    * corresponding family, and allows specified parameters to be optional.
+   *
+   * <P>WARNING: This operand checker does not work correctly when optional 
parameters are
+   * specified: see <a 
href="https://issues.apache.org/jira/browse/CALCITE-6984";>[CALCITE-6984]</a>
+   * and <a 
href="https://issues.apache.org/jira/browse/CALCITE-6976";>[CALCITE-6976]</a>.
    */
   public static FamilyOperandTypeChecker family(List<SqlTypeFamily> families,
       Predicate<Integer> optional) {
@@ -404,15 +408,17 @@ public static SqlOperandTypeChecker variadic(
   public static final SqlSingleOperandTypeChecker VARIANT =
       family(SqlTypeFamily.VARIANT);
 
+  public static final SqlSingleOperandTypeChecker NUMERIC_NUMERIC =
+      family(SqlTypeFamily.NUMERIC, SqlTypeFamily.NUMERIC);
+
   public static final SqlSingleOperandTypeChecker NUMERIC_OPTIONAL_NUMERIC =
-      family(ImmutableList.of(SqlTypeFamily.NUMERIC, SqlTypeFamily.NUMERIC),
-          // Second operand optional (operand index 0, 1)
-          number -> number == 1);
+      NUMERIC.or(NUMERIC_NUMERIC);
+
+  public static final SqlSingleOperandTypeChecker NUMERIC_INTEGER =
+      family(SqlTypeFamily.NUMERIC, SqlTypeFamily.INTEGER);
 
   public static final SqlSingleOperandTypeChecker NUMERIC_OPTIONAL_INTEGER =
-      family(ImmutableList.of(SqlTypeFamily.NUMERIC, SqlTypeFamily.INTEGER),
-          // Second operand optional (operand index 0, 1)
-          number -> number == 1);
+      NUMERIC.or(NUMERIC_INTEGER);
 
   public static final SqlOperandTypeChecker NUMERIC_INT32 =
       sequence(
@@ -424,12 +430,6 @@ public static SqlOperandTypeChecker variadic(
   public static final SqlSingleOperandTypeChecker NUMERIC_CHARACTER =
       family(SqlTypeFamily.NUMERIC, SqlTypeFamily.CHARACTER);
 
-  public static final SqlSingleOperandTypeChecker NUMERIC_INTEGER =
-      family(SqlTypeFamily.NUMERIC, SqlTypeFamily.INTEGER);
-
-  public static final SqlSingleOperandTypeChecker NUMERIC_NUMERIC =
-      family(SqlTypeFamily.NUMERIC, SqlTypeFamily.NUMERIC);
-
   public static final SqlSingleOperandTypeChecker EXACT_NUMERIC =
       family(SqlTypeFamily.EXACT_NUMERIC);
 
@@ -448,39 +448,33 @@ public static SqlOperandTypeChecker variadic(
   public static final FamilyOperandTypeChecker STRING_STRING =
       family(SqlTypeFamily.STRING, SqlTypeFamily.STRING);
 
-  public static final FamilyOperandTypeChecker STRING_OPTIONAL_STRING =
-      family(
-          ImmutableList.of(SqlTypeFamily.STRING, SqlTypeFamily.STRING),
-          // Second operand optional (operand index 0, 1)
-          number -> number == 1);
+  public static final SqlSingleOperandTypeChecker STRING_OPTIONAL_STRING =
+      STRING.or(STRING_STRING);
 
   public static final FamilyOperandTypeChecker STRING_STRING_STRING =
       family(SqlTypeFamily.STRING, SqlTypeFamily.STRING, SqlTypeFamily.STRING);
 
-  public static final FamilyOperandTypeChecker STRING_STRING_OPTIONAL_STRING =
-      family(
-          ImmutableList.of(SqlTypeFamily.STRING, SqlTypeFamily.STRING,
-              SqlTypeFamily.STRING),
-          // Third operand optional (operand index 0, 1, 2)
-          number -> number == 2);
+  public static final SqlSingleOperandTypeChecker 
STRING_STRING_OPTIONAL_STRING =
+      STRING_STRING.or(STRING_STRING_STRING);
 
-  public static final FamilyOperandTypeChecker 
STRING_OPTIONAL_STRING_OPTIONAL_STRING =
-      family(
-          ImmutableList.of(SqlTypeFamily.STRING, SqlTypeFamily.STRING,
-              SqlTypeFamily.STRING),
-          // Second and Third operand both are optional (operand index 0, 1, 2)
-          number -> number == 1 || number == 2);
+  public static final SqlSingleOperandTypeChecker 
STRING_OPTIONAL_STRING_OPTIONAL_STRING =
+      // operands 1 and 2 are optional
+      STRING
+          .or(STRING_STRING)
+          .or(STRING_STRING_STRING);
 
   public static final FamilyOperandTypeChecker STRING_STRING_STRING_STRING =
       family(SqlTypeFamily.STRING, SqlTypeFamily.STRING, SqlTypeFamily.STRING,
           SqlTypeFamily.STRING);
 
-  public static final FamilyOperandTypeChecker STRING_NUMERIC_OPTIONAL_STRING =
-      family(
-          ImmutableList.of(SqlTypeFamily.STRING, SqlTypeFamily.NUMERIC,
-              SqlTypeFamily.STRING),
-          // Third operand optional (operand index 0, 1, 2)
-          number -> number == 2);
+  public static final SqlSingleOperandTypeChecker STRING_NUMERIC =
+      family(SqlTypeFamily.STRING, SqlTypeFamily.NUMERIC);
+
+  static final SqlSingleOperandTypeChecker STRING_NUMERIC_STRING =
+      family(SqlTypeFamily.STRING, SqlTypeFamily.NUMERIC, 
SqlTypeFamily.STRING);
+
+  public static final SqlSingleOperandTypeChecker 
STRING_NUMERIC_OPTIONAL_STRING =
+      STRING_NUMERIC.or(STRING_NUMERIC_STRING);
 
   public static final SqlSingleOperandTypeChecker CHARACTER =
       family(SqlTypeFamily.CHARACTER);
@@ -1059,15 +1053,21 @@ public static SqlSingleOperandTypeChecker same(int 
operandCount,
           SqlTypeFamily.INTEGER, SqlTypeFamily.INTEGER);
 
   public static final SqlSingleOperandTypeChecker 
STRING_STRING_OPTIONAL_INTEGER_OPTIONAL_INTEGER =
+      STRING_STRING
+          .or(STRING_STRING_INTEGER)
+          .or(STRING_STRING_INTEGER_INTEGER);
+
+  public static final SqlSingleOperandTypeChecker 
STRING_STRING_INTEGER_INTEGER_INTEGER =
       family(
-          ImmutableList.of(SqlTypeFamily.STRING, SqlTypeFamily.STRING, 
SqlTypeFamily.INTEGER,
-              SqlTypeFamily.INTEGER), i -> i == 2 || i == 3);
+          ImmutableList.of(SqlTypeFamily.STRING, SqlTypeFamily.STRING,
+              SqlTypeFamily.INTEGER, SqlTypeFamily.INTEGER, 
SqlTypeFamily.INTEGER));
 
   public static final SqlSingleOperandTypeChecker
       STRING_STRING_OPTIONAL_INTEGER_OPTIONAL_INTEGER_OPTIONAL_INTEGER =
-      family(
-          ImmutableList.of(SqlTypeFamily.STRING, SqlTypeFamily.STRING, 
SqlTypeFamily.INTEGER,
-              SqlTypeFamily.INTEGER, SqlTypeFamily.INTEGER), i -> i == 2 || i 
== 3 || i == 4);
+      STRING_STRING
+          .or(STRING_STRING_INTEGER)
+          .or(STRING_STRING_INTEGER_INTEGER)
+          .or(STRING_STRING_INTEGER_INTEGER_INTEGER);
 
   public static final SqlSingleOperandTypeChecker STRING_INTEGER =
       family(SqlTypeFamily.STRING, SqlTypeFamily.INTEGER);
@@ -1077,12 +1077,7 @@ public static SqlSingleOperandTypeChecker same(int 
operandCount,
           SqlTypeFamily.INTEGER);
 
   public static final SqlSingleOperandTypeChecker 
STRING_INTEGER_OPTIONAL_INTEGER =
-      family(
-          ImmutableList.of(SqlTypeFamily.STRING, SqlTypeFamily.INTEGER,
-              SqlTypeFamily.INTEGER), i -> i == 2);
-
-  public static final SqlSingleOperandTypeChecker STRING_NUMERIC =
-      family(SqlTypeFamily.STRING, SqlTypeFamily.NUMERIC);
+      STRING_INTEGER.or(STRING_INTEGER_INTEGER);
 
   public static final SqlSingleOperandTypeChecker STRING_NUMERIC_NUMERIC =
       family(SqlTypeFamily.STRING, SqlTypeFamily.NUMERIC, 
SqlTypeFamily.NUMERIC);
@@ -1134,9 +1129,8 @@ public static SqlSingleOperandTypeChecker same(int 
operandCount,
   public static final SqlSingleOperandTypeChecker ANY_STRING_STRING =
       family(SqlTypeFamily.ANY, SqlTypeFamily.STRING, SqlTypeFamily.STRING);
   public static final SqlSingleOperandTypeChecker ANY_STRING_OPTIONAL_STRING =
-      family(ImmutableList.of(SqlTypeFamily.ANY, SqlTypeFamily.STRING, 
SqlTypeFamily.STRING),
-          // Third operand optional (operand index 0, 1, 2)
-          number -> number == 2);
+      family(ImmutableList.of(SqlTypeFamily.ANY, SqlTypeFamily.STRING))
+          .or(ANY_STRING_STRING);
 
   /**
    * Operand type-checking strategy used by {@code ARG_MIN(value, comp)} and
diff --git a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java 
b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
index d04a35e3ac..bcf270fd82 100644
--- a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
+++ b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
@@ -7721,6 +7721,11 @@ void checkRegexpExtract(SqlOperatorFixture f0, 
FunctionAlias functionAlias) {
     f0.checkFails("^log(100, 10)^",
         "No match found for function signature LOG\\(<NUMERIC>, <NUMERIC>\\)", 
false);
     final SqlOperatorFixture f = f0.withLibrary(SqlLibrary.BIG_QUERY);
+    // Test case for https://issues.apache.org/jira/browse/CALCITE-6984
+    // FamilyOperandTypeChecker with a Predicate describing optional arguments 
does not
+    // reject mistyped expressions
+    f.checkFails("^log(x'')^",
+        "Cannot apply 'LOG' to arguments of type 
'LOG\\(<BINARY\\(0\\)>\\)'.*\\n.*", false);
     f.checkScalarApprox("log(10, 10)", "DOUBLE NOT NULL",
         isWithin(1.0, 0.000001));
     f.checkScalarApprox("log(64, 8)", "DOUBLE NOT NULL",

Reply via email to