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",