GIN jsonb_ops misses root strings in strict jsonpath queries

Started by Rui Zhao5 days ago3 messagesbugs
Beta feature

Hackorum builds and tests every patch posted to the lists, not only commitfest submissions. This is Hackorum's own CI rather than the PostgreSQL project's, and it is still under testing - please report anything that looks wrong.

won't retrysuccessCI history

This thread has been committed, so CI has stopped here. Anything below is the last result it produced.

You can run a PostgreSQL built from this patch straight from Docker, with no checkout and no build:

docker run --rm -p 5432:5432 ghcr.io/hackorum-dev/postgres-patch:t254293
psql -h localhost -U postgres

Built from patchset v1 (message #1), October 23, 2026 at 04:02 PM.

Every patchset is also pushed to a branch of our PostgreSQL fork, so you can check out the same tree CI built. Without a PostgreSQL checkout:

git clone --branch t254293_1 https://github.com/hackorum-dev/postgres.git

In a checkout you already have, add the fork once:

git remote add hackorum https://github.com/hackorum-dev/postgres.git

then, for this patchset and every later one:

git fetch hackorum t254293_1 && git checkout t254293_1

Patchset v1 (message #1) is on t254293_1

Jump to latest
#1Rui Zhao
zhaorui126@gmail.com

Hi,

A GIN jsonb_ops scan can miss a root string when a jsonpath comparison
uses strict mode. I reproduced this on master at e75fcc9867f.

Using the existing test_jsonb data in jsonb.sql, which includes the
root string "a scalar":

SELECT test_json FROM test_jsonb
WHERE test_json @@ 'strict $ == "a scalar"';
-- "a scalar"

SELECT test_json FROM test_jsonb
WHERE test_json @? 'strict $ ? (@ == "a scalar")';
-- "a scalar"

CREATE INDEX test_jsonb_idx ON test_jsonb USING gin (test_json);
SET enable_seqscan = off;

SELECT test_json FROM test_jsonb
WHERE test_json @@ 'strict $ == "a scalar"';
-- 0 rows

SELECT test_json FROM test_jsonb
WHERE test_json @? 'strict $ ? (@ == "a scalar")';
-- 0 rows

The index stores root strings with JGINFLAG_KEY. Internally, a root
scalar is wrapped in an array. JsonbValueToJsonb() does this inside
its IsAJsonbScalar(val) branch:

scalarArray.type = jbvArray;
scalarArray.val.array.rawScalar = true;
scalarArray.val.array.nElems = 1;

pushJsonbValue(&pstate, WJB_BEGIN_ARRAY, &scalarArray); /* [ */
pushJsonbValue(&pstate, WJB_ELEM, val); /* original value */
pushJsonbValue(&pstate, WJB_END_ARRAY, NULL); /* ] */

gin_extract_jsonb() therefore sees WJB_ELEM and stores the root string
in the GIN index with the JGINFLAG_KEY marker.
But when processing the SQL query, jsonb_ops__extract_nodes() sets
key_entry to GIN_FALSE for the root in strict mode. That searches for
JGINFLAG_STR instead, so it cannot find the matching root string.

The attached patch changes that root case to GIN_TRUE. Both queries
then return "a scalar" using the existing index; no REINDEX is needed.
This bug affects only strict-mode queries using jsonb_ops.

The patch adds the queries above to jsonb.sql. make check passed all
239 tests. Without the code fix, only the two new indexed queries fail.

Regards,
Rui

Attachments:

t254293_1
v1-0001-Fix-GIN-lookups-for-root-strings-in-strict-jsonpa.patchapplication/octet-stream; name=v1-0001-Fix-GIN-lookups-for-root-strings-in-strict-jsonpa.patchDownload+46-2
#2shihao zhong
zhong950419@gmail.com
In reply to: Rui Zhao (#1)
Re: GIN jsonb_ops misses root strings in strict jsonpath queries

Hi Rui,

I ran into the same bug on my side and ended up with the same one-line
fix, so +1 to v1.

Thanks,
Shihao

#3Tom Lane
tgl@sss.pgh.pa.us
In reply to: shihao zhong (#2)
Re: GIN jsonb_ops misses root strings in strict jsonpath queries

shihao zhong <zhong950419@gmail.com> writes:

Hi Rui,
I ran into the same bug on my side and ended up with the same one-line
fix, so +1 to v1.

Pushed with minor editorialization.

regards, tom lane