jsonpath .double() is a no-op on JSON numbers (differs from string input)

From: Ewan Young <kdbase(dot)hack(at)gmail(dot)com>
To: PostgreSQL Hackers <pgsql-hackers(at)lists(dot)postgresql(dot)org>
Subject: jsonpath .double() is a no-op on JSON numbers (differs from string input)
Date: 2026-07-02 10:07:26
Message-ID: CAON2xHPkO8Y67vToY8Ae_XF0Ku3=+oVcFPw1+j1XXf5EL4L9FQ@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hi,

While exercising the SQL/JSON path methods I noticed that the .double()
method gives different results for the same numeric value depending on
whether it is written as a JSON number or as a JSON string:
-- JSON number: unchanged
SELECT jsonb_path_query('1.0000000000000001', '$.double()');
jsonb_path_query
------------------
1.0000000000000001

-- JSON string: rounded to float8
SELECT jsonb_path_query('"1.0000000000000001"', '$.double()');
jsonb_path_query
------------------
1

-- string form yields true
SELECT jsonb_path_query('10000000000000001',
'$.double() == 10000000000000000');
jsonb_path_query
------------------
false

The documentation describes .double() as producing an "Approximate
floating-point number converted from a JSON number or string", so a
JSON number input is expected to be converted to the float8
approximation just like a string. Today it is effectively a no-op on
numbers, which is both a wrong result (no approximation) and, more
importantly, makes the method representation-dependent: the same value
compares equal or not depending only on how it was spelled in the JSON.

The cause is in executeItemOptUnwrapResult()'s jpiDouble case
(src/backend/utils/adt/jsonpath_exec.c). The string branch parses the
text with float8in_internal() and stores the resulting float8 value back
into the item. The numeric branch runs float8in_internal() only to
range-check the value (rejecting out-of-range / NaN / Infinity) and then
discards the result, leaving the original full-precision numeric in
place.

The attached patch makes the numeric branch store the float8
approximation as well, mirroring the string branch. The range, NaN and
Infinity checks are unchanged, so inputs like '1e400' still error as
before, and integral values are unaffected.

SELECT jsonb_path_query('1.0000000000000001', '$.double()'); -- now 1
SELECT jsonb_path_query('{"len": "1.9"}', '$.len.double() * 2'); -- 3.8

The existing regression tests only used values whose float8 and numeric
representations print identically (1.23, 1.9), which is why the
discrepancy went unnoticed; the patch adds a few high-precision cases.
It passes the jsonpath, jsonb_jsonpath and sqljson* suites.

One open question for committers: this changes long-standing output of
.double() on numeric inputs, so while it seems clearly right for master,
I'm not sure whether it is appropriate to back-patch. Happy to follow
whatever the consensus is.

Patch attached, against master.

--
Regards,
Ewan Young

Attachment Content-Type Size
v1-0001-Make-jsonpath-double-round-JSON-number-through-float8.patch application/octet-stream 6.0 KB

Browse pgsql-hackers by date

  From Date Subject
Next Message Álvaro Herrera 2026-07-02 10:20:52 Re: DROP INVALID INDEXES command
Previous Message shveta malik 2026-07-02 10:03:38 Re: Proposal: Conflict log history table for Logical Replication