Skip to content

Commit 33e6c34

Browse files
committed
Fix tablespace handling for partitioned indexes
When creating partitioned indexes, the tablespace was not being saved for the parent index. This meant that subsequently created partitions would not use the right tablespace for their indexes. ALTER INDEX SET TABLESPACE and ALTER INDEX ALL IN TABLESPACE raised errors when tried; fix them too. This requires bespoke code for ATExecCmd() that applies to the special case when the tablespace move is just a catalog change. Discussion: https://postgr.es/m/[email protected]
1 parent 0e88ba1 commit 33e6c34

File tree

4 files changed

+94
-5
lines changed

4 files changed

+94
-5
lines changed

src/backend/catalog/heap.c

+9-1
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,6 @@ heap_create(const char *relname,
297297
case RELKIND_COMPOSITE_TYPE:
298298
case RELKIND_FOREIGN_TABLE:
299299
case RELKIND_PARTITIONED_TABLE:
300-
case RELKIND_PARTITIONED_INDEX:
301300
create_storage = false;
302301

303302
/*
@@ -306,6 +305,15 @@ heap_create(const char *relname,
306305
*/
307306
reltablespace = InvalidOid;
308307
break;
308+
309+
case RELKIND_PARTITIONED_INDEX:
310+
/*
311+
* Preserve tablespace so that it's used as tablespace for indexes
312+
* on future partitions.
313+
*/
314+
create_storage = false;
315+
break;
316+
309317
case RELKIND_SEQUENCE:
310318
create_storage = true;
311319

src/backend/commands/tablecmds.c

+57-3
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,7 @@ static bool ATPrepChangePersistence(Relation rel, bool toLogged);
446446
static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
447447
const char *tablespacename, LOCKMODE lockmode);
448448
static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
449+
static void ATExecPartedIdxSetTableSpace(Relation rel, Oid newTableSpace);
449450
static void ATExecSetRelOptions(Relation rel, List *defList,
450451
AlterTableType operation,
451452
LOCKMODE lockmode);
@@ -3845,7 +3846,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
38453846
pass = AT_PASS_DROP;
38463847
break;
38473848
case AT_SetTableSpace: /* SET TABLESPACE */
3848-
ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX);
3849+
ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX |
3850+
ATT_PARTITIONED_INDEX);
38493851
/* This command never recurses */
38503852
ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
38513853
pass = AT_PASS_MISC; /* doesn't actually matter */
@@ -4181,10 +4183,13 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
41814183
*/
41824184
break;
41834185
case AT_SetTableSpace: /* SET TABLESPACE */
4184-
41854186
/*
4186-
* Nothing to do here; Phase 3 does the work
4187+
* Only do this for partitioned indexes, for which this is just
4188+
* a catalog change. Other relation types are handled by Phase 3.
41874189
*/
4190+
if (rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
4191+
ATExecPartedIdxSetTableSpace(rel, tab->newTableSpace);
4192+
41884193
break;
41894194
case AT_SetRelOptions: /* SET (...) */
41904195
case AT_ResetRelOptions: /* RESET (...) */
@@ -11049,6 +11054,55 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
1104911054
list_free(reltoastidxids);
1105011055
}
1105111056

