This is an automated email from the ASF dual-hosted git repository.
yiguolei pushed a commit to branch branch-4.1
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-4.1 by this push:
new 6f069fc759e branch-4.1: [fix](function) Reject even argument count of
json_set/json_insert/json_replace at analysis time #67804 (#67914)
6f069fc759e is described below
commit 6f069fc759e99bc6cbc96699316613fffe2c4e00
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Mon Sep 14 09:43:14 2026 +0800
branch-4.1: [fix](function) Reject even argument count of
json_set/json_insert/json_replace at analysis time #67804 (#67914)
Cherry-picked from #67804
Co-authored-by: Jerry Hu <[email protected]>
---
.../expressions/functions/scalar/JsonInsert.java | 10 ++++++++++
.../expressions/functions/scalar/JsonReplace.java | 10 ++++++++++
.../expressions/functions/scalar/JsonSet.java | 10 ++++++++++
.../analysis/CheckExpressionLegalityTest.java | 18 ++++++++++++++++++
.../json_function/test_query_json_insert.out | 3 +++
.../json_function/test_query_json_replace.out | 3 +++
.../json_function/test_query_json_set.out | 3 +++
.../json_function/test_query_json_insert.groovy | 22 ++++++++++++++++++++++
.../json_function/test_query_json_replace.groovy | 22 ++++++++++++++++++++++
.../json_function/test_query_json_set.groovy | 22 ++++++++++++++++++++++
10 files changed, 123 insertions(+)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/JsonInsert.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/JsonInsert.java
index a99df63983c..06bedc8d522 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/JsonInsert.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/JsonInsert.java
@@ -18,6 +18,7 @@
package org.apache.doris.nereids.trees.expressions.functions.scalar;
import org.apache.doris.catalog.FunctionSignature;
+import org.apache.doris.nereids.exceptions.AnalysisException;
import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.functions.AlwaysNullable;
import org.apache.doris.nereids.trees.expressions.functions.CustomSignature;
@@ -64,6 +65,15 @@ public class JsonInsert extends ScalarFunction implements
CustomSignature, Alway
return FunctionSignature.of(JsonType.INSTANCE, arguments);
}
+ @Override
+ public void checkLegalityBeforeTypeCoercion() {
+ // arguments are a JSON document followed by (path, value) pairs, so
arity must be odd
+ if ((arity() & 1) == 0) {
+ throw new AnalysisException(getName() + " requires a JSON document
followed by path/value pairs,"
+ + " so the number of arguments must be odd, but got " +
arity() + ": " + this.toSql());
+ }
+ }
+
/**
* withChildren.
*/
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/JsonReplace.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/JsonReplace.java
index a4d06ddbaff..c626d3ed5a8 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/JsonReplace.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/JsonReplace.java
@@ -18,6 +18,7 @@
package org.apache.doris.nereids.trees.expressions.functions.scalar;
import org.apache.doris.catalog.FunctionSignature;
+import org.apache.doris.nereids.exceptions.AnalysisException;
import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.functions.AlwaysNullable;
import org.apache.doris.nereids.trees.expressions.functions.CustomSignature;
@@ -64,6 +65,15 @@ public class JsonReplace extends ScalarFunction implements
CustomSignature, Alwa
return FunctionSignature.of(JsonType.INSTANCE, arguments);
}
+ @Override
+ public void checkLegalityBeforeTypeCoercion() {
+ // arguments are a JSON document followed by (path, value) pairs, so
arity must be odd
+ if ((arity() & 1) == 0) {
+ throw new AnalysisException(getName() + " requires a JSON document
followed by path/value pairs,"
+ + " so the number of arguments must be odd, but got " +
arity() + ": " + this.toSql());
+ }
+ }
+
/**
* withChildren.
*/
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/JsonSet.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/JsonSet.java
index cdc9ed82b4f..0f017d3d0d0 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/JsonSet.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/JsonSet.java
@@ -18,6 +18,7 @@
package org.apache.doris.nereids.trees.expressions.functions.scalar;
import org.apache.doris.catalog.FunctionSignature;
+import org.apache.doris.nereids.exceptions.AnalysisException;
import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.functions.AlwaysNullable;
import org.apache.doris.nereids.trees.expressions.functions.CustomSignature;
@@ -64,6 +65,15 @@ public class JsonSet extends ScalarFunction implements
CustomSignature, AlwaysNu
return FunctionSignature.of(JsonType.INSTANCE, arguments);
}
+ @Override
+ public void checkLegalityBeforeTypeCoercion() {
+ // arguments are a JSON document followed by (path, value) pairs, so
arity must be odd
+ if ((arity() & 1) == 0) {
+ throw new AnalysisException(getName() + " requires a JSON document
followed by path/value pairs,"
+ + " so the number of arguments must be odd, but got " +
arity() + ": " + this.toSql());
+ }
+ }
+
/**
* withChildren.
*/
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/CheckExpressionLegalityTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/CheckExpressionLegalityTest.java
index 447156afb38..21c11e6797e 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/CheckExpressionLegalityTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/CheckExpressionLegalityTest.java
@@ -70,6 +70,24 @@ public class CheckExpressionLegalityTest implements
MemoPatternMatchSupported {
});
}
+ @Test
+ public void testJsonModifyFunctionsRejectEvenArity() {
+ ConnectContext connectContext = MemoTestUtils.createConnectContext();
+ for (String function : new String[] {"json_set", "jsonb_set",
"json_insert", "jsonb_insert",
+ "json_replace", "jsonb_replace"}) {
+ ExceptionChecker.expectThrowsWithMsg(AnalysisException.class,
+ "number of arguments must be odd, but got 4", () ->
+ PlanChecker.from(connectContext)
+ .analyze("select " + function + "('{}',
'$.a', 1, '$.b')"));
+ ExceptionChecker.expectThrowsWithMsg(AnalysisException.class,
+ "number of arguments must be odd, but got 6", () ->
+ PlanChecker.from(connectContext)
+ .analyze("select " + function + "('{}',
'$.a', 1, '$.b', 2, '$.c')"));
+ PlanChecker.from(connectContext).analyze("select " + function +
"('{}', '$.a', 1)");
+ PlanChecker.from(connectContext).analyze("select " + function +
"('{}', '$.a', 1, '$.b', 2)");
+ }
+ }
+
@Test
public void testCountDistinctBitmap() {
ConnectContext connectContext = MemoTestUtils.createConnectContext();
diff --git
a/regression-test/data/query_p0/sql_functions/json_function/test_query_json_insert.out
b/regression-test/data/query_p0/sql_functions/json_function/test_query_json_insert.out
index 03210f0f6a2..72c47e601ee 100644
---
a/regression-test/data/query_p0/sql_functions/json_function/test_query_json_insert.out
+++
b/regression-test/data/query_p0/sql_functions/json_function/test_query_json_insert.out
@@ -90,3 +90,6 @@
-- !insert7 --
{"a":200}
+-- !insert_odd_arity_ok --
+{"a":1} {"a":1,"b":2} {"a":1,"b":2}
+
diff --git
a/regression-test/data/query_p0/sql_functions/json_function/test_query_json_replace.out
b/regression-test/data/query_p0/sql_functions/json_function/test_query_json_replace.out
index ac8cfb45010..3a67a270a04 100644
---
a/regression-test/data/query_p0/sql_functions/json_function/test_query_json_replace.out
+++
b/regression-test/data/query_p0/sql_functions/json_function/test_query_json_replace.out
@@ -80,3 +80,6 @@ null
-- !replace8 --
{"a":100}
+-- !replace_odd_arity_ok --
+{} {} {}
+
diff --git
a/regression-test/data/query_p0/sql_functions/json_function/test_query_json_set.out
b/regression-test/data/query_p0/sql_functions/json_function/test_query_json_set.out
index b57a50e314b..e198f86db0b 100644
---
a/regression-test/data/query_p0/sql_functions/json_function/test_query_json_set.out
+++
b/regression-test/data/query_p0/sql_functions/json_function/test_query_json_set.out
@@ -85,3 +85,6 @@ null
-- !set8 --
{"a":100}
+-- !set_odd_arity_ok --
+{"a":1} {"a":1,"b":2} {"a":1,"b":2}
+
diff --git
a/regression-test/suites/query_p0/sql_functions/json_function/test_query_json_insert.groovy
b/regression-test/suites/query_p0/sql_functions/json_function/test_query_json_insert.groovy
index 95c0cc3ab64..59bd3159a35 100644
---
a/regression-test/suites/query_p0/sql_functions/json_function/test_query_json_insert.groovy
+++
b/regression-test/suites/query_p0/sql_functions/json_function/test_query_json_insert.groovy
@@ -127,4 +127,26 @@ suite("test_query_json_insert", "query,arrow_flight_sql") {
sql "select json_insert('1', '\$.', 4);"
exception "Json path error: Invalid Json Path for value"
}
+
+ // arguments must be a JSON document followed by path/value pairs, so an
even count is rejected by FE
+ test {
+ sql "select json_insert('{}', '\$.a', 1, '\$.b');"
+ exception "number of arguments must be odd, but got 4"
+ }
+
+ test {
+ sql "select json_insert('{}', '\$.a', 1, '\$.b', 2, '\$.c');"
+ exception "number of arguments must be odd, but got 6"
+ }
+
+ test {
+ sql "select jsonb_insert('{}', '\$.a', 1, '\$.b');"
+ exception "number of arguments must be odd, but got 4"
+ }
+
+ test {
+ sql "explain select json_insert('{}', '\$.a', 1, '\$.b');"
+ exception "number of arguments must be odd, but got 4"
+ }
+ qt_insert_odd_arity_ok """select json_insert('{}', '\$.a', 1),
json_insert('{}', '\$.a', 1, '\$.b', 2), jsonb_insert('{}', '\$.a', 1, '\$.b',
2);"""
}
diff --git
a/regression-test/suites/query_p0/sql_functions/json_function/test_query_json_replace.groovy
b/regression-test/suites/query_p0/sql_functions/json_function/test_query_json_replace.groovy
index 69f7142eff7..0b91ca39dbb 100644
---
a/regression-test/suites/query_p0/sql_functions/json_function/test_query_json_replace.groovy
+++
b/regression-test/suites/query_p0/sql_functions/json_function/test_query_json_replace.groovy
@@ -133,4 +133,26 @@ suite("test_query_json_replace", "query") {
sql "select json_replace('1', '\$.', 4);"
exception "Json path error: Invalid Json Path for value"
}
+
+ // arguments must be a JSON document followed by path/value pairs, so an
even count is rejected by FE
+ test {
+ sql "select json_replace('{}', '\$.a', 1, '\$.b');"
+ exception "number of arguments must be odd, but got 4"
+ }
+
+ test {
+ sql "select json_replace('{}', '\$.a', 1, '\$.b', 2, '\$.c');"
+ exception "number of arguments must be odd, but got 6"
+ }
+
+ test {
+ sql "select jsonb_replace('{}', '\$.a', 1, '\$.b');"
+ exception "number of arguments must be odd, but got 4"
+ }
+
+ test {
+ sql "explain select json_replace('{}', '\$.a', 1, '\$.b');"
+ exception "number of arguments must be odd, but got 4"
+ }
+ qt_replace_odd_arity_ok """select json_replace('{}', '\$.a', 1),
json_replace('{}', '\$.a', 1, '\$.b', 2), jsonb_replace('{}', '\$.a', 1,
'\$.b', 2);"""
}
diff --git
a/regression-test/suites/query_p0/sql_functions/json_function/test_query_json_set.groovy
b/regression-test/suites/query_p0/sql_functions/json_function/test_query_json_set.groovy
index 705e6046705..91174fa8827 100644
---
a/regression-test/suites/query_p0/sql_functions/json_function/test_query_json_set.groovy
+++
b/regression-test/suites/query_p0/sql_functions/json_function/test_query_json_set.groovy
@@ -120,4 +120,26 @@ suite("test_query_json_set", "query") {
sql "select json_set('1', '\$.', 4);"
exception "Json path error: Invalid Json Path for value"
}
+
+ // arguments must be a JSON document followed by path/value pairs, so an
even count is rejected by FE
+ test {
+ sql "select json_set('{}', '\$.a', 1, '\$.b');"
+ exception "number of arguments must be odd, but got 4"
+ }
+
+ test {
+ sql "select json_set('{}', '\$.a', 1, '\$.b', 2, '\$.c');"
+ exception "number of arguments must be odd, but got 6"
+ }
+
+ test {
+ sql "select jsonb_set('{}', '\$.a', 1, '\$.b');"
+ exception "number of arguments must be odd, but got 4"
+ }
+
+ test {
+ sql "explain select json_set('{}', '\$.a', 1, '\$.b');"
+ exception "number of arguments must be odd, but got 4"
+ }
+ qt_set_odd_arity_ok """select json_set('{}', '\$.a', 1), json_set('{}',
'\$.a', 1, '\$.b', 2), jsonb_set('{}', '\$.a', 1, '\$.b', 2);"""
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]