This is an automated email from the ASF dual-hosted git repository.
xiong 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 3ef0999d39 [CALCITE-6478] JSON functions should return NULL when input
is NULL
3ef0999d39 is described below
commit 3ef0999d392cef9484d17d761cd2dec56d7ff122
Author: Xiong Duan <[email protected]>
AuthorDate: Tue Jul 30 18:38:00 2024 +0800
[CALCITE-6478] JSON functions should return NULL when input is NULL
---
.../calcite/adapter/enumerable/RexImpTable.java | 42 +++++++++++++++++++---
.../org/apache/calcite/runtime/JsonFunctions.java | 20 ++++++++---
.../org/apache/calcite/runtime/SqlFunctions.java | 2 +-
.../calcite/sql/fun/SqlStdOperatorTable.java | 16 ++++-----
.../apache/calcite/test/SqlJsonFunctionsTest.java | 4 +++
site/_docs/reference.md | 4 +++
.../org/apache/calcite/test/SqlOperatorTest.java | 8 +++++
7 files changed, 79 insertions(+), 17 deletions(-)
diff --git
a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
index 55ef175c67..d3f49d4349 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
@@ -1042,19 +1042,19 @@ public class RexImpTable {
new MethodImplementor(BuiltInMethod.IS_JSON_SCALAR.method,
NullPolicy.NONE, false));
map.put(IS_NOT_JSON_VALUE,
- NotImplementor.of(
+ NotJsonImplementor.of(
new MethodImplementor(BuiltInMethod.IS_JSON_VALUE.method,
NullPolicy.NONE, false)));
map.put(IS_NOT_JSON_OBJECT,
- NotImplementor.of(
+ NotJsonImplementor.of(
new MethodImplementor(BuiltInMethod.IS_JSON_OBJECT.method,
NullPolicy.NONE, false)));
map.put(IS_NOT_JSON_ARRAY,
- NotImplementor.of(
+ NotJsonImplementor.of(
new MethodImplementor(BuiltInMethod.IS_JSON_ARRAY.method,
NullPolicy.NONE, false)));
map.put(IS_NOT_JSON_SCALAR,
- NotImplementor.of(
+ NotJsonImplementor.of(
new MethodImplementor(BuiltInMethod.IS_JSON_SCALAR.method,
NullPolicy.NONE, false)));
@@ -3674,6 +3674,40 @@ public class RexImpTable {
}
}
+ /** Implementor for the {@code NOT JSON} operator. */
+ private static class NotJsonImplementor extends AbstractRexCallImplementor {
+ private final AbstractRexCallImplementor implementor;
+
+ private NotJsonImplementor(AbstractRexCallImplementor implementor) {
+ super("not_json", implementor.nullPolicy, false);
+ this.implementor = implementor;
+ }
+
+ static AbstractRexCallImplementor of(AbstractRexCallImplementor
implementor) {
+ return new NotJsonImplementor(implementor);
+ }
+
+ @Override Expression implementSafe(final RexToLixTranslator translator,
+ final RexCall call, final List<Expression> argValueList) {
+ // E.g., "final Boolean resultValue = (callValue == null) ? null :
!callValue"
+ final Expression expression =
+ implementor.implementSafe(translator, call, argValueList);
+ final ParameterExpression callValue =
+ Expressions.parameter(expression.getType());
+ translator.getBlockBuilder().add(
+ Expressions.declare(Modifier.FINAL, callValue, expression));
+ final Expression valueExpression =
+ Expressions.condition(
+ Expressions.equal(callValue, NULL_EXPR),
+ NULL_EXPR,
+ Expressions.not(callValue));
+ final ParameterExpression resultValue =
Expressions.parameter(expression.getType());
+ translator.getBlockBuilder().add(
+ Expressions.declare(Modifier.FINAL, resultValue, valueExpression));
+ return resultValue;
+ }
+ }
+
/** Implementor for various datetime arithmetic. */
private static class DatetimeArithmeticImplementor
extends AbstractRexCallImplementor {
diff --git a/core/src/main/java/org/apache/calcite/runtime/JsonFunctions.java
b/core/src/main/java/org/apache/calcite/runtime/JsonFunctions.java
index b77633924f..b613ad1d98 100644
--- a/core/src/main/java/org/apache/calcite/runtime/JsonFunctions.java
+++ b/core/src/main/java/org/apache/calcite/runtime/JsonFunctions.java
@@ -833,7 +833,10 @@ public class JsonFunctions {
}
}
- public static boolean isJsonValue(String input) {
+ public static @Nullable Boolean isJsonValue(@Nullable String input) {
+ if (input == null) {
+ return null;
+ }
try {
dejsonize(input);
return true;
@@ -842,7 +845,10 @@ public class JsonFunctions {
}
}
- public static boolean isJsonObject(String input) {
+ public static @Nullable Boolean isJsonObject(@Nullable String input) {
+ if (input == null) {
+ return null;
+ }
try {
Object o = dejsonize(input);
return o instanceof Map;
@@ -851,7 +857,10 @@ public class JsonFunctions {
}
}
- public static boolean isJsonArray(String input) {
+ public static @Nullable Boolean isJsonArray(@Nullable String input) {
+ if (input == null) {
+ return null;
+ }
try {
Object o = dejsonize(input);
return o instanceof Collection;
@@ -860,7 +869,10 @@ public class JsonFunctions {
}
}
- public static boolean isJsonScalar(String input) {
+ public static @Nullable Boolean isJsonScalar(@Nullable String input) {
+ if (input == null) {
+ return null;
+ }
try {
Object o = dejsonize(input);
return !(o instanceof Map) && !(o instanceof Collection);
diff --git a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
index ff51fc6b66..456977d1ad 100644
--- a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
+++ b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
@@ -996,7 +996,7 @@ public class SqlFunctions {
public static boolean containsSubstr(Object expr, String substr) {
expr = normalize(expr.toString());
substr = normalize(substr);
- if (JsonFunctions.isJsonObject(expr.toString())) {
+ if (Boolean.TRUE.equals(JsonFunctions.isJsonObject(expr.toString()))) {
return containsSubstr(expr.toString(), substr, "JSON_VALUES");
}
return ((String) expr).contains(substr);
diff --git
a/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
b/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
index dc2489664f..0254e2a219 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
@@ -810,7 +810,7 @@ public class SqlStdOperatorTable extends
ReflectiveSqlOperatorTable {
"IS JSON VALUE",
SqlKind.OTHER,
28,
- ReturnTypes.BOOLEAN,
+ ReturnTypes.BOOLEAN_NULLABLE,
null,
OperandTypes.CHARACTER);
@@ -819,7 +819,7 @@ public class SqlStdOperatorTable extends
ReflectiveSqlOperatorTable {
"IS NOT JSON VALUE",
SqlKind.OTHER,
28,
- ReturnTypes.BOOLEAN,
+ ReturnTypes.BOOLEAN_NULLABLE,
null,
OperandTypes.CHARACTER);
@@ -828,7 +828,7 @@ public class SqlStdOperatorTable extends
ReflectiveSqlOperatorTable {
"IS JSON OBJECT",
SqlKind.OTHER,
28,
- ReturnTypes.BOOLEAN,
+ ReturnTypes.BOOLEAN_NULLABLE,
null,
OperandTypes.CHARACTER);
@@ -837,7 +837,7 @@ public class SqlStdOperatorTable extends
ReflectiveSqlOperatorTable {
"IS NOT JSON OBJECT",
SqlKind.OTHER,
28,
- ReturnTypes.BOOLEAN,
+ ReturnTypes.BOOLEAN_NULLABLE,
null,
OperandTypes.CHARACTER);
@@ -846,7 +846,7 @@ public class SqlStdOperatorTable extends
ReflectiveSqlOperatorTable {
"IS JSON ARRAY",
SqlKind.OTHER,
28,
- ReturnTypes.BOOLEAN,
+ ReturnTypes.BOOLEAN_NULLABLE,
null,
OperandTypes.CHARACTER);
@@ -855,7 +855,7 @@ public class SqlStdOperatorTable extends
ReflectiveSqlOperatorTable {
"IS NOT JSON ARRAY",
SqlKind.OTHER,
28,
- ReturnTypes.BOOLEAN,
+ ReturnTypes.BOOLEAN_NULLABLE,
null,
OperandTypes.CHARACTER);
@@ -864,7 +864,7 @@ public class SqlStdOperatorTable extends
ReflectiveSqlOperatorTable {
"IS JSON SCALAR",
SqlKind.OTHER,
28,
- ReturnTypes.BOOLEAN,
+ ReturnTypes.BOOLEAN_NULLABLE,
null,
OperandTypes.CHARACTER);
@@ -873,7 +873,7 @@ public class SqlStdOperatorTable extends
ReflectiveSqlOperatorTable {
"IS NOT JSON SCALAR",
SqlKind.OTHER,
28,
- ReturnTypes.BOOLEAN,
+ ReturnTypes.BOOLEAN_NULLABLE,
null,
OperandTypes.CHARACTER);
diff --git
a/core/src/test/java/org/apache/calcite/test/SqlJsonFunctionsTest.java
b/core/src/test/java/org/apache/calcite/test/SqlJsonFunctionsTest.java
index 0879bdee0a..2786eabcdd 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlJsonFunctionsTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlJsonFunctionsTest.java
@@ -601,18 +601,22 @@ class SqlJsonFunctionsTest {
assertIsJsonValue("{}", is(true));
assertIsJsonValue("100", is(true));
assertIsJsonValue("{]", is(false));
+ assertIsJsonValue(null, nullValue());
assertIsJsonObject("[]", is(false));
assertIsJsonObject("{}", is(true));
assertIsJsonObject("100", is(false));
assertIsJsonObject("{]", is(false));
+ assertIsJsonObject(null, nullValue());
assertIsJsonArray("[]", is(true));
assertIsJsonArray("{}", is(false));
assertIsJsonArray("100", is(false));
assertIsJsonArray("{]", is(false));
+ assertIsJsonArray(null, nullValue());
assertIsJsonScalar("[]", is(false));
assertIsJsonScalar("{}", is(false));
assertIsJsonScalar("100", is(true));
assertIsJsonScalar("{]", is(false));
+ assertIsJsonScalar(null, nullValue());
}
@Test public void testJsonInsert() {
diff --git a/site/_docs/reference.md b/site/_docs/reference.md
index b3565bc1fa..fe8ff90365 100644
--- a/site/_docs/reference.md
+++ b/site/_docs/reference.md
@@ -2646,6 +2646,10 @@ Note:
| jsonValue IS JSON ARRAY | Whether *jsonValue* is a JSON array
| jsonValue IS NOT JSON ARRAY | Whether *jsonValue* is not a JSON array
+Note:
+
+* If the *jsonValue* is `NULL`, the function will return `NULL`.
+
### Dialect-specific Operators
The following operators are not in the SQL standard, and are not enabled in
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 12d3961289..d3679c0ab1 100644
--- a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
+++ b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
@@ -6732,6 +6732,14 @@ public class SqlOperatorTest {
f.checkBoolean("'[]' is not json array", false);
f.checkBoolean("'100' is not json scalar", false);
f.checkBoolean("'[]' is not json scalar", true);
+ f.checkNull("null is json value");
+ f.checkNull("null is json object");
+ f.checkNull("null is json array");
+ f.checkNull("null is json scalar");
+ f.checkNull("null is not json value");
+ f.checkNull("null is not json object");
+ f.checkNull("null is not json array");
+ f.checkNull("null is not json scalar");
}
@Test void testCompress() {