Skip to content

Commit 31f9d41

Browse files
committed
Fix updates of indisvalid for partitioned indexes
indisvalid is switched to true for partitioned indexes when all its partitions have valid indexes when attaching a new partition, up to the top-most parent if all its leaves are themselves valid when dealing with multiple layers of partitions. The copy of the tuple from pg_index used to switch indisvalid to true came from the relation cache, which is incorrect. Particularly, in the case reported by Shruthi Gowda, executing a series of commands in a single transaction would cause the validation of partitioned indexes to use an incorrect version of a pg_index tuple, as indexes are reloaded after an invalidation request with RelationReloadIndexInfo(), a much faster version than a full index cache rebuild. In this case, the limited information updated in the cache leads to an incorrect version of the tuple used. One of the symptoms reported was the following error, with a replica identity update, for instance: "ERROR: attempted to update invisible tuple" This is incorrect since 8b08f7d, so backpatch all the way down. Reported-by: Shruthi Gowda Author: Michael Paquier Reviewed-by: Shruthi Gowda, Dilip Kumar Discussion: https://postgr.es/m/CAASxf_PBcxax0wW-3gErUyftZ0XrCs3Lrpuhq4-Z3Fak1DoW7Q@mail.gmail.com Backpatch-through: 11
1 parent a4b4cc1 commit 31f9d41

File tree

3 files changed

+117
-4
lines changed

3 files changed

+117
-4
lines changed

src/backend/commands/tablecmds.c

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19174,17 +19174,24 @@ validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
1917419174
if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
1917519175
{
1917619176
Relation idxRel;
19177-
HeapTuple newtup;
19177+
HeapTuple indTup;
19178+
Form_pg_index indexForm;
1917819179

1917919180
idxRel = table_open(IndexRelationId, RowExclusiveLock);
19181+
indTup = SearchSysCacheCopy1(INDEXRELID,
19182+
ObjectIdGetDatum(RelationGetRelid(partedIdx)));
19183+
if (!HeapTupleIsValid(indTup))
19184+
elog(ERROR, "cache lookup failed for index %u",
19185+
RelationGetRelid(partedIdx));
19186+
indexForm = (Form_pg_index) GETSTRUCT(indTup);
1918019187

19181-
newtup = heap_copytuple(partedIdx->rd_indextuple);
19182-
((Form_pg_index) GETSTRUCT(newtup))->indisvalid = true;
19188+
indexForm->indisvalid = true;
1918319189
updated = true;
1918419190

19185-
CatalogTupleUpdate(idxRel, &partedIdx->rd_indextuple->t_self, newtup);
19191+
CatalogTupleUpdate(idxRel, &indTup->t_self, indTup);
1918619192

1918719193
table_close(idxRel, RowExclusiveLock);
19194+
heap_freetuple(indTup);
1918819195
}
1918919196

1919019197
/*

src/test/regress/expected/indexing.out

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1486,3 +1486,66 @@ select indexrelid::regclass, indisvalid,
14861486
(5 rows)
14871487

14881488
drop table parted_isvalid_tab;
1489+
-- Check state of replica indexes when attaching a partition.
1490+
begin;
1491+
create table parted_replica_tab (id int not null) partition by range (id);
1492+
create table parted_replica_tab_1 partition of parted_replica_tab
1493+
for values from (1) to (10) partition by range (id);
1494+
create table parted_replica_tab_11 partition of parted_replica_tab_1
1495+
for values from (1) to (5);
1496+
create unique index parted_replica_idx
1497+
on only parted_replica_tab using btree (id);
1498+
create unique index parted_replica_idx_1
1499+
on only parted_replica_tab_1 using btree (id);
1500+
-- This triggers an update of pg_index.indisreplident for parted_replica_idx.
1501+
alter table only parted_replica_tab_1 replica identity
1502+
using index parted_replica_idx_1;
1503+
create unique index parted_replica_idx_11 on parted_replica_tab_11 USING btree (id);
1504+
select indexrelid::regclass, indisvalid, indisreplident,
1505+
indrelid::regclass, inhparent::regclass
1506+
from pg_index idx left join
1507+
pg_inherits inh on (idx.indexrelid = inh.inhrelid)
1508+
where indexrelid::regclass::text like 'parted_replica%'
1509+
order by indexrelid::regclass::text collate "C";
1510+
indexrelid | indisvalid | indisreplident | indrelid | inhparent
1511+
-----------------------+------------+----------------+-----------------------+-----------
1512+
parted_replica_idx | f | f | parted_replica_tab |
1513+
parted_replica_idx_1 | f | t | parted_replica_tab_1 |
1514+
parted_replica_idx_11 | t | f | parted_replica_tab_11 |
1515+
(3 rows)
1516+
1517+
-- parted_replica_idx is not valid yet here, because parted_replica_idx_1
1518+
-- is not valid.
1519+
alter index parted_replica_idx ATTACH PARTITION parted_replica_idx_1;
1520+
select indexrelid::regclass, indisvalid, indisreplident,
1521+
indrelid::regclass, inhparent::regclass
1522+
from pg_index idx left join
1523+
pg_inherits inh on (idx.indexrelid = inh.inhrelid)
1524+
where indexrelid::regclass::text like 'parted_replica%'
1525+
order by indexrelid::regclass::text collate "C";
1526+
indexrelid | indisvalid | indisreplident | indrelid | inhparent
1527+
-----------------------+------------+----------------+-----------------------+--------------------
1528+
parted_replica_idx | f | f | parted_replica_tab |
1529+
parted_replica_idx_1 | f | t | parted_replica_tab_1 | parted_replica_idx
1530+
parted_replica_idx_11 | t | f | parted_replica_tab_11 |
1531+
(3 rows)
1532+
1533+
-- parted_replica_idx becomes valid here.
1534+
alter index parted_replica_idx_1 ATTACH PARTITION parted_replica_idx_11;
1535+
alter table only parted_replica_tab_1 replica identity
1536+
using index parted_replica_idx_1;
1537+
commit;
1538+
select indexrelid::regclass, indisvalid, indisreplident,
1539+
indrelid::regclass, inhparent::regclass
1540+
from pg_index idx left join
1541+
pg_inherits inh on (idx.indexrelid = inh.inhrelid)
1542+
where indexrelid::regclass::text like 'parted_replica%'
1543+
order by indexrelid::regclass::text collate "C";
1544+
indexrelid | indisvalid | indisreplident | indrelid | inhparent
1545+
-----------------------+------------+----------------+-----------------------+----------------------
1546+
parted_replica_idx | t | f | parted_replica_tab |
1547+
parted_replica_idx_1 | t | t | parted_replica_tab_1 | parted_replica_idx
1548+
parted_replica_idx_11 | t | f | parted_replica_tab_11 | parted_replica_idx_1
1549+
(3 rows)
1550+
1551+
drop table parted_replica_tab;

src/test/regress/sql/indexing.sql

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -808,3 +808,46 @@ select indexrelid::regclass, indisvalid,
808808
where indexrelid::regclass::text like 'parted_isvalid%'
809809
order by indexrelid::regclass::text collate "C";
810810
drop table parted_isvalid_tab;
811+
812+
-- Check state of replica indexes when attaching a partition.
813+
begin;
814+
create table parted_replica_tab (id int not null) partition by range (id);
815+
create table parted_replica_tab_1 partition of parted_replica_tab
816+
for values from (1) to (10) partition by range (id);
817+
create table parted_replica_tab_11 partition of parted_replica_tab_1
818+
for values from (1) to (5);
819+
create unique index parted_replica_idx
820+
on only parted_replica_tab using btree (id);
821+
create unique index parted_replica_idx_1
822+
on only parted_replica_tab_1 using btree (id);
823+
-- This triggers an update of pg_index.indisreplident for parted_replica_idx.
824+
alter table only parted_replica_tab_1 replica identity
825+
using index parted_replica_idx_1;
826+
create unique index parted_replica_idx_11 on parted_replica_tab_11 USING btree (id);
827+
select indexrelid::regclass, indisvalid, indisreplident,
828+
indrelid::regclass, inhparent::regclass
829+
from pg_index idx left join
830+
pg_inherits inh on (idx.indexrelid = inh.inhrelid)
831+
where indexrelid::regclass::text like 'parted_replica%'
832+
order by indexrelid::regclass::text collate "C";
833+
-- parted_replica_idx is not valid yet here, because parted_replica_idx_1
834+
-- is not valid.
835+
alter index parted_replica_idx ATTACH PARTITION parted_replica_idx_1;
836+
select indexrelid::regclass, indisvalid, indisreplident,
837+
indrelid::regclass, inhparent::regclass
838+
from pg_index idx left join
839+
pg_inherits inh on (idx.indexrelid = inh.inhrelid)
840+
where indexrelid::regclass::text like 'parted_replica%'
841+
order by indexrelid::regclass::text collate "C";
842+
-- parted_replica_idx becomes valid here.
843+
alter index parted_replica_idx_1 ATTACH PARTITION parted_replica_idx_11;
844+
alter table only parted_replica_tab_1 replica identity
845+
using index parted_replica_idx_1;
846+
commit;
847+
select indexrelid::regclass, indisvalid, indisreplident,
848+
indrelid::regclass, inhparent::regclass
849+
from pg_index idx left join
850+
pg_inherits inh on (idx.indexrelid = inh.inhrelid)
851+
where indexrelid::regclass::text like 'parted_replica%'
852+
order by indexrelid::regclass::text collate "C";
853+
drop table parted_replica_tab;

0 commit comments

Comments
 (0)