Skip to content

Commit 4a5e1d3

Browse files
committed
Make sure ALTER TABLE preserves index tablespaces.
When rebuilding an existing index, ALTER TABLE correctly kept the physical file in the same tablespace, but it messed up the pg_class entry if the index had been in the database's default tablespace and "default_tablespace" was set to some non-default tablespace. This led to an inaccessible index. Fix by fixing pg_get_indexdef_string() to always include a tablespace clause, whether or not the index is in the default tablespace. The previous behavior was installed in commit 537e92e, and I think it just wasn't thought through very clearly; certainly the possible effect of default_tablespace wasn't considered. There's some risk in changing the behavior of this function, but there are no other call sites in the core code. Even if it's being used by some third party extension, it's fairly hard to envision a usage that is okay with a tablespace clause being appended some of the time but can't handle it being appended all the time. Back-patch to all supported versions. Code fix by me, investigation and test cases by Michael Paquier. Discussion: <[email protected]>
1 parent 51aebcd commit 4a5e1d3

File tree

3 files changed

+149
-10
lines changed

3 files changed

+149
-10
lines changed

src/backend/utils/adt/ruleutils.c

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1038,7 +1038,7 @@ pg_get_triggerdef_worker(Oid trigid, bool pretty)
10381038
*
10391039
* Note that the SQL-function versions of this omit any info about the
10401040
* index tablespace; this is intentional because pg_dump wants it that way.
1041-
* However pg_get_indexdef_string() includes index tablespace if not default.
1041+
* However pg_get_indexdef_string() includes the index tablespace.
10421042
* ----------
10431043
*/
10441044
Datum
@@ -1079,7 +1079,11 @@ pg_get_indexdef_ext(PG_FUNCTION_ARGS)
10791079
PG_RETURN_TEXT_P(string_to_text(res));
10801080
}
10811081

1082-
/* Internal version that returns a palloc'd C string; no pretty-printing */
1082+
/*
1083+
* Internal version for use by ALTER TABLE.
1084+
* Includes a tablespace clause in the result.
1085+
* Returns a palloc'd C string; no pretty-printing.
1086+
*/
10831087
char *
10841088
pg_get_indexdef_string(Oid indexrelid)
10851089
{
@@ -1337,20 +1341,19 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
13371341
}
13381342

13391343
/*
1340-
* If it's in a nondefault tablespace, say so, but only if requested
1344+
* Print tablespace, but only if requested
13411345
*/
13421346
if (showTblSpc)
13431347
{
13441348
Oid tblspc;
13451349

13461350
tblspc = get_rel_tablespace(indexrelid);
1347-
if (OidIsValid(tblspc))
1348-
{
1349-
if (isConstraint)
1350-
appendStringInfoString(&buf, " USING INDEX");
1351-
appendStringInfo(&buf, " TABLESPACE %s",
1352-
quote_identifier(get_tablespace_name(tblspc)));
1353-
}
1351+
if (!OidIsValid(tblspc))
1352+
tblspc = MyDatabaseTableSpace;
1353+
if (isConstraint)
1354+
appendStringInfoString(&buf, " USING INDEX");
1355+
appendStringInfo(&buf, " TABLESPACE %s",
1356+
quote_identifier(get_tablespace_name(tblspc)));
13541357
}
13551358

