BUG #19639: EXPLAIN (FORMAT JSON) emits a 309-digit cost value, and the node still reports "Disabled": false
The following bug has been logged on the website:
Bug reference: 19639
Logged by: Manuel Reyes Bravo
Email address: manuelreyesbravo@gmail.com
PostgreSQL version: 19beta3
Operating system: Fedora 44, Linux 7.1.8, gcc 16.1.1, PostgreSQL bui
Description:
Note up front: reproducing this needs a third-party index access method, but
what I am reporting is the EXPLAIN output itself. Whatever the origin of the
cost value, EXPLAIN (FORMAT JSON) emitting a 309-digit numeric literal
breaks
JSON consumers, and that part is in core. See "On attribution" at the end
for
what I did and did not verify.
On 19beta3, EXPLAIN (FORMAT JSON) can emit a cost of DBL_MAX, rendered as a
309-digit number. serde_json rejects it with "number out of range", and I
would
expect most plan-analysis tooling to have the same problem, since there is
no
way for a client to consume it other than parsing the number as text.
There is a second, separate oddity in the same output: the node carries
"Disabled": false while having that saturated cost. On 18.6 the same query
gets an ordinary cost and the disabled node is marked as disabled, which is
what the disabled-nodes mechanism exists for.
Reproducer
----------
Using pgvectorscale 0.9.0 ("diskann") with pgvector 0.8.6:
CREATE EXTENSION vector;
CREATE EXTENSION vectorscale;
CREATE TABLE t_quant (
id SERIAL PRIMARY KEY,
embedding vector(3),
labels SMALLINT[]
);
CREATE INDEX idx_quant ON t_quant USING diskann (embedding, labels);
INSERT INTO t_quant (embedding, labels) VALUES
('[1,2,3]', '{1,2}'), ('[4,5,6]', '{1,3}'), ('[7,8,9]', '{2,3}');
SET enable_seqscan = 0;
EXPLAIN (FORMAT JSON)
SELECT * FROM t_quant
WHERE labels && '{1}'
ORDER BY labels, embedding <=> '[0,0,0]';
Ordering by labels first is the point: the index cannot satisfy that
ordering,
so with enable_seqscan off the only remaining plan is a penalized one.
19beta3:
"Node Type": "Sort"
"Total Cost": 179769313486231570814527423731704356798... (309 digits)
"Disabled": false
"Node Type": "Index Scan"
"Total Cost": 179769313486231570814527423731704356798... (309 digits)
"Disabled": false
18.6 (same extension, same schema, same query):
"Node Type": "Sort"
"Total Cost": 20.64
"Disabled": false
"Node Type": "Seq Scan"
"Total Cost": 20.63
"Disabled": true
The text format shows the same value, with the cost running off the line.
Two issues, I believe
---------------------
1. Regardless of how the cost became that large, EXPLAIN (FORMAT JSON)
producing a 309-digit numeric literal is a problem in itself. JSON
consumers
are not prepared for it.
2. "Disabled": false together with a saturated cost is self-contradictory.
Since the disabled-nodes mechanism was introduced precisely so that
disabling a node no longer required inflating its cost, seeing both
suggests
something is not going through that mechanism.
On attribution
--------------
What I measured: 19beta3 emits this and 18.6 does not, with identical
extension
code, schema and query.
What I did not determine: where the value originates. It is possible that
the
AM's amcostestimate returns a very large cost and that 19 propagates it
while
18 never generated that path at all (18 picks a sequential scan instead). So
I
am not claiming the root cause is in core -- but issue 1 seems worth
addressing
either way, and issue 2 looks like a genuine inconsistency in the output.
Versions tested
---------------
PostgreSQL 19beta3, built from source: as shown above
PostgreSQL 18.6, built from source with the same compiler and flags:
normal costs
Happy to dig further if someone points me at the right place to look.
On Tue, 25 Aug 2026 at 00:16, PG Bug reporting form
<noreply@postgresql.org> wrote:
On 19beta3, EXPLAIN (FORMAT JSON) can emit a cost of DBL_MAX, rendered as a
309-digit number. serde_json rejects it with "number out of range", and I
would
expect most plan-analysis tooling to have the same problem, since there is
no
way for a client to consume it other than parsing the number as text.
Does serde_json parse it if you add a .0 to the end of the number?
Maybe they should consider switching to doubles if the number
overflows the integer type they're using.
RFC8259 does say:
"This specification allows implementations to set limits on the range
and precision of numbers accepted. Since software that implements
IEEE 754 binary64 (double precision) numbers [IEEE754] is generally
available and widely used, good interoperability can be achieved by
implementations that expect no more precision or range than these
provide, in the sense that implementations will approximate JSON
numbers within the expected precision. A JSON number such as 1E400
or 3.141592653589793238462643383279 may indicate potential
interoperability problems, since it suggests that the software that
created it expects receiving software to have greater capabilities
for numeric magnitude and precision than is widely available."
So it does seem a bit poor if their parser failed on DBL_MAX.
David
Does serde_json parse it if you add a .0 to the end of the number?
It already has one -- EXPLAIN prints costs with %.2f, so the literal ends
in ".00" as emitted. serde_json 1.0 rejects it either way, and also
rejects the bare literal parsed as f64. But you are right that this is
its problem rather than PostgreSQL's, and I should correct my own report:
I claimed most plan-analysis tooling would choke on it and had not
measured that. Measured now:
Python 3 json (stdlib) parses fine -> 1.7976931348623157e+308
jq 1.7 parses fine
serde_json 1.0 fails -> number out of range
One parser, not most tooling. I will raise it with serde_json.
That leaves the second half of my report, and having gone looking, I am
withdrawing that too. I built a minimal test AM (the one from #19638)
whose amcostestimate simply returns DBL_MAX, and on 18.6:
"Node Type": "Index Scan"
"Total Cost": 179769313486231570814527423731704356798...
"Disabled": false
So core is faithfully propagating a cost the AM handed it, and
"Disabled": false is correct -- the node is not disabled, it is merely
absurdly expensive. I had read that flag as meaning something it does not
mean, and built an argument about self-contradictory output on top of it.
There is no inconsistency here, and nothing specific to 19: 18 does the
same thing when an AM reports that cost.
The origin is pgvectorscale's amcostestimate. I will take it there, where
it belongs.
Sorry for the noise. Both of these came from comparing 18 and 19 output
and reading a difference as a defect without first checking what produced
it, which is the same mistake I made in #19638.
El lun, 24 ago 2026 a las 9:52, David Rowley (<dgrowleyml@gmail.com>)
escribió:
On Tue, 25 Aug 2026 at 00:16, PG Bug reporting form
<noreply@postgresql.org> wrote:On 19beta3, EXPLAIN (FORMAT JSON) can emit a cost of DBL_MAX, rendered
as a
309-digit number. serde_json rejects it with "number out of range", and I
would
expect most plan-analysis tooling to have the same problem, since thereis
no
way for a client to consume it other than parsing the number as text.Does serde_json parse it if you add a .0 to the end of the number?
Maybe they should consider switching to doubles if the number
overflows the integer type they're using.RFC8259 does say:
"This specification allows implementations to set limits on the range
and precision of numbers accepted. Since software that implements
IEEE 754 binary64 (double precision) numbers [IEEE754] is generally
available and widely used, good interoperability can be achieved by
implementations that expect no more precision or range than these
provide, in the sense that implementations will approximate JSON
numbers within the expected precision. A JSON number such as 1E400
or 3.141592653589793238462643383279 may indicate potential
interoperability problems, since it suggests that the software that
created it expects receiving software to have greater capabilities
for numeric magnitude and precision than is widely available."So it does seem a bit poor if their parser failed on DBL_MAX.
David
--
Saludos cordiales,
Manuel Reyes