Fix ALTER COLUMN TYPE to preserve the tablespace and reloptions of indexes
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 13 Oct 2007 15:55:49 +0000 (15:55 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 13 Oct 2007 15:55:49 +0000 (15:55 +0000)
it affects.  The original coding neglected tablespace entirely (causing
the indexes to move to the database's default tablespace) and for an index
belonging to a UNIQUE or PRIMARY KEY constraint, it would actually try to
assign the parent table's reloptions to the index :-(.  Per bug #3672 and
subsequent investigation.

8.0 and 8.1 did not have reloptions, but the tablespace bug is present.

src/backend/utils/adt/ruleutils.c
src/backend/utils/cache/lsyscache.c
src/include/utils/lsyscache.h

index 7b57c5bb11cb8aeb1184b14ca0c89b5831c936b8..b7c3d50767d962749f78c4bab0432b79fe34dfc1 100644 (file)
@@ -2,7 +2,7 @@
  * ruleutils.c - Functions to convert stored expressions/querytrees
  *             back to source text
  *
- *   $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.235.2.2 2007/01/30 02:32:05 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.235.2.3 2007/10/13 15:55:49 tgl Exp $
  **********************************************************************/
 
 #include "postgres.h"
@@ -19,6 +19,7 @@
 #include "catalog/pg_opclass.h"
 #include "catalog/pg_operator.h"
 #include "catalog/pg_trigger.h"
+#include "commands/tablespace.h"
 #include "executor/spi.h"
 #include "funcapi.h"
 #include "nodes/makefuncs.h"
@@ -119,12 +120,13 @@ static char *pg_get_viewdef_worker(Oid viewoid, int prettyFlags);
 static void decompile_column_index_array(Datum column_index_array, Oid relId,
                             StringInfo buf);
 static char *pg_get_ruledef_worker(Oid ruleoid, int prettyFlags);
-static char *pg_get_indexdef_worker(Oid indexrelid, int colno,
+static char *pg_get_indexdef_worker(Oid indexrelid, int colno, bool showTblSpc,
                       int prettyFlags);
 static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
                            int prettyFlags);
 static char *pg_get_expr_worker(text *expr, Oid relid, char *relname,
                   int prettyFlags);
+static Oid get_constraint_index(Oid constraintId);
 static void make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
             int prettyFlags);
 static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
@@ -567,6 +569,10 @@ pg_get_triggerdef(PG_FUNCTION_ARGS)
  * In the extended version, there is a colno argument as well as pretty bool.
  * if colno == 0, we want a complete index definition.
  * if colno > 0, we only want the Nth index key's variable or expression.
+ *
+ * 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.
  * ----------
  */
 Datum
@@ -574,7 +580,8 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
 {
    Oid         indexrelid = PG_GETARG_OID(0);
 
-   PG_RETURN_TEXT_P(string_to_text(pg_get_indexdef_worker(indexrelid, 0, 0)));
+   PG_RETURN_TEXT_P(string_to_text(pg_get_indexdef_worker(indexrelid, 0,
+                                                          false, 0)));
 }
 
 Datum
@@ -586,18 +593,20 @@ pg_get_indexdef_ext(PG_FUNCTION_ARGS)
    int         prettyFlags;
 
    prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : 0;
-   PG_RETURN_TEXT_P(string_to_text(pg_get_indexdef_worker(indexrelid, colno, prettyFlags)));
+   PG_RETURN_TEXT_P(string_to_text(pg_get_indexdef_worker(indexrelid, colno,
+                                                        false, prettyFlags)));
 }
 
 /* Internal version that returns a palloc'd C string */
 char *
 pg_get_indexdef_string(Oid indexrelid)
 {
-   return pg_get_indexdef_worker(indexrelid, 0, 0);
+   return pg_get_indexdef_worker(indexrelid, 0, true, 0);
 }
 
 static char *
-pg_get_indexdef_worker(Oid indexrelid, int colno, int prettyFlags)
+pg_get_indexdef_worker(Oid indexrelid, int colno, bool showTblSpc,
+                      int prettyFlags)
 {
    HeapTuple   ht_idx;
    HeapTuple   ht_idxrel;
@@ -766,8 +775,17 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, int prettyFlags)
        }
 
        /*
-        * XXX we don't include the tablespace ... this is for pg_dump
+        * If it's in a nondefault tablespace, say so, but only if requested
         */
+       if (showTblSpc)
+       {
+           Oid         tblspc;
+
+           tblspc = get_rel_tablespace(indexrelid);
+           if (OidIsValid(tblspc))
+               appendStringInfo(&buf, " TABLESPACE %s",
+                                quote_identifier(get_tablespace_name(tblspc)));
+       }
 
        /*
         * If it's a partial index, decompile and append the predicate
@@ -997,6 +1015,7 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
            {
                Datum       val;
                bool        isnull;
+               Oid         indexId;
 
                /* Start off the constraint definition */
                if (conForm->contype == CONSTRAINT_PRIMARY)
@@ -1015,15 +1034,24 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
 
                appendStringInfo(&buf, ")");
 
