mirror of
https://github.com/postgres/postgres.git
synced 2025-05-02 11:44:50 +03:00
Fix REINDEX CONCURRENTLY of partitions
In case of a partition index, when swapping the old and new index, we also need to attach the new index as a partition and detach the old one. Also, to handle partition indexes, we not only need to change dependencies referencing the index, but also dependencies of the index referencing something else. The previous code did this only specifically for a constraint, but we also need to do this for partitioned indexes. So instead write a generic function that does it for all dependencies. Author: Michael Paquier <michael@paquier.xyz> Author: Peter Eisentraut <peter.eisentraut@2ndquadrant.com> Discussion: https://www.postgresql.org/message-id/flat/DF4PR8401MB11964EDB77C860078C343BEBEE5A0%40DF4PR8401MB1196.NAMPRD84.PROD.OUTLOOK.COM#154df1fedb735190a773481765f7b874
This commit is contained in:
parent
f7feb020c3
commit
ef6f30fe77
@ -39,6 +39,7 @@
|
||||
#include "catalog/heap.h"
|
||||
#include "catalog/index.h"
|
||||
#include "catalog/objectaccess.h"
|
||||
#include "catalog/partition.h"
|
||||
#include "catalog/pg_am.h"
|
||||
#include "catalog/pg_collation.h"
|
||||
#include "catalog/pg_constraint.h"
|
||||
@ -1263,7 +1264,13 @@ index_concurrently_create_copy(Relation heapRelation, Oid oldIndexId, const char
|
||||
indexColNames = lappend(indexColNames, NameStr(att->attname));
|
||||
}
|
||||
|
||||
/* Now create the new index */
|
||||
/*
|
||||
* Now create the new index.
|
||||
*
|
||||
* For a partition index, we adjust the partition dependency later, to
|
||||
* ensure a consistent state at all times. That is why parentIndexRelid
|
||||
* is not set here.
|
||||
*/
|
||||
newIndexId = index_create(heapRelation,
|
||||
newName,
|
||||
InvalidOid, /* indexRelationId */
|
||||
@ -1395,6 +1402,9 @@ index_concurrently_swap(Oid newIndexId, Oid oldIndexId, const char *oldName)
|
||||
namestrcpy(&newClassForm->relname, NameStr(oldClassForm->relname));
|
||||
namestrcpy(&oldClassForm->relname, oldName);
|
||||
|
||||
/* Copy partition flag to track inheritance properly */
|
||||
newClassForm->relispartition = oldClassForm->relispartition;
|
||||
|
||||
CatalogTupleUpdate(pg_class, &oldClassTuple->t_self, oldClassTuple);
|
||||
CatalogTupleUpdate(pg_class, &newClassTuple->t_self, newClassTuple);
|
||||
|
||||
@ -1555,29 +1565,23 @@ index_concurrently_swap(Oid newIndexId, Oid oldIndexId, const char *oldName)
|
||||
}
|
||||
|
||||
/*
|
||||
* Move all dependencies on the old index to the new one
|
||||
* Swap inheritance relationship with parent index
|
||||
*/
|
||||
|
||||
if (OidIsValid(indexConstraintOid))
|
||||
if (get_rel_relispartition(oldIndexId))
|
||||
{
|
||||
ObjectAddress myself,
|
||||
referenced;
|
||||
List *ancestors = get_partition_ancestors(oldIndexId);
|
||||
Oid parentIndexRelid = linitial_oid(ancestors);
|
||||
|
||||
/* Change to having the new index depend on the constraint */
|
||||
deleteDependencyRecordsForClass(RelationRelationId, oldIndexId,
|
||||
ConstraintRelationId, DEPENDENCY_INTERNAL);
|
||||
DeleteInheritsTuple(oldIndexId, parentIndexRelid);
|
||||
StoreSingleInheritance(newIndexId, parentIndexRelid, 1);
|
||||
|
||||
myself.classId = RelationRelationId;
|
||||
myself.objectId = newIndexId;
|
||||
myself.objectSubId = 0;
|
||||
|
||||
referenced.classId = ConstraintRelationId;
|
||||
referenced.objectId = indexConstraintOid;
|
||||
referenced.objectSubId = 0;
|
||||
|
||||
recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
|
||||
list_free(ancestors);
|
||||
}
|
||||
|
||||
/*
|
||||
* Move all dependencies of and on the old index to the new one
|
||||
*/
|
||||
changeDependenciesOf(RelationRelationId, oldIndexId, newIndexId);
|
||||
changeDependenciesOn(RelationRelationId, oldIndexId, newIndexId);
|
||||
|
||||
/*
|
||||
|
@ -395,6 +395,62 @@ changeDependencyFor(Oid classId, Oid objectId,
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Adjust all dependency records to come from a different object of the same type
|
||||
*
|
||||
* classId/oldObjectId specify the old referencing object.
|
||||
* newObjectId is the new referencing object (must be of class classId).
|
||||
*
|
||||
* Returns the number of records updated.
|
||||
*/
|
||||
long
|
||||
changeDependenciesOf(Oid classId, Oid oldObjectId,
|
||||
Oid newObjectId)
|
||||
{
|
||||
long count = 0;
|
||||
Relation depRel;
|
||||
ScanKeyData key[2];
|
||||
SysScanDesc scan;
|
||||
HeapTuple tup;
|
||||
|
||||
depRel = table_open(DependRelationId, RowExclusiveLock);
|
||||
|
||||
ScanKeyInit(&key[0],
|
||||
Anum_pg_depend_classid,
|
||||
BTEqualStrategyNumber, F_OIDEQ,
|
||||
ObjectIdGetDatum(classId));
|
||||
ScanKeyInit(&key[1],
|
||||
Anum_pg_depend_objid,
|
||||
BTEqualStrategyNumber, F_OIDEQ,
|
||||
ObjectIdGetDatum(oldObjectId));
|
||||
|
||||
scan = systable_beginscan(depRel, DependDependerIndexId, true,
|
||||
NULL, 2, key);
|
||||
|
||||
while (HeapTupleIsValid((tup = systable_getnext(scan))))
|
||||
{
|
||||
Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
|
||||
|
||||
/* make a modifiable copy */
|
||||
tup = heap_copytuple(tup);
|
||||
depform = (Form_pg_depend) GETSTRUCT(tup);
|
||||
|
||||
depform->objid = newObjectId;
|
||||
|
||||
CatalogTupleUpdate(depRel, &tup->t_self, tup);
|
||||
|
||||
heap_freetuple(tup);
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
systable_endscan(scan);
|
||||
|
||||
table_close(depRel, RowExclusiveLock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Adjust all dependency records to point to a different object of the same type
|
||||
*
|
||||
|
@ -199,6 +199,9 @@ extern long changeDependencyFor(Oid classId, Oid objectId,
|
||||
Oid refClassId, Oid oldRefObjectId,
|
||||
Oid newRefObjectId);
|
||||
|
||||
extern long changeDependenciesOf(Oid classId, Oid oldObjectId,
|
||||
Oid newObjectId);
|
||||
|
||||
extern long changeDependenciesOn(Oid refClassId, Oid oldRefObjectId,
|
||||
Oid newRefObjectId);
|
||||
|
||||
|
@ -2001,6 +2001,91 @@ SELECT obj_description('testcomment_idx1'::regclass, 'pg_class');
|
||||
(1 row)
|
||||
|
||||
DROP TABLE testcomment;
|
||||
-- Partitions
|
||||
-- Create some partitioned tables
|
||||
CREATE TABLE concur_reindex_part (c1 int, c2 int) PARTITION BY RANGE (c1);
|
||||
CREATE TABLE concur_reindex_part_0 PARTITION OF concur_reindex_part
|
||||
FOR VALUES FROM (0) TO (10) PARTITION BY list (c2);
|
||||
CREATE TABLE concur_reindex_part_0_1 PARTITION OF concur_reindex_part_0
|
||||
FOR VALUES IN (1);
|
||||
CREATE TABLE concur_reindex_part_0_2 PARTITION OF concur_reindex_part_0
|
||||
FOR VALUES IN (2);
|
||||
-- This partitioned table will have no partitions.
|
||||
CREATE TABLE concur_reindex_part_10 PARTITION OF concur_reindex_part
|
||||
FOR VALUES FROM (10) TO (20) PARTITION BY list (c2);
|
||||
-- Create some partitioned indexes
|
||||
CREATE INDEX concur_reindex_part_index ON ONLY concur_reindex_part (c1);
|
||||
CREATE INDEX concur_reindex_part_index_0 ON ONLY concur_reindex_part_0 (c1);
|
||||
ALTER INDEX concur_reindex_part_index ATTACH PARTITION concur_reindex_part_index_0;
|
||||
-- This partitioned index will have no partitions.
|
||||
CREATE INDEX concur_reindex_part_index_10 ON ONLY concur_reindex_part_10 (c1);
|
||||
ALTER INDEX concur_reindex_part_index ATTACH PARTITION concur_reindex_part_index_10;
|
||||
CREATE INDEX concur_reindex_part_index_0_1 ON ONLY concur_reindex_part_0_1 (c1);
|
||||
ALTER INDEX concur_reindex_part_index_0 ATTACH PARTITION concur_reindex_part_index_0_1;
|
||||
CREATE INDEX concur_reindex_part_index_0_2 ON ONLY concur_reindex_part_0_2 (c1);
|
||||
ALTER INDEX concur_reindex_part_index_0 ATTACH PARTITION concur_reindex_part_index_0_2;
|
||||
SELECT relid, parentrelid, level FROM pg_partition_tree('concur_reindex_part_index')
|
||||
ORDER BY relid, level;
|
||||
relid | parentrelid | level
|
||||
-------------------------------+-----------------------------+-------
|
||||
concur_reindex_part_index | | 0
|
||||
concur_reindex_part_index_0 | concur_reindex_part_index | 1
|
||||
concur_reindex_part_index_10 | concur_reindex_part_index | 1
|
||||
concur_reindex_part_index_0_1 | concur_reindex_part_index_0 | 2
|
||||
concur_reindex_part_index_0_2 | concur_reindex_part_index_0 | 2
|
||||
(5 rows)
|
||||
|
||||
-- REINDEX fails for partitioned indexes
|
||||
REINDEX INDEX concur_reindex_part_index_10;
|
||||
ERROR: REINDEX is not yet implemented for partitioned indexes
|
||||
REINDEX INDEX CONCURRENTLY concur_reindex_part_index_10;
|
||||
ERROR: REINDEX is not yet implemented for partitioned indexes
|
||||
-- REINDEX is a no-op for partitioned tables
|
||||
REINDEX TABLE concur_reindex_part_10;
|
||||
WARNING: REINDEX of partitioned tables is not yet implemented, skipping "concur_reindex_part_10"
|
||||
NOTICE: table "concur_reindex_part_10" has no indexes
|
||||
REINDEX TABLE CONCURRENTLY concur_reindex_part_10;
|
||||
WARNING: REINDEX of partitioned tables is not yet implemented, skipping "concur_reindex_part_10"
|
||||
NOTICE: table "concur_reindex_part_10" has no indexes
|
||||
SELECT relid, parentrelid, level FROM pg_partition_tree('concur_reindex_part_index')
|
||||
ORDER BY relid, level;
|
||||
relid | parentrelid | level
|
||||
-------------------------------+-----------------------------+-------
|
||||
concur_reindex_part_index | | 0
|
||||
concur_reindex_part_index_0 | concur_reindex_part_index | 1
|
||||
concur_reindex_part_index_10 | concur_reindex_part_index | 1
|
||||
concur_reindex_part_index_0_1 | concur_reindex_part_index_0 | 2
|
||||
concur_reindex_part_index_0_2 | concur_reindex_part_index_0 | 2
|
||||
(5 rows)
|
||||
|
||||
-- REINDEX should preserve dependencies of partition tree.
|
||||
REINDEX INDEX CONCURRENTLY concur_reindex_part_index_0_1;
|
||||
REINDEX INDEX CONCURRENTLY concur_reindex_part_index_0_2;
|
||||
SELECT relid, parentrelid, level FROM pg_partition_tree('concur_reindex_part_index')
|
||||
ORDER BY relid, level;
|
||||
relid | parentrelid | level
|
||||
-------------------------------+-----------------------------+-------
|
||||
concur_reindex_part_index | | 0
|
||||
concur_reindex_part_index_0 | concur_reindex_part_index | 1
|
||||
concur_reindex_part_index_10 | concur_reindex_part_index | 1
|
||||
concur_reindex_part_index_0_1 | concur_reindex_part_index_0 | 2
|
||||
concur_reindex_part_index_0_2 | concur_reindex_part_index_0 | 2
|
||||
(5 rows)
|
||||
|
||||
REINDEX TABLE CONCURRENTLY concur_reindex_part_0_1;
|
||||
REINDEX TABLE CONCURRENTLY concur_reindex_part_0_2;
|
||||
SELECT relid, parentrelid, level FROM pg_partition_tree('concur_reindex_part_index')
|
||||
ORDER BY relid, level;
|
||||
relid | parentrelid | level
|
||||
-------------------------------+-----------------------------+-------
|
||||
concur_reindex_part_index | | 0
|
||||
concur_reindex_part_index_0 | concur_reindex_part_index | 1
|
||||
concur_reindex_part_index_10 | concur_reindex_part_index | 1
|
||||
concur_reindex_part_index_0_1 | concur_reindex_part_index_0 | 2
|
||||
concur_reindex_part_index_0_2 | concur_reindex_part_index_0 | 2
|
||||
(5 rows)
|
||||
|
||||
DROP TABLE concur_reindex_part;
|
||||
-- Check errors
|
||||
-- Cannot run inside a transaction block
|
||||
BEGIN;
|
||||
|
@ -789,6 +789,49 @@ SELECT obj_description('testcomment_idx1'::regclass, 'pg_class');
|
||||
REINDEX TABLE CONCURRENTLY testcomment ;
|
||||
SELECT obj_description('testcomment_idx1'::regclass, 'pg_class');
|
||||
DROP TABLE testcomment;
|
||||
-- Partitions
|
||||
-- Create some partitioned tables
|
||||
CREATE TABLE concur_reindex_part (c1 int, c2 int) PARTITION BY RANGE (c1);
|
||||
CREATE TABLE concur_reindex_part_0 PARTITION OF concur_reindex_part
|
||||
FOR VALUES FROM (0) TO (10) PARTITION BY list (c2);
|
||||
CREATE TABLE concur_reindex_part_0_1 PARTITION OF concur_reindex_part_0
|
||||
FOR VALUES IN (1);
|
||||
CREATE TABLE concur_reindex_part_0_2 PARTITION OF concur_reindex_part_0
|
||||
FOR VALUES IN (2);
|
||||
-- This partitioned table will have no partitions.
|
||||
CREATE TABLE concur_reindex_part_10 PARTITION OF concur_reindex_part
|
||||
FOR VALUES FROM (10) TO (20) PARTITION BY list (c2);
|
||||
-- Create some partitioned indexes
|
||||
CREATE INDEX concur_reindex_part_index ON ONLY concur_reindex_part (c1);
|
||||
CREATE INDEX concur_reindex_part_index_0 ON ONLY concur_reindex_part_0 (c1);
|
||||
ALTER INDEX concur_reindex_part_index ATTACH PARTITION concur_reindex_part_index_0;
|
||||
-- This partitioned index will have no partitions.
|
||||
CREATE INDEX concur_reindex_part_index_10 ON ONLY concur_reindex_part_10 (c1);
|
||||
ALTER INDEX concur_reindex_part_index ATTACH PARTITION concur_reindex_part_index_10;
|
||||
CREATE INDEX concur_reindex_part_index_0_1 ON ONLY concur_reindex_part_0_1 (c1);
|
||||
ALTER INDEX concur_reindex_part_index_0 ATTACH PARTITION concur_reindex_part_index_0_1;
|
||||
CREATE INDEX concur_reindex_part_index_0_2 ON ONLY concur_reindex_part_0_2 (c1);
|
||||
ALTER INDEX concur_reindex_part_index_0 ATTACH PARTITION concur_reindex_part_index_0_2;
|
||||
SELECT relid, parentrelid, level FROM pg_partition_tree('concur_reindex_part_index')
|
||||
ORDER BY relid, level;
|
||||
-- REINDEX fails for partitioned indexes
|
||||
REINDEX INDEX concur_reindex_part_index_10;
|
||||
REINDEX INDEX CONCURRENTLY concur_reindex_part_index_10;
|
||||
-- REINDEX is a no-op for partitioned tables
|
||||
REINDEX TABLE concur_reindex_part_10;
|
||||
REINDEX TABLE CONCURRENTLY concur_reindex_part_10;
|
||||
SELECT relid, parentrelid, level FROM pg_partition_tree('concur_reindex_part_index')
|
||||
ORDER BY relid, level;
|
||||
-- REINDEX should preserve dependencies of partition tree.
|
||||
REINDEX INDEX CONCURRENTLY concur_reindex_part_index_0_1;
|
||||
REINDEX INDEX CONCURRENTLY concur_reindex_part_index_0_2;
|
||||
SELECT relid, parentrelid, level FROM pg_partition_tree('concur_reindex_part_index')
|
||||
ORDER BY relid, level;
|
||||
REINDEX TABLE CONCURRENTLY concur_reindex_part_0_1;
|
||||
REINDEX TABLE CONCURRENTLY concur_reindex_part_0_2;
|
||||
SELECT relid, parentrelid, level FROM pg_partition_tree('concur_reindex_part_index')
|
||||
ORDER BY relid, level;
|
||||
DROP TABLE concur_reindex_part;
|
||||
|
||||
-- Check errors
|
||||
-- Cannot run inside a transaction block
|
||||
|
Loading…
x
Reference in New Issue
Block a user