Avoid trying to fetch metapage of an SPGist partitioned index.
authorTom Lane
Thu, 21 Dec 2023 17:43:36 +0000 (12:43 -0500)
committerTom Lane
Thu, 21 Dec 2023 17:43:36 +0000 (12:43 -0500)
This is necessary when spgcanreturn() is invoked on a partitioned
index, and the failure might be reachable in other scenarios as
well.  The rest of what spgGetCache() does is perfectly sensible
for a partitioned index, so we should allow it to go through.

I think the main takeaway from this is that we lack sufficient test
coverage for non-btree partitioned indexes.  Therefore, I added
simple test cases for brin and gin as well as spgist (hash and
gist AMs were covered already in indexing.sql).

Per bug #18256 from Alexander Lakhin.  Although the known test case
only fails since v16 (3c569049b), I've got no faith at all that there
aren't other ways to reach this problem; so back-patch to all
supported branches.

Discussion: https://postgr.es/m/18256-0b0e1b6e4a620f1b@postgresql.org

src/backend/access/spgist/spgutils.c
src/test/regress/expected/indexing.out
src/test/regress/sql/indexing.sql

index 8f32e46fb8313ea00d34e60a323a3affbdd05f2d..26499166e7cac9fdbfb1849383d3b1581026f68e 100644 (file)
@@ -186,8 +186,6 @@ spgGetCache(Relation index)
        Oid         atttype;
        spgConfigIn in;
        FmgrInfo   *procinfo;
-       Buffer      metabuffer;
-       SpGistMetaPageData *metadata;
 
        cache = MemoryContextAllocZero(index->rd_indexcxt,
                                       sizeof(SpGistCache));
@@ -255,19 +253,28 @@ spgGetCache(Relation index)
        fillTypeDesc(&cache->attPrefixType, cache->config.prefixType);
        fillTypeDesc(&cache->attLabelType, cache->config.labelType);
 
-       /* Last, get the lastUsedPages data from the metapage */
-       metabuffer = ReadBuffer(index, SPGIST_METAPAGE_BLKNO);
-       LockBuffer(metabuffer, BUFFER_LOCK_SHARE);
+       /*
+        * Finally, if it's a real index (not a partitioned one), get the
+        * lastUsedPages data from the metapage
+        */
+       if (index->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
+       {
+           Buffer      metabuffer;
+           SpGistMetaPageData *metadata;
+
+           metabuffer = ReadBuffer(index, SPGIST_METAPAGE_BLKNO);
+           LockBuffer(metabuffer, BUFFER_LOCK_SHARE);
 
-       metadata = SpGistPageGetMeta(BufferGetPage(metabuffer));
+           metadata = SpGistPageGetMeta(BufferGetPage(metabuffer));
 
-       if (metadata->magicNumber != SPGIST_MAGIC_NUMBER)
-           elog(ERROR, "index \"%s\" is not an SP-GiST index",
-                RelationGetRelationName(index));
+           if (metadata->magicNumber != SPGIST_MAGIC_NUMBER)
+               elog(ERROR, "index \"%s\" is not an SP-GiST index",
+                    RelationGetRelationName(index));
 
-       cache->lastUsedPages = metadata->lastUsedPages;
+           cache->lastUsedPages = metadata->lastUsedPages;
 
-       UnlockReleaseBuffer(metabuffer);
+           UnlockReleaseBuffer(metabuffer);
+       }
 
        index->rd_amcache = (void *) cache;
    }
index 368e735de24d22344f16e1f9602e885fc110ccc0..e17879b794f80d8e0b9c74b8076a2d27b2ca8313 100644 (file)
@@ -1281,6 +1281,45 @@ select tableoid::regclass, * from idxpart order by a;
  idxpart2 | 857142 | six
 (8 rows)
 
+drop table idxpart;
+-- Test some other non-btree index types
+create table idxpart (a int, b text, c int[]) partition by range (a);
+create table idxpart1 partition of idxpart for values from (0) to (100000);
+set enable_seqscan to off;
+create index idxpart_brin on idxpart using brin(b);
+explain (costs off) select * from idxpart where b = 'abcd';
+                QUERY PLAN                 
+-------------------------------------------
+ Bitmap Heap Scan on idxpart1 idxpart
+   Recheck Cond: (b = 'abcd'::text)
+   ->  Bitmap Index Scan on idxpart1_b_idx
+         Index Cond: (b = 'abcd'::text)
+(4 rows)
+
+drop index idxpart_brin;
+create index idxpart_spgist on idxpart using spgist(b);
+explain (costs off) select * from idxpart where b = 'abcd';
+                QUERY PLAN                 
+-------------------------------------------
+ Bitmap Heap Scan on idxpart1 idxpart
+   Recheck Cond: (b = 'abcd'::text)
+   ->  Bitmap Index Scan on idxpart1_b_idx
+         Index Cond: (b = 'abcd'::text)
+(4 rows)
+
+drop index idxpart_spgist;
+create index idxpart_gin on idxpart using gin(c);
+explain (costs off) select * from idxpart where c @> array[42];
+                  QUERY PLAN                  
+----------------------------------------------
+ Bitmap Heap Scan on idxpart1 idxpart
+   Recheck Cond: (c @> '{42}'::integer[])
+   ->  Bitmap Index Scan on idxpart1_c_idx
+         Index Cond: (c @> '{42}'::integer[])
+(4 rows)
+
+drop index idxpart_gin;
+reset enable_seqscan;
 drop table idxpart;
 -- intentionally leave some objects around
 create table idxpart (a int) partition by range (a);
index 6f60d1dc0fdd84df78c3a50b7915f33390930873..a48a3177faf5eae8531ea4adfd4f64d006a7547d 100644 (file)
@@ -668,6 +668,26 @@ insert into idxpart values (857142, 'six');
 select tableoid::regclass, * from idxpart order by a;
 drop table idxpart;
 
+-- Test some other non-btree index types
+create table idxpart (a int, b text, c int[]) partition by range (a);
+create table idxpart1 partition of idxpart for values from (0) to (100000);
+set enable_seqscan to off;
+
+create index idxpart_brin on idxpart using brin(b);
+explain (costs off) select * from idxpart where b = 'abcd';
+drop index idxpart_brin;
+
+create index idxpart_spgist on idxpart using spgist(b);
+explain (costs off) select * from idxpart where b = 'abcd';
+drop index idxpart_spgist;
+
+create index idxpart_gin on idxpart using gin(c);
+explain (costs off) select * from idxpart where c @> array[42];
+drop index idxpart_gin;
+
+reset enable_seqscan;
+drop table idxpart;
+
 -- intentionally leave some objects around
 create table idxpart (a int) partition by range (a);
 create table idxpart1 partition of idxpart for values from (0) to (100);