Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit a40caf5

Browse files
committed
Preserve clustered index after rewrites with ALTER TABLE
A table rewritten by ALTER TABLE would lose tracking of an index usable for CLUSTER. This setting is tracked by pg_index.indisclustered and is controlled by ALTER TABLE, so some extra work was needed to restore it properly. Note that ALTER TABLE only marks the index that can be used for clustering, and does not do the actual operation. Author: Amit Langote, Justin Pryzby Reviewed-by: Ibrar Ahmed, Michael Paquier Discussion: https://postgr.es/m/20200202161718.GI13621@telsasoft.com Backpatch-through: 9.5
1 parent fc3f445 commit a40caf5

File tree

5 files changed

+143
-0
lines changed

5 files changed

+143
-0
lines changed

src/backend/commands/tablecmds.c

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ typedef struct AlteredTableInfo
177177
List *changedIndexOids; /* OIDs of indexes to rebuild */
178178
List *changedIndexDefs; /* string definitions of same */
179179
char *replicaIdentityIndex; /* index to reset as REPLICA IDENTITY */
180+
char *clusterOnIndex; /* index to use for CLUSTER */
180181
} AlteredTableInfo;
181182

182183
/* Struct describing one new constraint to check in Phase 3 scan */
@@ -11581,6 +11582,21 @@ RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
1158111582
tab->replicaIdentityIndex = get_rel_name(indoid);
1158211583
}
1158311584

11585+
/*
11586+
* Subroutine for ATExecAlterColumnType: remember any clustered index.
11587+
*/
11588+
static void
11589+
RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
11590+
{
11591+
if (!get_index_isclustered(indoid))
11592+
return;
11593+
11594+
if (tab->clusterOnIndex)
11595+
elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
11596+
11597+
tab->clusterOnIndex = get_rel_name(indoid);
11598+
}
11599+
1158411600
/*
1158511601
* Subroutine for ATExecAlterColumnType: remember that a constraint needs
1158611602
* to be rebuilt (which we might already know).
@@ -11606,9 +11622,18 @@ RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab)
1160611622
tab->changedConstraintDefs = lappend(tab->changedConstraintDefs,
1160711623
defstring);
1160811624

11625+
/*
11626+
* For the index of a constraint, if any, remember if it is used for
11627+
* the table's replica identity or if it is a clustered index, so that
11628+
* ATPostAlterTypeCleanup() can queue up commands necessary to restore
11629+
* those properties.
11630+
*/
1160911631
indoid = get_constraint_index(conoid);
1161011632
if (OidIsValid(indoid))
11633+
{
1161111634
RememberReplicaIdentityForRebuilding(indoid, tab);
11635+
RememberClusterOnForRebuilding(indoid, tab);
11636+
}
1161211637
}
1161311638
}
1161411639

@@ -11652,7 +11677,13 @@ RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
1165211677
tab->changedIndexDefs = lappend(tab->changedIndexDefs,
1165311678
defstring);
1165411679

11680+
/*
11681+
* Remember if this index is used for the table's replica identity
11682+
* or if it is a clustered index, so that ATPostAlterTypeCleanup()
11683+
* can queue up commands necessary to restore those properties.
11684+
*/
1165511685
RememberReplicaIdentityForRebuilding(indoid, tab);
11686+
RememberClusterOnForRebuilding(indoid, tab);
1165611687
}
1165711688
}
1165811689
}
@@ -11779,6 +11810,21 @@ ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
1177911810
lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
1178011811
}
1178111812

11813+
/*
11814+
* Queue up command to restore marking of index used for cluster.
11815+
*/
11816+
if (tab->clusterOnIndex)
11817+
{
11818+
AlterTableCmd *cmd = makeNode(AlterTableCmd);
11819+
11820+
cmd->subtype = AT_ClusterOn;
11821+
cmd->name = tab->clusterOnIndex;
11822+
11823+
/* do it after indexes and constraints */
11824+
tab->subcmds[AT_PASS_OLD_CONSTR] =
11825+
lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
11826+
}
11827+
1178211828
/*
1178311829
* It should be okay to use DROP_RESTRICT here, since nothing else should
1178411830
* be depending on these objects.

src/backend/utils/cache/lsyscache.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3334,3 +3334,26 @@ get_index_isvalid(Oid index_oid)
33343334

33353335
return isvalid;
33363336
}
3337+
3338+
/*
3339+
* get_index_isclustered
3340+
*
3341+
* Given the index OID, return pg_index.indisclustered.
3342+
*/
3343+
bool
3344+
get_index_isclustered(Oid index_oid)
3345+
{
3346+
bool isclustered;
3347+
HeapTuple tuple;
3348+
Form_pg_index rd_index;
3349+
3350+
tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(index_oid));
3351+
if (!HeapTupleIsValid(tuple))
3352+
elog(ERROR, "cache lookup failed for index %u", index_oid);
3353+
3354+
rd_index = (Form_pg_index) GETSTRUCT(tuple);
3355+
isclustered = rd_index->indisclustered;
3356+
ReleaseSysCache(tuple);
3357+
3358+
return isclustered;
3359+
}

src/include/utils/lsyscache.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ extern Oid get_range_collation(Oid rangeOid);
185185
extern Oid get_index_column_opclass(Oid index_oid, int attno);
186186
extern bool get_index_isreplident(Oid index_oid);
187187
extern bool get_index_isvalid(Oid index_oid);
188+
extern bool get_index_isclustered(Oid index_oid);
188189

189190
#define type_is_array(typid) (get_element_type(typid) != InvalidOid)
190191
/* type_is_array_domain accepts both plain arrays and domains over arrays */

src/test/regress/expected/alter_table.out

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4302,3 +4302,51 @@ create trigger xtrig
43024302
update bar1 set a = a + 1;
43034303
INFO: a=1, b=1
43044304
/* End test case for bug #16242 */
4305+
-- Test that ALTER TABLE rewrite preserves a clustered index
4306+
-- for normal indexes and indexes on constraints.
4307+
create table alttype_cluster (a int);
4308+
alter table alttype_cluster add primary key (a);
4309+
create index alttype_cluster_ind on alttype_cluster (a);
4310+
alter table alttype_cluster cluster on alttype_cluster_ind;
4311+
-- Normal index remains clustered.
4312+
select indexrelid::regclass, indisclustered from pg_index
4313+
where indrelid = 'alttype_cluster'::regclass
4314+
order by indexrelid::regclass::text;
4315+
indexrelid | indisclustered
4316+
----------------------+----------------
4317+
alttype_cluster_ind | t
4318+
alttype_cluster_pkey | f
4319+
(2 rows)
4320+
4321+
alter table alttype_cluster alter a type bigint;
4322+
select indexrelid::regclass, indisclustered from pg_index
4323+
where indrelid = 'alttype_cluster'::regclass
4324+
order by indexrelid::regclass::text;
4325+
indexrelid | indisclustered
4326+
----------------------+----------------
4327+
alttype_cluster_ind | t
4328+
alttype_cluster_pkey | f
4329+
(2 rows)
4330+
4331+
-- Constraint index remains clustered.
4332+
alter table alttype_cluster cluster on alttype_cluster_pkey;
4333+
select indexrelid::regclass, indisclustered from pg_index
4334+
where indrelid = 'alttype_cluster'::regclass
4335+
order by indexrelid::regclass::text;
4336+
indexrelid | indisclustered
4337+
----------------------+----------------
4338+
alttype_cluster_ind | f
4339+
alttype_cluster_pkey | t
4340+
(2 rows)
4341+
4342+
alter table alttype_cluster alter a type int;
4343+
select indexrelid::regclass, indisclustered from pg_index
4344+
where indrelid = 'alttype_cluster'::regclass
4345+
order by indexrelid::regclass::text;
4346+
indexrelid | indisclustered
4347+
----------------------+----------------
4348+
alttype_cluster_ind | f
4349+
alttype_cluster_pkey | t
4350+
(2 rows)
4351+
4352+
drop table alttype_cluster;

src/test/regress/sql/alter_table.sql

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2847,3 +2847,28 @@ create trigger xtrig
28472847
update bar1 set a = a + 1;
28482848

28492849
/* End test case for bug #16242 */
2850+
2851+
-- Test that ALTER TABLE rewrite preserves a clustered index
2852+
-- for normal indexes and indexes on constraints.
2853+
create table alttype_cluster (a int);
2854+
alter table alttype_cluster add primary key (a);
2855+
create index alttype_cluster_ind on alttype_cluster (a);
2856+
alter table alttype_cluster cluster on alttype_cluster_ind;
2857+
-- Normal index remains clustered.
2858+
select indexrelid::regclass, indisclustered from pg_index
2859+
where indrelid = 'alttype_cluster'::regclass
2860+
order by indexrelid::regclass::text;
2861+
alter table alttype_cluster alter a type bigint;
2862+
select indexrelid::regclass, indisclustered from pg_index
2863+
where indrelid = 'alttype_cluster'::regclass
2864+
order by indexrelid::regclass::text;
2865+
-- Constraint index remains clustered.
2866+
alter table alttype_cluster cluster on alttype_cluster_pkey;
2867+
select indexrelid::regclass, indisclustered from pg_index
2868+
where indrelid = 'alttype_cluster'::regclass
2869+
order by indexrelid::regclass::text;
2870+
alter table alttype_cluster alter a type int;
2871+
select indexrelid::regclass, indisclustered from pg_index
2872+
where indrelid = 'alttype_cluster'::regclass
2873+
order by indexrelid::regclass::text;
2874+
drop table alttype_cluster;

0 commit comments

Comments
 (0)