| From: | PG Bug reporting form <noreply(at)postgresql(dot)org> |
|---|---|
| To: | pgsql-bugs(at)lists(dot)postgresql(dot)org |
| Cc: | manuelreyesbravo(at)gmail(dot)com |
| Subject: | BUG #19638: Planner chooses an index-only scan for an index AM without amcanreturn, and execution fails |
| Date: | 2026-08-23 23:45:47 |
| Message-ID: | 19638-277d0f73dfaeaec8@postgresql.org |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-bugs |
The following bug has been logged on the website:
Bug reference: 19638
Logged by: Manuel Reyes Bravo
Email address: manuelreyesbravo(at)gmail(dot)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
the bug itself is in core, not in the extension. An AM that does not
implement
amcanreturn is legal per the documented index AM API; the planner
nevertheless
builds an index-only scan over it, and the executor then cannot run the
plan.
The extension is only the vehicle that exposes it -- I could not find any
in-core AM with the required combination (see "Why no in-core reproducer"
below), which is probably why this has gone unnoticed.
On PostgreSQL 19beta3 the following query produces a plan that cannot be
executed:
ERROR: no data returned for index-only scan
The same query, same schema and same extension code works correctly on 18.6.
Reproducer
----------
Using pgvectorscale 0.9.0 (its "diskann" AM) with pgvector 0.8.6:
CREATE EXTENSION vector;
CREATE EXTENSION vectorscale;
CREATE TABLE t_nopk (embedding vector(3));
CREATE INDEX idx_nopk ON t_nopk USING diskann (embedding);
INSERT INTO t_nopk VALUES ('[1,2,3]'), ('[4,5,6]'), ('[7,8,9]');
SET enable_seqscan = 0;
SELECT COUNT(*)
FROM (SELECT embedding FROM t_nopk ORDER BY embedding <-> NULL LIMIT
3) x;
19beta3:
QUERY PLAN
---------------------------------------------------
Aggregate
-> Limit
-> Index Only Scan using idx_nopk on t_nopk
ERROR: no data returned for index-only scan
18.6 (same extension, same schema, same query):
QUERY PLAN
---------------------------------
Aggregate
-> Limit
-> Seq Scan on t_nopk
Disabled: true
count
-------
3
So 18 correctly falls back to a disabled sequential scan and returns the
right
answer, while 19 produces an unexecutable plan.
Note: the table must have no PRIMARY KEY
----------------------------------------
With a btree primary key present, the planner uses that index for the
index-only scan instead and the problem does not appear. That cost me some
time, so it may save yours.
Why there is no in-core reproducer
----------------------------------
I tried to reproduce this with in-core AMs and could not. GIN and hash also
lack amcanreturn, but they require an index qual, so the path is never
considered. It appears to need amoptionalkey = true together with a missing
amcanreturn, and as far as I can tell no in-core AM has that combination. A
regression test would probably have to go through a test module.
Where it seems to come from
---------------------------
check_index_only() in src/backend/optimizer/path/indxpath.c ends with
return bms_is_subset(attrs_used, index_canreturn_attrs);
When attrs_used is empty, bms_is_subset() returns true regardless of what
the
AM can actually return, while index_can_return() returns false for an AM
whose
amcanreturn is NULL. So an index that can return nothing at all passes the
check as long as the query needs no attributes from it.
I have not bisected this, so what follows is a guess rather than a finding:
indxpath.c gained a path-generation mask in "Allow for plugin control over
path
generation strategies" (2026-01-28), and PGS_CONSIDER_INDEXONLY looks like a
plausible reason why this path is now considered where it previously was
not.
Someone familiar with that code will see it much faster than I did.
This also looks related to the earlier discussion in "[PATCH] Check that
index
can return in get_actual_variable_range()" (Sept-Oct 2025), which addressed
the
same underlying assumption in a different place. This case does not appear
to
be covered by that fix.
Versions tested
---------------
PostgreSQL 19beta3, built from source: fails as shown above
PostgreSQL 18.6, built from source with the same compiler and flags:
correct
Happy to test a patch or provide any further detail.
| From | Date | Subject | |
|---|---|---|---|
| Next Message | PG Bug reporting form | 2026-08-23 23:46:57 | BUG #19639: EXPLAIN (FORMAT JSON) emits a 309-digit cost value, and the node still reports "Disabled": false |
| Previous Message | Rui Zhao | 2026-08-23 17:12:46 | Re: BUG #19519: REPACK can fail due to missing chunk for toast value |