public inbox for [email protected]
help / color / mirror / Atom feedBUG: jsonpath .split_part() bypasses lax-mode error suppression
2+ messages / 2 participants
[nested] [flat]
* BUG: jsonpath .split_part() bypasses lax-mode error suppression
@ 2026-04-18 16:31 SATYANARAYANA NARLAPURAM <[email protected]>
2026-04-19 20:21 ` Re: BUG: jsonpath .split_part() bypasses lax-mode error suppression Dmitry Dolgov <[email protected]>
0 siblings, 1 reply; 2+ messages in thread
From: SATYANARAYANA NARLAPURAM @ 2026-04-18 16:31 UTC (permalink / raw)
To: PostgreSQL Hackers <[email protected]>; +Cc: [email protected]; [email protected]
Hi hackers,
The jsonpath string method .split_part() bypasses lax-mode error
suppression.
This is because executeStringInternalMethod() uses
DirectFunctionCall1(numeric_int4, ...)
to convert the field number from numeric to int4. This throws
ereport(ERROR) directly,
bypassing the jspThrowErrors(cxt) / RETURN_ERROR mechanism that other
methods
like .double() use correctly.
Reproduction:
-- These should return NULL in lax mode, but throw hard ERRORs:
SELECT '"hello-world"'::jsonb @? '$.split_part("-", 99999999999)';
SELECT '"hello-world"'::jsonb @? '$.split_part("-", 0)';
ERROR: integer out of range
ERROR: field position must not be zero
Fix by replacing the bare DirectFunctionCall with numeric_int4_safe()
using ErrorSaveContext to catch overflow, and adding an explicit field==0
check, both gated behind RETURN_ERROR so errors are properly suppressed
in lax/silent mode while still raised in strict/non-silent mode.
Thanks,
Satya
Attachments:
[application/octet-stream] 0001-Fix-jsonpath-.split_part-to-respect-error-suppressio.patch (6.2K, 3-0001-Fix-jsonpath-.split_part-to-respect-error-suppressio.patch)
download | inline diff:
From d1f001c81dd096cf2f994536579e995c4fceb3da Mon Sep 17 00:00:00 2001
From: satyanarayana narlapuram <[email protected]>
Date: Sat, 18 Apr 2026 15:42:18 +0000
Subject: [PATCH] Fix jsonpath .split_part() to respect error suppression in
lax mode
The .split_part() jsonpath string method, introduced in bd4f879a, used
bare DirectFunctionCall1(numeric_int4, ...) to convert the field number
argument from numeric to int4. This throws an uncatchable ERROR when the
field number overflows int32 range or is zero, bypassing the lax-mode
error suppression mechanism that other jsonpath methods use.
For example, these expressions should return NULL / false in lax mode,
but instead throw hard ERRORs:
'"hello"'::jsonb @? '$.split_part("-", 99999999999)'
-- ERROR: integer out of range (expected: NULL)
'"hello"'::jsonb @? '$.split_part("-", 0)'
-- ERROR: field position must not be zero (expected: NULL)
Fix by replacing the bare DirectFunctionCall with numeric_int4_safe()
using ErrorSaveContext to catch overflow, and adding an explicit field==0
check, both gated behind RETURN_ERROR so errors are properly suppressed
in lax/silent mode while still raised in strict/non-silent mode.
This is consistent with how .double() and .integer() handle conversion
errors using ErrorSaveContext and RETURN_ERROR.
---
src/backend/utils/adt/jsonpath_exec.c | 17 ++++++++-
src/test/regress/expected/jsonb_jsonpath.out | 40 ++++++++++++++++++++
src/test/regress/sql/jsonb_jsonpath.sql | 11 ++++++
3 files changed, 67 insertions(+), 1 deletion(-)
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index 0ec9b4df..42ed3aea 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -74,6 +74,7 @@
#include "utils/json.h"
#include "utils/jsonpath.h"
#include "utils/memutils.h"
+#include "utils/numeric.h"
#include "utils/timestamp.h"
/*
@@ -3018,6 +3019,8 @@ executeStringInternalMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
{
char *from_str;
Numeric n;
+ int32 field;
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
jspGetLeftArg(jsp, &elem);
if (elem.type != jpiString)
@@ -3031,11 +3034,23 @@ executeStringInternalMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
n = jspGetNumeric(&elem);
+ field = numeric_int4_safe(n, (Node *) &escontext);
+ if (escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("jsonpath item method .%s() field number is out of range for type integer",
+ jspOperationName(jsp->type)))));
+
+ if (field == 0)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("field position must not be zero"))));
+
resStr = TextDatumGetCString(DirectFunctionCall3Coll(split_part,
DEFAULT_COLLATION_OID,
str,
CStringGetTextDatum(from_str),
- DirectFunctionCall1(numeric_int4, NumericGetDatum(n))));
+ Int32GetDatum(field)));
break;
}
default:
diff --git a/src/test/regress/expected/jsonb_jsonpath.out b/src/test/regress/expected/jsonb_jsonpath.out
index afa6c4cb..14c13592 100644
--- a/src/test/regress/expected/jsonb_jsonpath.out
+++ b/src/test/regress/expected/jsonb_jsonpath.out
@@ -3073,6 +3073,46 @@ select jsonb_path_query('"abc,def,ghi,jkl"', '$.split_part(",", -2)');
"ghi"
(1 row)
+-- Test .split_part() error handling in lax mode (should suppress errors)
+select jsonb '"hello-world"' @? '$.split_part("-", 0)';
+ ?column?
+----------
+
+(1 row)
+
+select jsonb '"hello-world"' @? '$.split_part("-", 99999999999)';
+ ?column?
+----------
+
+(1 row)
+
+select jsonb_path_exists('"hello-world"', '$.split_part("-", 0)', silent => true);
+ jsonb_path_exists
+-------------------
+
+(1 row)
+
+select jsonb_path_exists('"hello-world"', '$.split_part("-", 99999999999)', silent => true);
+ jsonb_path_exists
+-------------------
+
+(1 row)
+
+select jsonb_path_query('"hello-world"', '$.split_part("-", 0)', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('"hello-world"', '$.split_part("-", 99999999999)', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+-- These should still error in strict/non-silent mode
+select jsonb_path_query('"hello-world"', '$.split_part("-", 0)');
+ERROR: field position must not be zero
+select jsonb_path_query('"hello-world"', '$.split_part("-", 99999999999)');
+ERROR: jsonpath item method .split_part() field number is out of range for type integer
-- Test string methods play nicely together
select jsonb_path_query('"hello world"', '$.replace("hello","bye").upper()');
jsonb_path_query
diff --git a/src/test/regress/sql/jsonb_jsonpath.sql b/src/test/regress/sql/jsonb_jsonpath.sql
index d3a38c57..b9e189a0 100644
--- a/src/test/regress/sql/jsonb_jsonpath.sql
+++ b/src/test/regress/sql/jsonb_jsonpath.sql
@@ -722,6 +722,17 @@ select jsonb_path_query('"hello world"', '$.replace("hello","bye") starts with "
select jsonb_path_query('"abc~@~def~@~ghi"', '$.split_part("~@~", 2)');
select jsonb_path_query('"abc,def,ghi,jkl"', '$.split_part(",", -2)');
+-- Test .split_part() error handling in lax mode (should suppress errors)
+select jsonb '"hello-world"' @? '$.split_part("-", 0)';
+select jsonb '"hello-world"' @? '$.split_part("-", 99999999999)';
+select jsonb_path_exists('"hello-world"', '$.split_part("-", 0)', silent => true);
+select jsonb_path_exists('"hello-world"', '$.split_part("-", 99999999999)', silent => true);
+select jsonb_path_query('"hello-world"', '$.split_part("-", 0)', silent => true);
+select jsonb_path_query('"hello-world"', '$.split_part("-", 99999999999)', silent => true);
+-- These should still error in strict/non-silent mode
+select jsonb_path_query('"hello-world"', '$.split_part("-", 0)');
+select jsonb_path_query('"hello-world"', '$.split_part("-", 99999999999)');
+
-- Test string methods play nicely together
select jsonb_path_query('"hello world"', '$.replace("hello","bye").upper()');
select jsonb_path_query('"hElLo WorlD"', '$.lower().upper().lower().replace("hello","bye")');
--
2.43.0
^ permalink raw reply [nested|flat] 2+ messages in thread
* Re: BUG: jsonpath .split_part() bypasses lax-mode error suppression
2026-04-18 16:31 BUG: jsonpath .split_part() bypasses lax-mode error suppression SATYANARAYANA NARLAPURAM <[email protected]>
@ 2026-04-19 20:21 ` Dmitry Dolgov <[email protected]>
0 siblings, 0 replies; 2+ messages in thread
From: Dmitry Dolgov @ 2026-04-19 20:21 UTC (permalink / raw)
To: SATYANARAYANA NARLAPURAM <[email protected]>; +Cc: PostgreSQL Hackers <[email protected]>; [email protected]; [email protected]
> On Sat, Apr 18, 2026 at 09:31:03AM -0700, SATYANARAYANA NARLAPURAM wrote:
> Hi hackers,
>
> The jsonpath string method .split_part() bypasses lax-mode error
> suppression.
> This is because executeStringInternalMethod() uses
> DirectFunctionCall1(numeric_int4, ...)
> to convert the field number from numeric to int4. This throws
> ereport(ERROR) directly,
> bypassing the jspThrowErrors(cxt) / RETURN_ERROR mechanism that other
> methods
> like .double() use correctly.
>
> Reproduction:
> -- These should return NULL in lax mode, but throw hard ERRORs:
> SELECT '"hello-world"'::jsonb @? '$.split_part("-", 99999999999)';
> SELECT '"hello-world"'::jsonb @? '$.split_part("-", 0)';
> ERROR: integer out of range
> ERROR: field position must not be zero
I don't have a SQL standard at hands, it might be worth checking what SQL/JSON
says about such situations. But at least from the definition of lax mode, given
in the documentation, current behavior seems to be correct:
lax (default) — the path engine implicitly adapts the queried data to
the specified path. Any structural errors that cannot be fixed as described
below are suppressed, producing no match.
I.e. only structural errors are suppressed, where a structural error defined as:
An attempt to access a non-existent member of an object or element of an
array is defined as a structural error.
In this case what we have is an error, which happens due to an incorrect
function argument valye (the second argument of split_part, either out of
integer bound, or zero for 1 based field counting). It has nothing to do with
the jsonb document, and hence doesn't fall into "structural errors" category.
^ permalink raw reply [nested|flat] 2+ messages in thread
end of thread, other threads:[~2026-04-19 20:21 UTC | newest]
Thread overview: 2+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2026-04-18 16:31 BUG: jsonpath .split_part() bypasses lax-mode error suppression SATYANARAYANA NARLAPURAM <[email protected]>
2026-04-19 20:21 ` Dmitry Dolgov <[email protected]>
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox