From 97290ed6e61c9cf482154af0bdacd755b95921b0 Mon Sep 17 00:00:00 2001
From: Ewan Young <kdbase.hack@gmail.com>
Date: Wed, 24 Jun 2026 22:44:54 +0800
Subject: [PATCH] Fix jsonpath .decimal() to honor silent mode

The jsonpath .decimal() method passed the precision and scale through
numerictypmodin() via a direct function call, which throws a hard error
for a precision outside 1..1000 or a scale outside -1000..1000.  This
could not be trapped in silent mode (or by the @? and @@ operators that
are documented to suppress such errors), unlike the other error paths in
.decimal() which already use soft error reporting.

Validate the precision and scale against their valid ranges before
calling numerictypmodin(), reporting any violation softly so that silent
mode is honored.

This is the same class of oversight as commit 954e57708ea6 (.split_part);
this one dates to 66ea94e8e606.
---
 src/backend/utils/adt/jsonpath_exec.c        | 18 +++++++++
 src/test/regress/expected/jsonb_jsonpath.out | 42 +++++++++++++++++---
 src/test/regress/sql/jsonb_jsonpath.sql      |  7 ++++
 3 files changed, 62 insertions(+), 5 deletions(-)

diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index 6cc2acb4254..bb81b3c7801 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -1526,6 +1526,24 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
 														 jspOperationName(jsp->type)))));
 					}
 
+					/*
+					 * Validate the precision and scale here so that we can
+					 * report any error softly.  numerictypmodin() below would
+					 * otherwise throw a hard error for an out-of-range
+					 * precision or scale, which could not be trapped in silent
+					 * mode.
+					 */
+					if (precision < 1 || precision > NUMERIC_MAX_PRECISION)
+						RETURN_ERROR(ereport(ERROR,
+											 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+											  errmsg("precision of jsonpath item method .%s() must be between 1 and %d",
+													 jspOperationName(jsp->type), NUMERIC_MAX_PRECISION))));
+					if (scale < NUMERIC_MIN_SCALE || scale > NUMERIC_MAX_SCALE)
+						RETURN_ERROR(ereport(ERROR,
+											 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+											  errmsg("scale of jsonpath item method .%s() must be between %d and %d",
+													 jspOperationName(jsp->type), NUMERIC_MIN_SCALE, NUMERIC_MAX_SCALE))));
+
 					/*
 					 * numerictypmodin() takes the precision and scale in the
 					 * form of CString arrays.
diff --git a/src/test/regress/expected/jsonb_jsonpath.out b/src/test/regress/expected/jsonb_jsonpath.out
index 81efebc3d0f..5930ed4d438 100644
--- a/src/test/regress/expected/jsonb_jsonpath.out
+++ b/src/test/regress/expected/jsonb_jsonpath.out
@@ -2287,9 +2287,9 @@ select jsonb_path_query('1234.5678', '$.decimal(6, 2)');
 select jsonb_path_query('12345.678', '$.decimal(4, 6)');
 ERROR:  argument "12345.678" of jsonpath item method .decimal() is invalid for type numeric
 select jsonb_path_query('12345.678', '$.decimal(0, 6)');
-ERROR:  NUMERIC precision 0 must be between 1 and 1000
+ERROR:  precision of jsonpath item method .decimal() must be between 1 and 1000
 select jsonb_path_query('12345.678', '$.decimal(1001, 6)');
-ERROR:  NUMERIC precision 1001 must be between 1 and 1000
+ERROR:  precision of jsonpath item method .decimal() must be between 1 and 1000
 select jsonb_path_query('1234.5678', '$.decimal(+6, +2)');
  jsonb_path_query 
 ------------------
@@ -2303,11 +2303,11 @@ select jsonb_path_query('1234.5678', '$.decimal(+6, -2)');
 (1 row)
 
 select jsonb_path_query('1234.5678', '$.decimal(-6, +2)');
-ERROR:  NUMERIC precision -6 must be between 1 and 1000
+ERROR:  precision of jsonpath item method .decimal() must be between 1 and 1000
 select jsonb_path_query('1234.5678', '$.decimal(6, -1001)');
-ERROR:  NUMERIC scale -1001 must be between -1000 and 1000
+ERROR:  scale of jsonpath item method .decimal() must be between -1000 and 1000
 select jsonb_path_query('1234.5678', '$.decimal(6, 1001)');
-ERROR:  NUMERIC scale 1001 must be between -1000 and 1000
+ERROR:  scale of jsonpath item method .decimal() must be between -1000 and 1000
 select jsonb_path_query('-1234.5678', '$.decimal(+6, -2)');
  jsonb_path_query 
 ------------------
@@ -2336,6 +2336,38 @@ select jsonb_path_query('12.3', '$.decimal(12345678901,1)');
 ERROR:  precision of jsonpath item method .decimal() is out of range for type integer
 select jsonb_path_query('12.3', '$.decimal(1,12345678901)');
 ERROR:  scale of jsonpath item method .decimal() is out of range for type integer
+-- an out-of-range precision or scale must be trappable in silent mode
+select jsonb_path_query('12345.678', '$.decimal(0, 6)', silent => true);
+ jsonb_path_query 
+------------------
+(0 rows)
+
+select jsonb_path_query('12345.678', '$.decimal(1001, 6)', silent => true);
+ jsonb_path_query 
+------------------
+(0 rows)
+
+select jsonb_path_query('1234.5678', '$.decimal(-6, +2)', silent => true);
+ jsonb_path_query 
+------------------
+(0 rows)
+
+select jsonb_path_query('1234.5678', '$.decimal(6, -1001)', silent => true);
+ jsonb_path_query 
+------------------
+(0 rows)
+
+select jsonb_path_query('1234.5678', '$.decimal(6, 1001)', silent => true);
+ jsonb_path_query 
+------------------
+(0 rows)
+
+select '1.5'::jsonb @? '$.decimal(0)';
+ ?column? 
+----------
+ 
+(1 row)
+
 -- Test .integer()
 select jsonb_path_query('null', '$.integer()');
 ERROR:  jsonpath item method .integer() can only be applied to a string or numeric value
diff --git a/src/test/regress/sql/jsonb_jsonpath.sql b/src/test/regress/sql/jsonb_jsonpath.sql
index c1f4ab5422e..ec4d053d72f 100644
--- a/src/test/regress/sql/jsonb_jsonpath.sql
+++ b/src/test/regress/sql/jsonb_jsonpath.sql
@@ -523,6 +523,13 @@ select jsonb_path_query('0.0012345', '$.decimal(2,4)');
 select jsonb_path_query('-0.00123456', '$.decimal(2,-4)');
 select jsonb_path_query('12.3', '$.decimal(12345678901,1)');
 select jsonb_path_query('12.3', '$.decimal(1,12345678901)');
+-- an out-of-range precision or scale must be trappable in silent mode
+select jsonb_path_query('12345.678', '$.decimal(0, 6)', silent => true);
+select jsonb_path_query('12345.678', '$.decimal(1001, 6)', silent => true);
+select jsonb_path_query('1234.5678', '$.decimal(-6, +2)', silent => true);
+select jsonb_path_query('1234.5678', '$.decimal(6, -1001)', silent => true);
+select jsonb_path_query('1234.5678', '$.decimal(6, 1001)', silent => true);
+select '1.5'::jsonb @? '$.decimal(0)';
 
 -- Test .integer()
 select jsonb_path_query('null', '$.integer()');
-- 
2.47.3