13561359
/*

src/test/regress/input/tablespace.source

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,37 @@ 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+
-- check that default_tablespace doesn't affect ALTER TABLE index rebuilds
48+
CREATE TABLE testschema.test_default_tab(id bigint) TABLESPACE regress_tblspace;
49+
INSERT INTO testschema.test_default_tab VALUES (1);
50+
CREATE INDEX test_index1 on testschema.test_default_tab (id);
51+
CREATE INDEX test_index2 on testschema.test_default_tab (id) TABLESPACE regress_tblspace;
52+
\d testschema.test_index1
53+
\d testschema.test_index2
54+
-- use a custom tablespace for default_tablespace
55+
SET default_tablespace TO regress_tblspace;
56+
-- tablespace should not change if no rewrite
57+
ALTER TABLE testschema.test_default_tab ALTER id TYPE bigint;
58+
\d testschema.test_index1
59+
\d testschema.test_index2
60+
SELECT * FROM testschema.test_default_tab;
61+
-- tablespace should not change even if there is an index rewrite
62+
ALTER TABLE testschema.test_default_tab ALTER id TYPE int;
63+
\d testschema.test_index1
64+
\d testschema.test_index2
65+
SELECT * FROM testschema.test_default_tab;
66+
-- now use the default tablespace for default_tablespace
67+
SET default_tablespace TO '';
68+
-- tablespace should not change if no rewrite
69+
ALTER TABLE testschema.test_default_tab ALTER id TYPE int;
70+
\d testschema.test_index1
71+
\d testschema.test_index2
72+
-- tablespace should not change even if there is an index rewrite
73+
ALTER TABLE testschema.test_default_tab ALTER id TYPE bigint;
74+
\d testschema.test_index1
75+
\d testschema.test_index2
76+
DROP TABLE testschema.test_default_tab;
77+
4778
-- let's try moving a table from one place to another
4879
CREATE TABLE testschema.atable AS VALUES (1), (2);
4980
CREATE UNIQUE INDEX anindex ON testschema.atable(column1);

src/test/regress/output/tablespace.source

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,111 @@ SELECT relname, spcname FROM pg_catalog.pg_tablespace t, pg_catalog.pg_class c
6161
foo_idx | regress_tblspace
6262
(1 row)
6363

64+
-- check that default_tablespace doesn't affect ALTER TABLE index rebuilds
65+
CREATE TABLE testschema.test_default_tab(id bigint) TABLESPACE regress_tblspace;
66+
INSERT INTO testschema.test_default_tab VALUES (1);
67+
CREATE INDEX test_index1 on testschema.test_default_tab (id);
68+
CREATE INDEX test_index2 on testschema.test_default_tab (id) TABLESPACE regress_tblspace;
69+
\d testschema.test_index1
70+
Index "testschema.test_index1"
71+
Column | Type | Definition
72+
--------+--------+------------
73+
id | bigint | id
74+
btree, for table "testschema.test_default_tab"
75+
76+
\d testschema.test_index2
77+
Index "testschema.test_index2"
78+
Column | Type | Definition
79+
--------+--------+------------
80+
id | bigint | id
81+
btree, for table "testschema.test_default_tab"
82+
Tablespace: "regress_tblspace"
83+
84+
-- use a custom tablespace for default_tablespace
85+
SET default_tablespace TO regress_tblspace;
86+
-- tablespace should not change if no rewrite
87+
ALTER TABLE testschema.test_default_tab ALTER id TYPE bigint;
88+
\d testschema.test_index1
89+
Index "testschema.test_index1"
90+
Column | Type | Definition
91+
--------+--------+------------
92+
id | bigint | id
93+
btree, for table "testschema.test_default_tab"
94+
95+
\d testschema.test_index2
96+
Index "testschema.test_index2"
97+
Column | Type | Definition
98+
--------+--------+------------
99+
id | bigint | id
100+
btree, for table "testschema.test_default_tab"
101+
Tablespace: "regress_tblspace"
102+
103+
SELECT * FROM testschema.test_default_tab;
104+
id
105+
----
106+
1
107+
(1 row)
108+
109+
-- tablespace should not change even if there is an index rewrite
110+
ALTER TABLE testschema.test_default_tab ALTER id TYPE int;
111+
\d testschema.test_index1
112+
Index "testschema.test_index1"
113+
Column | Type | Definition
114+
--------+---------+------------
115+
id | integer | id
116+
btree, for table "testschema.test_default_tab"
117+
118+
\d testschema.test_index2
119+
Index "testschema.test_index2"
120+
Column | Type | Definition
121+
--------+---------+------------
122+
id | integer | id
123+
btree, for table "testschema.test_default_tab"
124+
Tablespace: "regress_tblspace"
125+
126+
SELECT * FROM testschema.test_default_tab;
127+
id
128+
----
129+
1
130+
(1 row)
131+
132+
-- now use the default tablespace for default_tablespace
133+
SET default_tablespace TO '';
134+
-- tablespace should not change if no rewrite
135+
ALTER TABLE testschema.test_default_tab ALTER id TYPE int;
136+
\d testschema.test_index1
137+
Index "testschema.test_index1"
138+
Column | Type | Definition
139+
--------+---------+------------
140+
id | integer | id
141+
btree, for table "testschema.test_default_tab"
142+
143+
\d testschema.test_index2
144+
Index "testschema.test_index2"
145+
Column | Type | Definition
146+
--------+---------+------------
147+
id | integer | id
148+
btree, for table "testschema.test_default_tab"
149+
Tablespace: "regress_tblspace"
150+
151+
-- tablespace should not change even if there is an index rewrite
152+
ALTER TABLE testschema.test_default_tab ALTER id TYPE bigint;
153+
\d testschema.test_index1
154+
Index "testschema.test_index1"
155+
Column | Type | Definition
156+
--------+--------+------------
157+
id | bigint | id
158+
btree, for table "testschema.test_default_tab"
159+
160+
\d testschema.test_index2
161+
Index "testschema.test_index2"
162+
Column | Type | Definition
163+
--------+--------+------------
164+
id | bigint | id
165+
btree, for table "testschema.test_default_tab"
166+
Tablespace: "regress_tblspace"
167+
168+
DROP TABLE testschema.test_default_tab;
64169
-- let's try moving a table from one place to another
65170
CREATE TABLE testschema.atable AS VALUES (1), (2);
66171
CREATE UNIQUE INDEX anindex ON testschema.atable(column1);

0 commit comments

Comments
 (0)