11057+
/*
11058+
* Special handling of ALTER TABLE SET TABLESPACE for partitioned indexes,
11059+
* which have no storage (so not handled in Phase 3 like other relation types)
11060+
*/
11061+
static void
11062+
ATExecPartedIdxSetTableSpace(Relation rel, Oid newTableSpace)
11063+
{
11064+
HeapTuple tuple;
11065+
Oid oldTableSpace;
11066+
Relation pg_class;
11067+
Form_pg_class rd_rel;
11068+
Oid indexOid = RelationGetRelid(rel);
11069+
11070+
Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
11071+
11072+
/*
11073+
* No work if no change in tablespace.
11074+
*/
11075+
oldTableSpace = rel->rd_rel->reltablespace;
11076+
if (newTableSpace == oldTableSpace ||
11077+
(newTableSpace == MyDatabaseTableSpace && oldTableSpace == 0))
11078+
{
11079+
InvokeObjectPostAlterHook(RelationRelationId,
11080+
indexOid, 0);
11081+
return;
11082+
}
11083+
11084+
/* Get a modifiable copy of the relation's pg_class row */
11085+
pg_class = heap_open(RelationRelationId, RowExclusiveLock);
11086+
11087+
tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(indexOid));
11088+
if (!HeapTupleIsValid(tuple))
11089+
elog(ERROR, "cache lookup failed for relation %u", indexOid);
11090+
rd_rel = (Form_pg_class) GETSTRUCT(tuple);
11091+
11092+
/* update the pg_class row */
11093+
rd_rel->reltablespace = (newTableSpace == MyDatabaseTableSpace) ? InvalidOid : newTableSpace;
11094+
CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
11095+
11096+
InvokeObjectPostAlterHook(RelationRelationId, indexOid, 0);
11097+
11098+
heap_freetuple(tuple);
11099+
11100+
heap_close(pg_class, RowExclusiveLock);
11101+
11102+
/* Make sure the reltablespace change is visible */
11103+
CommandCounterIncrement();
11104+
}
11105+
1105211106
/*
1105311107
* Alter Table ALL ... SET TABLESPACE
1105411108
*

src/test/regress/input/tablespace.source

+10
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,14 @@ CREATE INDEX foo_idx on testschema.foo(i) TABLESPACE regress_tblspace;
4444
SELECT relname, spcname FROM pg_catalog.pg_tablespace t, pg_catalog.pg_class c
4545
where c.reltablespace = t.oid AND c.relname = 'foo_idx';
4646

47+
-- partitioned index
48+
CREATE TABLE testschema.part (a int) PARTITION BY LIST (a);
49+
CREATE TABLE testschema.part1 PARTITION OF testschema.part FOR VALUES IN (1);
50+
CREATE INDEX part_a_idx ON testschema.part (a) TABLESPACE regress_tblspace;
51+
CREATE TABLE testschema.part2 PARTITION OF testschema.part FOR VALUES IN (2);
52+
SELECT relname, spcname FROM pg_catalog.pg_tablespace t, pg_catalog.pg_class c
53+
where c.reltablespace = t.oid AND c.relname LIKE 'part%_idx';
54+
4755
-- check that default_tablespace doesn't affect ALTER TABLE index rebuilds
4856
CREATE TABLE testschema.test_default_tab(id bigint) TABLESPACE regress_tblspace;
4957
INSERT INTO testschema.test_default_tab VALUES (1);
@@ -93,6 +101,8 @@ CREATE UNIQUE INDEX anindex ON testschema.atable(column1);
93101

94102
ALTER TABLE testschema.atable SET TABLESPACE regress_tblspace;
95103
ALTER INDEX testschema.anindex SET TABLESPACE regress_tblspace;
104+
ALTER INDEX testschema.part_a_idx SET TABLESPACE pg_default;
105+
ALTER INDEX testschema.part_a_idx SET TABLESPACE regress_tblspace;
96106

97107
INSERT INTO testschema.atable VALUES(3); -- ok
98108
INSERT INTO testschema.atable VALUES(1); -- fail (checks index)

src/test/regress/output/tablespace.source

+18-1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,20 @@ SELECT relname, spcname FROM pg_catalog.pg_tablespace t, pg_catalog.pg_class c
6161
foo_idx | regress_tblspace
6262
(1 row)
6363

64+
-- partitioned index
65+
CREATE TABLE testschema.part (a int) PARTITION BY LIST (a);
66+
CREATE TABLE testschema.part1 PARTITION OF testschema.part FOR VALUES IN (1);
67+
CREATE INDEX part_a_idx ON testschema.part (a) TABLESPACE regress_tblspace;
68+
CREATE TABLE testschema.part2 PARTITION OF testschema.part FOR VALUES IN (2);
69+
SELECT relname, spcname FROM pg_catalog.pg_tablespace t, pg_catalog.pg_class c
70+
where c.reltablespace = t.oid AND c.relname LIKE 'part%_idx';
71+
relname | spcname
72+
-------------+------------------
73+
part1_a_idx | regress_tblspace
74+
part2_a_idx | regress_tblspace
75+
part_a_idx | regress_tblspace
76+
(3 rows)
77+
6478
-- check that default_tablespace doesn't affect ALTER TABLE index rebuilds
6579
CREATE TABLE testschema.test_default_tab(id bigint) TABLESPACE regress_tblspace;
6680
INSERT INTO testschema.test_default_tab VALUES (1);
@@ -200,6 +214,8 @@ CREATE TABLE testschema.atable AS VALUES (1), (2);
200214
CREATE UNIQUE INDEX anindex ON testschema.atable(column1);
201215
ALTER TABLE testschema.atable SET TABLESPACE regress_tblspace;
202216
ALTER INDEX testschema.anindex SET TABLESPACE regress_tblspace;
217+
ALTER INDEX testschema.part_a_idx SET TABLESPACE pg_default;
218+
ALTER INDEX testschema.part_a_idx SET TABLESPACE regress_tblspace;
203219
INSERT INTO testschema.atable VALUES(3); -- ok
204220
INSERT INTO testschema.atable VALUES(1); -- fail (checks index)
205221
ERROR: duplicate key value violates unique constraint "anindex"
@@ -241,10 +257,11 @@ NOTICE: no matching relations in tablespace "regress_tblspace_renamed" found
241257
-- Should succeed
242258
DROP TABLESPACE regress_tblspace_renamed;
243259
DROP SCHEMA testschema CASCADE;
244-
NOTICE: drop cascades to 5 other objects
260+
NOTICE: drop cascades to 6 other objects
245261
DETAIL: drop cascades to table testschema.foo
246262
drop cascades to table testschema.asselect
247263
drop cascades to table testschema.asexecute
264+
drop cascades to table testschema.part
248265
drop cascades to table testschema.atable
249266
drop cascades to table testschema.tablespace_acl
250267
DROP ROLE regress_tablespace_user1;

0 commit comments

Comments
 (0)