From f960543a0d1c10f9ccbb9bb19b994b47d5929365 Mon Sep 17 00:00:00 2001 From: David Zhang Date: Thu, 17 Nov 2022 12:24:35 -0800 Subject: [PATCH 3/4] support global unique index attach and detach --- src/backend/commands/tablecmds.c | 214 +++++++++++++++++++++++++ src/test/regress/expected/indexing.out | 55 +++++++ src/test/regress/sql/indexing.sql | 33 ++++ 3 files changed, 302 insertions(+) diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index b16a1180d8..5aca295d29 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -645,6 +645,7 @@ static void ATDetachCheckNoForeignKeyRefs(Relation partition); static char GetAttributeCompression(Oid atttypid, char *compression); static char GetAttributeStorage(Oid atttypid, const char *storagemode); +static bool HasGlobalChildIndex(Relation idxRel); /* ---------------------------------------------------------------- * DefineRelation @@ -1217,6 +1218,11 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, idxstmt = generateClonedIndexStmt(NULL, idxRel, attmap, &constraintOid); + if (HasGlobalChildIndex(idxRel)) + { + elog(DEBUG2, "create global index for the new child partition table"); + idxstmt->global_index = true; + } DefineIndex(RelationGetRelid(rel), idxstmt, InvalidOid, @@ -17992,6 +17998,7 @@ AttachPartitionEnsureIndexes(Relation rel, Relation attachrel) IndexInfo **attachInfos; int i; ListCell *cell; + ListCell *cell2; MemoryContext cxt; MemoryContext oldcxt; @@ -18130,15 +18137,185 @@ AttachPartitionEnsureIndexes(Relation rel, Relation attachrel) { IndexStmt *stmt; Oid conOid; + bool isGlobal = false; + isGlobal = HasGlobalChildIndex(idxRel); stmt = generateClonedIndexStmt(NULL, idxRel, attmap, &conOid); + + /* + * Perform cross partition uniqueness check if it is a global + * unique index + */ + if (isGlobal && idxRel->rd_index->indisunique) + { + PartitionDesc partdesc; + Relation hRel; + Relation iRel; + int j = 0; + int nparts; + Oid *part_oids; + + List *childIndexList = find_inheritance_children(idx, ShareLock); + + partdesc = RelationGetPartitionDesc(rel, true); + nparts = partdesc->nparts; + part_oids = palloc(sizeof(Oid) * nparts); + + memcpy(part_oids, partdesc->oids, sizeof(Oid) * nparts); + for (j = 0; j < nparts; j++) + { + Oid childRelid = part_oids[j]; + List *childidxs; + + if (childRelid == RelationGetRelid(attachrel)) + { + elog(DEBUG2, "skip the partition-to-be from building global spool: %d", childRelid); + continue; + } + hRel = table_open(childRelid, AccessShareLock); + + childidxs = RelationGetIndexList(hRel); + foreach(cell2, childidxs) + { + Oid cldidxid = lfirst_oid(cell2); + + /* + * only take a child index that is directly inherited + * to parent index oid + */ + if (list_member_oid(childIndexList, cldidxid)) + { + iRel = index_open(cldidxid, AccessShareLock); + elog(DEBUG2, "found a matching child index OID to build global spool %d", cldidxid); + + /* + * We need to construct a global spool structure + * in nbtsort.c in order to determine global + * uniqueness. Marking partitions now + */ + if (j == 0) + { + elog(DEBUG2, "mark as first partitioned to build global spool"); + stmt->globalIndexPart = -1; + } + else + stmt->globalIndexPart = 0; + + stmt->global_index = true; + stmt->nparts = nparts; + + PopulateGlobalSpool(iRel, hRel, stmt); + index_close(iRel, NoLock); + break; + } + } + table_close(hRel, NoLock); + } + elog(DEBUG2, "mark as the last partitioned to utilize global spool"); + stmt->globalIndexPart = 1; + } + else + { + elog(DEBUG2, "partitioned index %d is not a unique index, build it now...", + RelationGetRelid(idxRel)); + } + DefineIndex(RelationGetRelid(attachrel), stmt, InvalidOid, RelationGetRelid(idxRel), conOid, true, false, false, false, false); } + else + { + IndexStmt *stmt; + Oid conOid; + bool isGlobal = false; + + stmt = generateClonedIndexStmt(NULL, + idxRel, attmap, + &conOid); + isGlobal = HasGlobalChildIndex(idxRel); + if (isGlobal && idxRel->rd_index->indisunique) + { + PartitionDesc partdesc; + Relation hRel; + Relation iRel; + int j = 0; + int nparts; + Oid *part_oids; + + List *childIndexList = find_inheritance_children(idx, ShareLock); + + partdesc = RelationGetPartitionDesc(rel, true); + nparts = partdesc->nparts; + part_oids = palloc(sizeof(Oid) * nparts); + + memcpy(part_oids, partdesc->oids, sizeof(Oid) * nparts); + for (j = 0; j < nparts; j++) + { + Oid childRelid = part_oids[j]; + List *childidxs; + + hRel = table_open(childRelid, AccessShareLock); + + childidxs = RelationGetIndexList(hRel); + foreach(cell2, childidxs) + { + Oid cldidxid = lfirst_oid(cell2); + + /* + * only take a child index that is directly inherited + * to parent index oid + */ + if (list_member_oid(childIndexList, cldidxid)) + { + iRel = index_open(cldidxid, AccessShareLock); + elog(DEBUG2, "found a matching child index OID type %c to build global spool %d", + iRel->rd_rel->relkind, cldidxid); + + /* + * change partition-to-be's duplicate unique index + * relkind to RELKIND_GLOBAL_INDEX + */ + if (iRel->rd_rel->relkind != RELKIND_GLOBAL_INDEX) + { + elog(DEBUG2, "Update index relation %d to have relkind = RELKIND_GLOBAL_INDEX", + RelationGetRelid(iRel)); + ChangeRelKind(iRel, RELKIND_GLOBAL_INDEX); + } + + /* + * We need to construct a global spool structure + * in nbtsort.c in order to determine global + * uniqueness. Marking partitions now + */ + if (j == 0) + { + elog(DEBUG2, "mark as first partition to build global spool"); + stmt->globalIndexPart = -1; + } + else if (j == nparts - 1) + { + elog(DEBUG2, "mark as last partition to build global spool"); + stmt->globalIndexPart = 1; + } + else + stmt->globalIndexPart = 0; + + stmt->global_index = true; + stmt->nparts = nparts; + + PopulateGlobalSpool(iRel, hRel, stmt); + index_close(iRel, NoLock); + break; + } + } + table_close(hRel, NoLock); + } + } + } index_close(idxRel, AccessShareLock); } @@ -18636,6 +18813,15 @@ DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent, if (OidIsValid(constrOid)) ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid); + /* + * if it has any global index, make it a regular index relkind after + * detach + */ + if (idx->rd_rel->relkind == RELKIND_GLOBAL_INDEX) + { + elog(DEBUG2, "found a global index, transform it to RELKIND_INDEX at detach..."); + ChangeRelKind(idx, RELKIND_INDEX); + } index_close(idx, NoLock); } @@ -19357,3 +19543,31 @@ GetAttributeStorage(Oid atttypid, const char *storagemode) return cstorage; } + +static bool +HasGlobalChildIndex(Relation idxRel) +{ + /* find out if current index contains child indexes that are global */ + List *childIndexOidList; + ListCell *cell; + bool isGlobal = false;; + + childIndexOidList = find_all_inheritors(RelationGetRelid(idxRel), + AccessExclusiveLock, NULL); + + foreach(cell, childIndexOidList) + { + Oid childIndexOid = lfirst_oid(cell); + Relation idxChildRel; + + idxChildRel = index_open(childIndexOid, AccessShareLock); + if (idxChildRel->rd_rel->relkind == RELKIND_GLOBAL_INDEX) + { + isGlobal = true; + index_close(idxChildRel, NoLock); + break; + } + index_close(idxChildRel, NoLock); + } + return isGlobal; +} diff --git a/src/test/regress/expected/indexing.out b/src/test/regress/expected/indexing.out index 30a3ce0f20..58de14c037 100644 --- a/src/test/regress/expected/indexing.out +++ b/src/test/regress/expected/indexing.out @@ -1473,3 +1473,58 @@ DETAIL: Key (b)=(572814) is duplicated. delete from gidxpart where a = 150000 and b = 572814; create unique index on gidxpart (b) global; drop table gidxpart; +-- Test partition attach and detach with global unique index (no existing index) +create table gidxpart (a int, b int, c text) partition by range (a); +create table gidxpart1 partition of gidxpart for values from (0) to (100000); +insert into gidxpart (a, b, c) values (42, 572814, 'inserted first on gidxpart1'); +create unique index on gidxpart (b) global; +create table gidxpart2 (a int, b int, c text); +insert into gidxpart2 (a, b, c) values (150000, 572814, 'dup inserted on gidxpart2'); +alter table gidxpart attach partition gidxpart2 for values from (100000) to (199999); -- should fail +ERROR: could not create unique index "gidxpart1_b_idx" +DETAIL: Key (b)=(572814) is duplicated. +update gidxpart2 set b = 5000; +alter table gidxpart attach partition gidxpart2 for values from (100000) to (199999); +select relname, relkind from pg_class where relname = 'gidxpart2_b_idx'; -- should be g + relname | relkind +-----------------+--------- + gidxpart2_b_idx | g +(1 row) + +alter table gidxpart detach partition gidxpart2; +select relname, relkind from pg_class where relname = 'gidxpart2_b_idx'; -- should be i + relname | relkind +-----------------+--------- + gidxpart2_b_idx | i +(1 row) + +drop table gidxpart; +drop table gidxpart2; +-- Test partition attach and detach with global unique index (with duplicate index) +create table gidxpart (a int, b int, c text) partition by range (a); +create table gidxpart1 partition of gidxpart for values from (0) to (100000); +insert into gidxpart (a, b, c) values (42, 572814, 'inserted first on gidxpart1'); +create unique index on gidxpart (b) global; +create table gidxpart2 (a int, b int, c text); +create unique index on gidxpart2 (b); +insert into gidxpart2 (a, b, c) values (150000, 572814, 'dup inserted on gidxpart2'); +select relname, relkind from pg_class where relname = 'gidxpart2_b_idx'; -- should be i + relname | relkind +-----------------+--------- + gidxpart2_b_idx | i +(1 row) + +alter table gidxpart attach partition gidxpart2 for values from (100000) to (199999); -- should fail +ERROR: could not create unique index "gidxpart1_b_idx" +DETAIL: Key (b)=(572814) is duplicated. +update gidxpart2 set b = 5000; +alter table gidxpart attach partition gidxpart2 for values from (100000) to (199999); +select relname, relkind from pg_class where relname = 'gidxpart2_b_idx'; -- should be g + relname | relkind +-----------------+--------- + gidxpart2_b_idx | g +(1 row) + +alter table gidxpart detach partition gidxpart2; +drop table gidxpart; +drop table gidxpart2; diff --git a/src/test/regress/sql/indexing.sql b/src/test/regress/sql/indexing.sql index 84dde4df93..78649bb5ca 100644 --- a/src/test/regress/sql/indexing.sql +++ b/src/test/regress/sql/indexing.sql @@ -783,3 +783,36 @@ delete from gidxpart where a = 150000 and b = 572814; create unique index on gidxpart (b) global; drop table gidxpart; +-- Test partition attach and detach with global unique index (no existing index) +create table gidxpart (a int, b int, c text) partition by range (a); +create table gidxpart1 partition of gidxpart for values from (0) to (100000); +insert into gidxpart (a, b, c) values (42, 572814, 'inserted first on gidxpart1'); +create unique index on gidxpart (b) global; +create table gidxpart2 (a int, b int, c text); +insert into gidxpart2 (a, b, c) values (150000, 572814, 'dup inserted on gidxpart2'); +alter table gidxpart attach partition gidxpart2 for values from (100000) to (199999); -- should fail +update gidxpart2 set b = 5000; +alter table gidxpart attach partition gidxpart2 for values from (100000) to (199999); +select relname, relkind from pg_class where relname = 'gidxpart2_b_idx'; -- should be g +alter table gidxpart detach partition gidxpart2; +select relname, relkind from pg_class where relname = 'gidxpart2_b_idx'; -- should be i +drop table gidxpart; +drop table gidxpart2; + +-- Test partition attach and detach with global unique index (with duplicate index) +create table gidxpart (a int, b int, c text) partition by range (a); +create table gidxpart1 partition of gidxpart for values from (0) to (100000); +insert into gidxpart (a, b, c) values (42, 572814, 'inserted first on gidxpart1'); +create unique index on gidxpart (b) global; +create table gidxpart2 (a int, b int, c text); +create unique index on gidxpart2 (b); +insert into gidxpart2 (a, b, c) values (150000, 572814, 'dup inserted on gidxpart2'); +select relname, relkind from pg_class where relname = 'gidxpart2_b_idx'; -- should be i +alter table gidxpart attach partition gidxpart2 for values from (100000) to (199999); -- should fail +update gidxpart2 set b = 5000; +alter table gidxpart attach partition gidxpart2 for values from (100000) to (199999); +select relname, relkind from pg_class where relname = 'gidxpart2_b_idx'; -- should be g +alter table gidxpart detach partition gidxpart2; +drop table gidxpart; +drop table gidxpart2; + -- 2.17.1