]> granicus.if.org Git - postgresql/commitdiff
Make sure ALTER TABLE preserves index tablespaces.
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 23 Nov 2016 18:45:56 +0000 (13:45 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 23 Nov 2016 18:45:56 +0000 (13:45 -0500)
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 537e92e41, 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: <1479294998857-5930602.post@n3.nabble.com>

src/backend/utils/adt/ruleutils.c
src/test/regress/input/tablespace.source
src/test/regress/output/tablespace.source

index 8f21eedac4c5b33b72af5c8d5fa08657d17c8e4b..81041c46f188ff656784b79c3ea8d6b8968814dc 100644 (file)
@@ -758,7 +758,7 @@ pg_get_triggerdef_worker(Oid trigid, bool pretty)
  *
  * Note that the SQL-function versions of this omit any info about the
  * index tablespace; this is intentional because pg_dump wants it that way.
- * However pg_get_indexdef_string() includes index tablespace if not default.
+ * However pg_get_indexdef_string() includes the index tablespace.
  * ----------
  */
 Datum
@@ -787,7 +787,11 @@ pg_get_indexdef_ext(PG_FUNCTION_ARGS)
                                                                                                                   prettyFlags)));
 }
 
-/* Internal version that returns a palloc'd C string */
+/*
+ * Internal version for use by ALTER TABLE.
+ * Includes a tablespace clause in the result.
+ * Returns a palloc'd C string; no pretty-printing.
+ */
 char *
 pg_get_indexdef_string(Oid indexrelid)
 {
@@ -1036,20 +1040,19 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
                }
 
                /*
-                * If it's in a nondefault tablespace, say so, but only if requested
+                * Print tablespace, but only if requested
                 */
                if (showTblSpc)
                {
                        Oid                     tblspc;
 
                        tblspc = get_rel_tablespace(indexrelid);
-                       if (OidIsValid(tblspc))
-                       {
-                               if (isConstraint)
-                                       appendStringInfoString(&buf, " USING INDEX");
-                               appendStringInfo(&buf, " TABLESPACE %s",
-                                                         quote_identifier(get_tablespace_name(tblspc)));
-                       }
+                       if (!OidIsValid(tblspc))
+                               tblspc = MyDatabaseTableSpace;
+                       if (isConstraint)
+                               appendStringInfoString(&buf, " USING INDEX");
+                       appendStringInfo(&buf, " TABLESPACE %s",
+                                                        quote_identifier(get_tablespace_name(tblspc)));
                }
 
                /*
index dba96f4547fa34424ee76dccaa676e8a21f0c7c6..ecc47124a5d49e4b5e01711149cd3e643a82cc11 100644 (file)
@@ -34,6 +34,37 @@ CREATE INDEX foo_idx on testschema.foo(i) TABLESPACE testspace;
 SELECT relname, spcname FROM pg_catalog.pg_tablespace t, pg_catalog.pg_class c
     where c.reltablespace = t.oid AND c.relname = 'foo_idx';
 
+-- check that default_tablespace doesn't affect ALTER TABLE index rebuilds
+CREATE TABLE testschema.test_default_tab(id bigint) TABLESPACE testspace;
+INSERT INTO testschema.test_default_tab VALUES (1);
+CREATE INDEX test_index1 on testschema.test_default_tab (id);
+CREATE INDEX test_index2 on testschema.test_default_tab (id) TABLESPACE testspace;
+\d testschema.test_index1
+\d testschema.test_index2
+-- use a custom tablespace for default_tablespace
+SET default_tablespace TO testspace;
+-- tablespace should not change if no rewrite
+ALTER TABLE testschema.test_default_tab ALTER id TYPE bigint;
+\d testschema.test_index1
+\d testschema.test_index2
+SELECT * FROM testschema.test_default_tab;
+-- tablespace should not change even if there is an index rewrite
+ALTER TABLE testschema.test_default_tab ALTER id TYPE int;
+\d testschema.test_index1
+\d testschema.test_index2
+SELECT * FROM testschema.test_default_tab;
+-- now use the default tablespace for default_tablespace
+SET default_tablespace TO '';
+-- tablespace should not change if no rewrite
+ALTER TABLE testschema.test_default_tab ALTER id TYPE int;
+\d testschema.test_index1
+\d testschema.test_index2
+-- tablespace should not change even if there is an index rewrite
+ALTER TABLE testschema.test_default_tab ALTER id TYPE bigint;
+\d testschema.test_index1
+\d testschema.test_index2
+DROP TABLE testschema.test_default_tab;
+
 -- let's try moving a table from one place to another
 CREATE TABLE testschema.atable AS VALUES (1), (2);
 CREATE UNIQUE INDEX anindex ON testschema.atable(column1);
index 1260c968880df44aa0698f1415e0be5265a7df7e..7b63ad872a43f2744991464807e328f733ea8ccc 100644 (file)
@@ -48,6 +48,111 @@ SELECT relname, spcname FROM pg_catalog.pg_tablespace t, pg_catalog.pg_class c
  foo_idx | testspace
 (1 row)
 
+-- check that default_tablespace doesn't affect ALTER TABLE index rebuilds
+CREATE TABLE testschema.test_default_tab(id bigint) TABLESPACE testspace;
+INSERT INTO testschema.test_default_tab VALUES (1);
+CREATE INDEX test_index1 on testschema.test_default_tab (id);
+CREATE INDEX test_index2 on testschema.test_default_tab (id) TABLESPACE testspace;
+\d testschema.test_index1
+Index "testschema.test_index1"
+ Column |  Type  | Definition 
+--------+--------+------------
+ id     | bigint | id
+btree, for table "testschema.test_default_tab"
+
+\d testschema.test_index2
+Index "testschema.test_index2"
+ Column |  Type  | Definition 
+--------+--------+------------
+ id     | bigint | id
+btree, for table "testschema.test_default_tab"
+Tablespace: "testspace"
+
+-- use a custom tablespace for default_tablespace
+SET default_tablespace TO testspace;
+-- tablespace should not change if no rewrite
+ALTER TABLE testschema.test_default_tab ALTER id TYPE bigint;
+\d testschema.test_index1
+Index "testschema.test_index1"
+ Column |  Type  | Definition 
+--------+--------+------------
+ id     | bigint | id
+btree, for table "testschema.test_default_tab"
+
+\d testschema.test_index2
+Index "testschema.test_index2"
+ Column |  Type  | Definition 
+--------+--------+------------
+ id     | bigint | id
+btree, for table "testschema.test_default_tab"
+Tablespace: "testspace"
+
+SELECT * FROM testschema.test_default_tab;
+ id 
+----
+  1
+(1 row)
+
+-- tablespace should not change even if there is an index rewrite
+ALTER TABLE testschema.test_default_tab ALTER id TYPE int;
+\d testschema.test_index1
+Index "testschema.test_index1"
+ Column |  Type   | Definition 
+--------+---------+------------
+ id     | integer | id
+btree, for table "testschema.test_default_tab"
+
+\d testschema.test_index2
+Index "testschema.test_index2"
+ Column |  Type   | Definition 
+--------+---------+------------
+ id     | integer | id
+btree, for table "testschema.test_default_tab"
+Tablespace: "testspace"
+
+SELECT * FROM testschema.test_default_tab;
+ id 
+----
+  1
+(1 row)
+
+-- now use the default tablespace for default_tablespace
+SET default_tablespace TO '';
+-- tablespace should not change if no rewrite
+ALTER TABLE testschema.test_default_tab ALTER id TYPE int;
+\d testschema.test_index1
+Index "testschema.test_index1"
+ Column |  Type   | Definition 
+--------+---------+------------
+ id     | integer | id
+btree, for table "testschema.test_default_tab"
+
+\d testschema.test_index2
+Index "testschema.test_index2"
+ Column |  Type   | Definition 
+--------+---------+------------
+ id     | integer | id
+btree, for table "testschema.test_default_tab"
+Tablespace: "testspace"
+
+-- tablespace should not change even if there is an index rewrite
+ALTER TABLE testschema.test_default_tab ALTER id TYPE bigint;
+\d testschema.test_index1
+Index "testschema.test_index1"
+ Column |  Type  | Definition 
+--------+--------+------------
+ id     | bigint | id
+btree, for table "testschema.test_default_tab"
+
+\d testschema.test_index2
+Index "testschema.test_index2"
+ Column |  Type  | Definition 
+--------+--------+------------
+ id     | bigint | id
+btree, for table "testschema.test_default_tab"
+Tablespace: "testspace"
+
+DROP TABLE testschema.test_default_tab;
 -- let's try moving a table from one place to another
 CREATE TABLE testschema.atable AS VALUES (1), (2);
 CREATE UNIQUE INDEX anindex ON testschema.atable(column1);