diff --git a/src/backend/utils/adt/jsonb_gin.c b/src/backend/utils/adt/jsonb_gin.c index 204fb8b591..1a8d646d74 100644 --- a/src/backend/utils/adt/jsonb_gin.c +++ b/src/backend/utils/adt/jsonb_gin.c @@ -375,51 +375,31 @@ gin_extract_jsonb_path(PG_FUNCTION_ARGS) parent = stack; stack = (PathHashStack *) palloc(sizeof(PathHashStack)); - if (parent->parent) - { - /* - * We pass forward hashes from previous container nesting - * levels so that nested arrays with an outermost nested - * object will have element hashes mixed with the - * outermost key. It's also somewhat useful to have - * nested objects' innermost values have hashes that are a - * function of not just their own key, but outer keys too. - * - * Nesting an array within another array will not alter - * innermost scalar element hash values, but that seems - * inconsequential. - */ - stack->hash = parent->hash; - } - else - { - /* - * At the outermost level, initialize hash with container - * type proxy value. Note that this makes JB_FARRAY and - * JB_FOBJECT part of the on-disk representation, but they - * are that in the base jsonb object storage already. - */ - stack->hash = (r == WJB_BEGIN_ARRAY) ? JB_FARRAY : JB_FOBJECT; - } + /* + * We pass forward hashes from outer nesting levels so that + * the hashes for nested values will include outer keys as + * well as their own keys. + * + * Nesting an array within another array will not alter + * innermost scalar element hash values, but that seems + * inconsequential. + */ + stack->hash = parent->hash; stack->parent = parent; break; case WJB_KEY: - /* initialize hash from parent */ - stack->hash = stack->parent->hash; - /* and mix in this key */ + /* mix this key into the current outer hash */ JsonbHashScalarValue(&v, &stack->hash); /* hash is now ready to incorporate the value */ break; case WJB_ELEM: - /* array elements use parent hash mixed with element's hash */ - stack->hash = stack->parent->hash; - /* FALL THRU */ case WJB_VALUE: /* mix the element or value's hash into the prepared hash */ JsonbHashScalarValue(&v, &stack->hash); /* and emit an index entry */ entries[i++] = UInt32GetDatum(stack->hash); - /* Note: we assume we'll see KEY before another VALUE */ + /* reset hash for next key, value, or sub-object */ + stack->hash = stack->parent->hash; break; case WJB_END_ARRAY: case WJB_END_OBJECT: @@ -427,6 +407,11 @@ gin_extract_jsonb_path(PG_FUNCTION_ARGS) parent = stack->parent; pfree(stack); stack = parent; + /* reset hash for next key, value, or sub-object */ + if (stack->parent) + stack->hash = stack->parent->hash; + else + stack->hash = 0; break; default: elog(ERROR, "invalid JsonbIteratorNext rc: %d", (int) r); diff --git a/src/test/regress/expected/jsonb.out b/src/test/regress/expected/jsonb.out index 58c2ab0273..89c3efd198 100644 --- a/src/test/regress/expected/jsonb.out +++ b/src/test/regress/expected/jsonb.out @@ -2420,6 +2420,56 @@ SELECT '{"a":[1,2,{"c":3,"x":4}],"c":"b"}'::jsonb @> '{"a":[{"x":4},1]}'; t (1 row) +-- check some corner cases for indexed nested containment (bug #13756) +create temp table nestjsonb (j jsonb); +insert into nestjsonb (j) values ('{"a":[["b",{"x":1}],["b",{"x":2}]],"c":3}'); +insert into nestjsonb (j) values ('[[14,2,3]]'); +insert into nestjsonb (j) values ('[1,[14,2,3]]'); +create index on nestjsonb using gin(j jsonb_path_ops); +set enable_seqscan = on; +set enable_bitmapscan = off; +select * from nestjsonb where j @> '{"a":[[{"x":2}]]}'::jsonb; + j +--------------------------------------------------- + {"a": [["b", {"x": 1}], ["b", {"x": 2}]], "c": 3} +(1 row) + +select * from nestjsonb where j @> '{"c":3}'; + j +--------------------------------------------------- + {"a": [["b", {"x": 1}], ["b", {"x": 2}]], "c": 3} +(1 row) + +select * from nestjsonb where j @> '[[14]]'; + j +----------------- + [[14, 2, 3]] + [1, [14, 2, 3]] +(2 rows) + +set enable_seqscan = off; +set enable_bitmapscan = on; +select * from nestjsonb where j @> '{"a":[[{"x":2}]]}'::jsonb; + j +--------------------------------------------------- + {"a": [["b", {"x": 1}], ["b", {"x": 2}]], "c": 3} +(1 row) + +select * from nestjsonb where j @> '{"c":3}'; + j +--------------------------------------------------- + {"a": [["b", {"x": 1}], ["b", {"x": 2}]], "c": 3} +(1 row) + +select * from nestjsonb where j @> '[[14]]'; + j +----------------- + [[14, 2, 3]] + [1, [14, 2, 3]] +(2 rows) + +reset enable_seqscan; +reset enable_bitmapscan; -- nested object field / array index lookup SELECT '{"n":null,"a":1,"b":[1,2],"c":{"1":2},"d":{"1":[2,3]}}'::jsonb -> 'n'; ?column? diff --git a/src/test/regress/sql/jsonb.sql b/src/test/regress/sql/jsonb.sql index a6b6d48205..b724f0ab1f 100644 --- a/src/test/regress/sql/jsonb.sql +++ b/src/test/regress/sql/jsonb.sql @@ -618,6 +618,26 @@ SELECT '{"a":[1,2,{"c":3,"x":4}],"c":"b"}'::jsonb @> '{"a":[{"x":4}]}'; SELECT '{"a":[1,2,{"c":3,"x":4}],"c":"b"}'::jsonb @> '{"a":[{"x":4},3]}'; SELECT '{"a":[1,2,{"c":3,"x":4}],"c":"b"}'::jsonb @> '{"a":[{"x":4},1]}'; +-- check some corner cases for indexed nested containment (bug #13756) +create temp table nestjsonb (j jsonb); +insert into nestjsonb (j) values ('{"a":[["b",{"x":1}],["b",{"x":2}]],"c":3}'); +insert into nestjsonb (j) values ('[[14,2,3]]'); +insert into nestjsonb (j) values ('[1,[14,2,3]]'); +create index on nestjsonb using gin(j jsonb_path_ops); + +set enable_seqscan = on; +set enable_bitmapscan = off; +select * from nestjsonb where j @> '{"a":[[{"x":2}]]}'::jsonb; +select * from nestjsonb where j @> '{"c":3}'; +select * from nestjsonb where j @> '[[14]]'; +set enable_seqscan = off; +set enable_bitmapscan = on; +select * from nestjsonb where j @> '{"a":[[{"x":2}]]}'::jsonb; +select * from nestjsonb where j @> '{"c":3}'; +select * from nestjsonb where j @> '[[14]]'; +reset enable_seqscan; +reset enable_bitmapscan; + -- nested object field / array index lookup SELECT '{"n":null,"a":1,"b":[1,2],"c":{"1":2},"d":{"1":[2,3]}}'::jsonb -> 'n'; SELECT '{"n":null,"a":1,"b":[1,2],"c":{"1":2},"d":{"1":[2,3]}}'::jsonb -> 'a';