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:
Peter Eisentraut 2019-04-12 08:36:05 +02:00
parent f7feb020c3
commit ef6f30fe77
5 changed files with 209 additions and 18 deletions

View File

@ -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);
/*

View File

@ -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
*

View File

@ -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);

View File

@ -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;

View File

@ -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