This is an automated email from the ASF dual-hosted git repository.

raminqaf pushed a commit to branch release-2.4
in repository https://gitbox.apache.org/repos/asf/flink.git


The following commit(s) were added to refs/heads/release-2.4 by this push:
     new 4ca6fb1c99c [BP-2.4][FLINK-41086][table] Return NULL from 
`JSON_LENGTH` and `JSON_TYPE` when a JSON path function fails (#29805)
4ca6fb1c99c is described below

commit 4ca6fb1c99c171909352a0db08237bdd8dbdffd1
Author: Ramin Gharib <[email protected]>
AuthorDate: Fri Nov 6 15:23:20 2026 +0100

    [BP-2.4][FLINK-41086][table] Return NULL from `JSON_LENGTH` and `JSON_TYPE` 
when a JSON path function fails (#29805)
    
    JSON_LENGTH(json, path) and JSON_TYPE(json, path) only caught
    InvalidPathException around JsonPath#read. Path functions such as
    min()/avg()/sum() throw JsonPathException on values they cannot handle,
    e.g. an empty or non-numeric array. It escaped and failed the job
    instead of returning NULL.
    
    Catch JsonPathException, the parent of InvalidPathException and
    PathNotFoundException. Other exceptions still surface.
    
    Generated-by: Claude Opus 5.5
    (cherry picked from commit 44131db0b9c9e01dc55ec7d2dae02a4c5fbe0e7e)
    
    Co-authored-by: Yahya Kisana <[email protected]>
---
 docs/data/sql_functions.yml                        |  6 ++--
 docs/data/sql_functions_zh.yml                     |  6 ++--
 .../planner/functions/JsonFunctionsITCase.java     | 32 ++++++++++++++++++++++
 .../table/runtime/functions/SqlJsonUtils.java      | 13 +++++----
 4 files changed, 48 insertions(+), 9 deletions(-)

diff --git a/docs/data/sql_functions.yml b/docs/data/sql_functions.yml
index c9aa68a6faa..696f6a09fe2 100644
--- a/docs/data/sql_functions.yml
+++ b/docs/data/sql_functions.yml
@@ -1294,9 +1294,10 @@ json:
     description: |
       Returns the number of elements in a JSON document, or the length of the 
value at the specified path if one is provided.
       
-      The input can be a JSON STRING or a VARIANT. Returns NULL if the 
argument is NULL, the json is invalid, or the path is empty, malformed or does 
not locate a value.
+      The input can be a JSON STRING or a VARIANT. Returns NULL if the 
argument is NULL, the json is invalid, a path function fails (e.g., .min(), 
.avg()), or the path is empty, malformed or does not locate a value.
       The path must be a plain path literal such as '$.a.b'. A path carrying a 
'lax'/'strict' path mode prefix raises an error.
       eg.
+      ```sql
         -- 2
         JSON_LENGTH('{"1": "hello", "2": "bye bye"}')
       
@@ -1317,6 +1318,7 @@ json:
 
         -- error: JSON_LENGTH does not support the 'lax'/'strict' path mode 
prefix
         JSON_LENGTH('{"1": "hello", "2": "bye bye"}', 'strict $.1')
+      ```
 
       The length is determined as follows:
       
@@ -1337,7 +1339,7 @@ json:
     table: jsonType(jsonValue[, path])
     description: |
       Returns a string value indicating the type of jsonValue, optionally at 
the given JSON
-      `path`. Returns SQL `NULL` if jsonValue is SQL `NULL`, is not valid 
JSON, or the path
+      `path`. Returns SQL `NULL` if jsonValue is SQL `NULL`, is not valid 
JSON, a path function fails (e.g., .min(), .avg()), or the path
       does not resolve to exactly one value.
 
       The possible results are `object`, `array`, `string`, `number`, 
`boolean` and `null` for
diff --git a/docs/data/sql_functions_zh.yml b/docs/data/sql_functions_zh.yml
index 3d9de3802dd..9a90f434692 100644
--- a/docs/data/sql_functions_zh.yml
+++ b/docs/data/sql_functions_zh.yml
@@ -1387,9 +1387,10 @@ json:
     description: |
       Returns the number of elements in a JSON document, or the length of the 
value at the specified path if one is provided.
       
-      The input can be a JSON STRING or a VARIANT. Returns NULL if the 
argument is NULL, the json is invalid, or the path is empty, malformed or does 
not locate a value.
+      The input can be a JSON STRING or a VARIANT. Returns NULL if the 
argument is NULL, the json is invalid, a path function fails (e.g., .min(), 
.avg()), or the path is empty, malformed or does not locate a value.
       The path must be a plain path literal such as '$.a.b'. A path carrying a 
'lax'/'strict' path mode prefix raises an error.
       eg.
+      ```sql
         -- 2
         JSON_LENGTH('{"1": "hello", "2": "bye bye"}')
       
@@ -1410,6 +1411,7 @@ json:
 
         -- error: JSON_LENGTH does not support the 'lax'/'strict' path mode 
prefix
         JSON_LENGTH('{"1": "hello", "2": "bye bye"}', 'strict $.1')
+      ```
 
       The length is determined as follows:
       
@@ -1430,7 +1432,7 @@ json:
     table: jsonType(jsonValue[, path])
     description: |
       Returns a string value indicating the type of jsonValue, optionally at 
the given JSON
-      `path`. Returns SQL `NULL` if jsonValue is SQL `NULL`, is not valid 
JSON, or the path
+      `path`. Returns SQL `NULL` if jsonValue is SQL `NULL`, is not valid 
JSON, a path function fails (e.g., .min(), .avg()), or the path
       does not resolve to exactly one value.
 
       The possible results are `object`, `array`, `string`, `number`, 
`boolean` and `null` for
diff --git 
a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/JsonFunctionsITCase.java
 
b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/JsonFunctionsITCase.java
index aaeb24f6a0a..46d12c842b4 100644
--- 
a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/JsonFunctionsITCase.java
+++ 
b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/JsonFunctionsITCase.java
@@ -170,6 +170,23 @@ class JsonFunctionsITCase extends BuiltInFunctionTestBase {
                         $("f8").jsonLength("   "), "JSON_LENGTH(f8, '   ')", 
null, INT().nullable())
                 .testResult($("f8").jsonLength(""), "JSON_LENGTH(f8, '')", 
null, INT().nullable())
 
+                // a path function that fails on the value it reaches -> NULL
+                .testResult(
+                        $("f10").jsonLength("$.min()"),
+                        "JSON_LENGTH(f10, '$.min()')",
+                        null,
+                        INT().nullable())
+                .testResult(
+                        $("f8").jsonLength("$.a.avg()"),
+                        "JSON_LENGTH(f8, '$.a.avg()')",
+                        null,
+                        INT().nullable())
+                .testResult(
+                        $("f8").jsonLength("$.a[*].min()"),
+                        "JSON_LENGTH(f8, '$.a[*].min()')",
+                        null,
+                        INT().nullable())
+
                 // the root path on a scalar document behaves like the no-path 
overload
                 .testResult($("f3").jsonLength("$"), "JSON_LENGTH(f3, '$')", 
1, INT().nullable())
 
@@ -2024,6 +2041,21 @@ class JsonFunctionsITCase extends 
BuiltInFunctionTestBase {
                                 "number",
                                 STRING().nullable()),
 
+                // A path function that fails on the value it reaches yields 
NULL.
+                TestSetSpec.forFunction(BuiltInFunctionDefinitions.JSON_TYPE)
+                        .onFieldsWithData("{\"a\": []}", "{\"a\": [1, 2]}")
+                        .andDataTypes(STRING(), STRING())
+                        .testResult(
+                                $("f0").jsonType("$.a.avg()"),
+                                "JSON_TYPE(f0, '$.a.avg()')",
+                                null,
+                                STRING().nullable())
+                        .testResult(
+                                $("f1").jsonType("$.a[*].min()"),
+                                "JSON_TYPE(f1, '$.a[*].min()')",
+                                null,
+                                STRING().nullable()),
+
                 // The 'lax'/'strict' path mode prefix is rejected at planning 
time, but a field of
                 // that name is addressed like any other.
                 TestSetSpec.forFunction(BuiltInFunctionDefinitions.JSON_TYPE)
diff --git 
a/flink-table/flink-table-runtime/src/main/java/org/apache/flink/table/runtime/functions/SqlJsonUtils.java
 
b/flink-table/flink-table-runtime/src/main/java/org/apache/flink/table/runtime/functions/SqlJsonUtils.java
index 431d0248cf6..24c6a0d3558 100644
--- 
a/flink-table/flink-table-runtime/src/main/java/org/apache/flink/table/runtime/functions/SqlJsonUtils.java
+++ 
b/flink-table/flink-table-runtime/src/main/java/org/apache/flink/table/runtime/functions/SqlJsonUtils.java
@@ -39,6 +39,7 @@ import 
org.apache.flink.shaded.com.jayway.jsonpath.Configuration;
 import org.apache.flink.shaded.com.jayway.jsonpath.DocumentContext;
 import org.apache.flink.shaded.com.jayway.jsonpath.InvalidPathException;
 import org.apache.flink.shaded.com.jayway.jsonpath.JsonPath;
+import org.apache.flink.shaded.com.jayway.jsonpath.JsonPathException;
 import org.apache.flink.shaded.com.jayway.jsonpath.Option;
 import org.apache.flink.shaded.com.jayway.jsonpath.PathNotFoundException;
 import org.apache.flink.shaded.com.jayway.jsonpath.spi.cache.CacheProvider;
@@ -437,7 +438,7 @@ public class SqlJsonUtils {
             final String pathSpec,
             final boolean isPathDefinite) {
         // An empty path is ruled out up front because JsonPath rejects it 
with an
-        // IllegalArgumentException instead of the InvalidPathException caught 
below.
+        // IllegalArgumentException instead of the JsonPathException caught 
below.
         if (parsedInput == null || parsedInput.hasException() || 
pathSpec.isEmpty()) {
             return null;
         }
@@ -450,8 +451,9 @@ public class SqlJsonUtils {
         final Object value;
         try {
             value = JsonPath.parse(parsedInput.obj, 
JSON_PATH_LENGTH_CONFIG).read(pathSpec);
-        } catch (InvalidPathException e) {
-            // The path does not exist, or is not a valid path at all.
+        } catch (JsonPathException e) {
+            // The path does not exist, is not a valid path at all, or applies 
a path function
+            // that fails on the value it reaches, e.g. min() on an empty 
array.
             return null;
         }
 
@@ -596,9 +598,10 @@ public class SqlJsonUtils {
 
         final Object value;
         try {
-            // PathNotFoundException extends InvalidPathException (covers both 
exceptions)
             value = JsonPath.parse(parsedInput.obj, 
JSON_PATH_TYPE_CONFIG).read(path);
-        } catch (InvalidPathException e) {
+        } catch (JsonPathException e) {
+            // The path does not exist, is not a valid path at all, or applies 
a path function
+            // that fails on the value it reaches, e.g. min() on an empty 
array.
             return null;
         }
 

Reply via email to