-               if (fullCommand && OidIsValid(conForm->conrelid))
+               indexId = get_constraint_index(constraintId);
+
+               /* XXX why do we only print these bits if fullCommand? */
+               if (fullCommand && OidIsValid(indexId))
                {
-                   char       *options = flatten_reloptions(conForm->conrelid);
+                   char       *options = flatten_reloptions(indexId);
+                   Oid         tblspc;
 
                    if (options)
                    {
                        appendStringInfo(&buf, " WITH (%s)", options);
                        pfree(options);
                    }
+
+                   tblspc = get_rel_tablespace(indexId);
+                   if (OidIsValid(tblspc))
+                       appendStringInfo(&buf, " USING INDEX TABLESPACE %s",
+                                        quote_identifier(get_tablespace_name(tblspc)));
                }
 
                break;
@@ -1340,7 +1368,69 @@ pg_get_serial_sequence(PG_FUNCTION_ARGS)
 }
 
 
-/* ----------
+/*
+ * get_constraint_index
+ *     Given the OID of a unique or primary-key constraint, return the
+ *     OID of the underlying unique index.
+ *
+ * Return InvalidOid if the index couldn't be found; this suggests the
+ * given OID is bogus, but we leave it to caller to decide what to do.
+ */
+static Oid
+get_constraint_index(Oid constraintId)
+{
+   Oid         indexId = InvalidOid;
+   Relation    depRel;
+   ScanKeyData key[3];
+   SysScanDesc scan;
+   HeapTuple   tup;
+
+   /* Search the dependency table for the dependent index */
+   depRel = heap_open(DependRelationId, AccessShareLock);
+
+   ScanKeyInit(&key[0],
+               Anum_pg_depend_refclassid,
+               BTEqualStrategyNumber, F_OIDEQ,
+               ObjectIdGetDatum(ConstraintRelationId));
+   ScanKeyInit(&key[1],
+               Anum_pg_depend_refobjid,
+               BTEqualStrategyNumber, F_OIDEQ,
+               ObjectIdGetDatum(constraintId));
+   ScanKeyInit(&key[2],
+               Anum_pg_depend_refobjsubid,
+               BTEqualStrategyNumber, F_INT4EQ,
+               Int32GetDatum(0));
+
+   scan = systable_beginscan(depRel, DependReferenceIndexId, true,
+                             SnapshotNow, 3, key);
+
+   while (HeapTupleIsValid(tup = systable_getnext(scan)))
+   {
+       Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
+
+       /*
+        * We assume any internal dependency of an index on the constraint
+        * must be what we are looking for.  (The relkind test is just
+        * paranoia; there shouldn't be any such dependencies otherwise.)
+        */
+       if (deprec->classid == RelationRelationId &&
+           deprec->objsubid == 0 &&
+           deprec->deptype == DEPENDENCY_INTERNAL &&
+           get_rel_relkind(deprec->objid) == RELKIND_INDEX)
+       {
+           indexId = deprec->objid;
+           break;
+       }
+   }
+
+   systable_endscan(scan);
+   heap_close(depRel, AccessShareLock);
+
+   return indexId;
+}
+
+
+/*
  * deparse_expression          - General utility for deparsing expressions
  *
  * calls deparse_expression_pretty with all prettyPrinting disabled
index 99dd84a9fa7c9cf5fa4be8ef1ecff5714b5a55ce..350d9661f56081b1d8633a03100bfe2d53497d31 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.138.2.1 2007/03/19 16:30:40 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.138.2.2 2007/10/13 15:55:49 tgl Exp $
  *
  * NOTES
  *   Eventually, the index information should go through here, too.
@@ -1210,6 +1210,35 @@ get_rel_relkind(Oid relid)
        return '\0';
 }
 
+/*
+ * get_rel_tablespace
+ *
+ *     Returns the pg_tablespace OID associated with a given relation.
+ *
+ * Note: InvalidOid might mean either that we couldn't find the relation,
+ * or that it is in the database's default tablespace.
+ */
+Oid
+get_rel_tablespace(Oid relid)
+{
+   HeapTuple   tp;
+
+   tp = SearchSysCache(RELOID,
+                       ObjectIdGetDatum(relid),
+                       0, 0, 0);
+   if (HeapTupleIsValid(tp))
+   {
+       Form_pg_class reltup = (Form_pg_class) GETSTRUCT(tp);
+       Oid         result;
+
+       result = reltup->reltablespace;
+       ReleaseSysCache(tp);
+       return result;
+   }
+   else
+       return InvalidOid;
+}
+
 
 /*             ---------- TYPE CACHE ----------                         */
 
index 69eb31bcc559d7fda7a3ff530f439853d9e1a637..26dc09b8ada3d295fe4ad166f2b1945b886bf981 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/lsyscache.h,v 1.107 2006/10/04 00:30:10 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/utils/lsyscache.h,v 1.107.2.1 2007/10/13 15:55:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -72,6 +72,7 @@ extern char *get_rel_name(Oid relid);
 extern Oid get_rel_namespace(Oid relid);
 extern Oid get_rel_type_id(Oid relid);
 extern char get_rel_relkind(Oid relid);
+extern Oid get_rel_tablespace(Oid relid);
 extern bool get_typisdefined(Oid typid);
 extern int16 get_typlen(Oid typid);
 extern bool get_typbyval(Oid typid);