From 82a143a1f4e62e6b9d409620bfe46616d555ce4e Mon Sep 17 00:00:00 2001
From: Ewan Young <kdbase.hack@gmail.com>
Date: Fri, 3 Jul 2026 01:54:40 +0800
Subject: [PATCH] Make jsonpath .double() round a JSON number through float8

The .double() jsonpath method is documented to yield an "Approximate
floating-point number converted from a JSON number or string".  For a
string input, executeItemOptUnwrapResult() parses the text with
float8in_internal() and stores the resulting float8 value back into the
item.  For a JSON *number* input, however, it ran float8in_internal()
only to range-check the value and then discarded the result, leaving the
original full-precision numeric in place.

As a result the method was a no-op on numbers and, worse, gave a
different answer depending on whether the same value was written as a
JSON number or a JSON string:

    jsonb_path_query('1.0000000000000001',   '$.double()')  => 1.0000000000000001
    jsonb_path_query('"1.0000000000000001"', '$.double()')  => 1

The method is supposed to be value-based, not representation-based, and
the number result also failed to be the approximate float the
documentation promises.  Make the numeric branch store the float8
approximation too, mirroring the string branch, so both inputs yield the
same result.  Range/NaN/Infinity checks are unchanged.

The existing regression tests only used values whose float8 and numeric
representations print identically (1.23, 1.9), so the discrepancy went
unnoticed; add high-precision cases that would have caught it.
---
 src/backend/utils/adt/jsonpath_exec.c        | 13 +++++++++++++
 src/test/regress/expected/jsonb_jsonpath.out | 20 ++++++++++++++++++++
 src/test/regress/sql/jsonb_jsonpath.sql      |  5 +++++
 3 files changed, 38 insertions(+)

diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index 6cc2acb4254..e86a2b27828 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -1199,6 +1199,19 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
 											 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
 											  errmsg("NaN or Infinity is not allowed for jsonpath item method .%s()",
 													 jspOperationName(jsp->type)))));
+
+					/*
+					 * Return the float8 approximation of the value, not the
+					 * original numeric.  .double() is documented to yield an
+					 * approximate floating-point number, and the string branch
+					 * below already rounds through float8; a JSON number input
+					 * must give the same result as the equivalent string so
+					 * that the method is value-based, not representation-based.
+					 */
+					jb = &jbv;
+					jb->type = jbvNumeric;
+					jb->val.numeric = DatumGetNumeric(DirectFunctionCall1(float8_numeric,
+																		  Float8GetDatum(val)));
 					res = jperOk;
 				}
 				else if (jb->type == jbvString)
diff --git a/src/test/regress/expected/jsonb_jsonpath.out b/src/test/regress/expected/jsonb_jsonpath.out
index 81efebc3d0f..f9bc2c288cb 100644
--- a/src/test/regress/expected/jsonb_jsonpath.out
+++ b/src/test/regress/expected/jsonb_jsonpath.out
@@ -1547,6 +1547,26 @@ select jsonb_path_query('"1.23"', '$.double()');
 
 select jsonb_path_query('"1.23aaa"', '$.double()');
 ERROR:  argument "1.23aaa" of jsonpath item method .double() is invalid for type double precision
+-- .double() must yield the float8 approximation for a JSON number too, giving
+-- the same result as the equivalent JSON string (value-based, not text-based)
+select jsonb_path_query('1.0000000000000001', '$.double()');
+ jsonb_path_query 
+------------------
+ 1
+(1 row)
+
+select jsonb_path_query('"1.0000000000000001"', '$.double()');
+ jsonb_path_query 
+------------------
+ 1
+(1 row)
+
+select jsonb_path_query('10000000000000001', '$.double() == 10000000000000000');
+ jsonb_path_query 
+------------------
+ true
+(1 row)
+
 select jsonb_path_query('1e1000', '$.double()');
 ERROR:  argument "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" of jsonpath item method .double() is invalid for type double precision
 select jsonb_path_query('"nan"', '$.double()');
diff --git a/src/test/regress/sql/jsonb_jsonpath.sql b/src/test/regress/sql/jsonb_jsonpath.sql
index c1f4ab5422e..5835be10e68 100644
--- a/src/test/regress/sql/jsonb_jsonpath.sql
+++ b/src/test/regress/sql/jsonb_jsonpath.sql
@@ -323,6 +323,11 @@ select jsonb_path_query('{}', '$.double()', silent => true);
 select jsonb_path_query('1.23', '$.double()');
 select jsonb_path_query('"1.23"', '$.double()');
 select jsonb_path_query('"1.23aaa"', '$.double()');
+-- .double() must yield the float8 approximation for a JSON number too, giving
+-- the same result as the equivalent JSON string (value-based, not text-based)
+select jsonb_path_query('1.0000000000000001', '$.double()');
+select jsonb_path_query('"1.0000000000000001"', '$.double()');
+select jsonb_path_query('10000000000000001', '$.double() == 10000000000000000');
 select jsonb_path_query('1e1000', '$.double()');
 select jsonb_path_query('"nan"', '$.double()');
 select jsonb_path_query('"NaN"', '$.double()');
-- 
2.47.3

