]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
pg_upgrade: prevent oid conflicts with new-cluster TOAST tables
authorBruce Momjian <bruce@momjian.us>
Thu, 7 Aug 2014 18:56:13 +0000 (14:56 -0400)
committerBruce Momjian <bruce@momjian.us>
Thu, 7 Aug 2014 18:56:13 +0000 (14:56 -0400)
Previously, TOAST tables only required in the new cluster could cause
oid conflicts if they were auto-numbered and a later conflicting oid had
to be assigned.

Backpatch through 9.3

contrib/pg_upgrade/dump.c
contrib/pg_upgrade/pg_upgrade.c
contrib/pg_upgrade/pg_upgrade.h
src/backend/catalog/toasting.c

index d03d2aa4e758c505f91508ff1ea5c2467827d691..943ced96aef0e60d6d5b34c62ce82ab8cfc20e9f 100644 (file)
 #include "pg_upgrade.h"
 
 #include <sys/types.h>
+#include "catalog/pg_authid.h"
+
+/* pick a OID that will never be used for TOAST tables */
+#define OPTIONALLY_CREATE_TOAST_OID   BOOTSTRAP_SUPERUSERID
+
 
 void
 generate_old_dump(void)
@@ -67,3 +72,71 @@ generate_old_dump(void)
        end_progress_output();
        check_ok();
 }
+
+
+/*
+ * It is possible for there to be a mismatch in the need for TOAST tables
+ * between the old and new servers, e.g. some pre-9.1 tables didn't need
+ * TOAST tables but will need them in 9.1+.  (There are also opposite cases,
+ * but these are handled by setting binary_upgrade_next_toast_pg_class_oid.)
+ *
+ * We can't allow the TOAST table to be created by pg_dump with a
+ * pg_dump-assigned oid because it might conflict with a later table that
+ * uses that oid, causing a "file exists" error for pg_class conflicts, and
+ * a "duplicate oid" error for pg_type conflicts.  (TOAST tables need pg_type
+ * entries.)
+ *
+ * Therefore, a backend in binary-upgrade mode will not create a TOAST
+ * table unless an OID as passed in via pg_upgrade_support functions.
+ * This function is called after the restore and uses ALTER TABLE to
+ * auto-create any needed TOAST tables which will not conflict with
+ * restored oids.
+ */
+void
+optionally_create_toast_tables(void)
+{
+       int                     dbnum;
+
+       prep_status("Creating newly-required TOAST tables");
+
+       for (dbnum = 0; dbnum < new_cluster.dbarr.ndbs; dbnum++)
+       {
+               PGresult   *res;
+               int                     ntups;
+               int                     rowno;
+               int                     i_nspname,
+                                       i_relname;
+               DbInfo     *active_db = &new_cluster.dbarr.dbs[dbnum];
+               PGconn     *conn = connectToServer(&new_cluster, active_db->db_name);
+
+               res = executeQueryOrDie(conn,
+                                                               "SELECT n.nspname, c.relname "
+                                                               "FROM   pg_catalog.pg_class c, "
+                                                               "               pg_catalog.pg_namespace n "
+                                                               "WHERE  c.relnamespace = n.oid AND "
+                                                         "             n.nspname NOT IN ('pg_catalog', 'information_schema') AND "
+                                                               "c.relkind IN ('r', 'm') AND "
+                                                               "c.reltoastrelid = 0");
+
+               ntups = PQntuples(res);
+               i_nspname = PQfnumber(res, "nspname");
+               i_relname = PQfnumber(res, "relname");
+               for (rowno = 0; rowno < ntups; rowno++)
+               {
+                       /* enable auto-oid-numbered TOAST creation if needed */
+                       PQclear(executeQueryOrDie(conn, "SELECT binary_upgrade.set_next_toast_pg_class_oid('%d'::pg_catalog.oid);",
+                                       OPTIONALLY_CREATE_TOAST_OID));
+
+                       /* dummy command that also triggers check for required TOAST table */
+                       PQclear(executeQueryOrDie(conn, "ALTER TABLE %s.%s RESET (binary_upgrade_dummy_option);",
+                                       quote_identifier(PQgetvalue(res, rowno, i_nspname)),
+                                       quote_identifier(PQgetvalue(res, rowno, i_relname))));
+               }
+
+               PQclear(res);
+
+               PQfinish(conn);
+       }
+
+       check_ok();
+}
index db21ec0f776015b53f641bab070689704279b912..1fc9d903e1c4c8ef2cdc14acb27ee08552692846 100644 (file)
@@ -364,6 +364,8 @@ create_new_objects(void)
        if (GET_MAJOR_VERSION(old_cluster.major_version) < 903)
                set_frozenxids(true);
 
+       optionally_create_toast_tables();
+
        /* regenerate now that we have objects in the databases */
        get_db_and_rel_infos(&new_cluster);
 
index 0d44f4e4477cf5bd2b95508d041e0b8924cda876..38d37a6c63f3c1c5784a83f51f86b05577a1ac25 100644 (file)
@@ -335,6 +335,7 @@ void                disable_old_cluster(void);
 /* dump.c */
 
 void           generate_old_dump(void);
+void           optionally_create_toast_tables(void);
 
 
 /* exec.c */
index b5d1c3f63b36e44ace38694526b9b18132b21215..bfafcac252333ceda0020608007d0e10b92de8a9 100644 (file)
@@ -20,6 +20,7 @@
 #include "catalog/heap.h"
 #include "catalog/index.h"
 #include "catalog/namespace.h"
+#include "catalog/pg_authid.h"
 #include "catalog/pg_namespace.h"
 #include "catalog/pg_opclass.h"
 #include "catalog/pg_type.h"
@@ -39,6 +40,9 @@ static bool create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
                                   Datum reloptions);
 static bool needs_toast_table(Relation rel);
 
+/* pick a OID that will never be used for TOAST tables */
+#define OPTIONALLY_CREATE_TOAST_OID    BOOTSTRAP_SUPERUSERID
+
 
 /*
  * AlterTableCreateToastTable
@@ -150,16 +154,51 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio
        if (rel->rd_rel->reltoastrelid != InvalidOid)
                return false;
 
-       /*
-        * Check to see whether the table actually needs a TOAST table.
-        *
-        * If an update-in-place toast relfilenode is specified, force toast file
-        * creation even if it seems not to need one.
-        */
-       if (!needs_toast_table(rel) &&
-               (!IsBinaryUpgrade ||
-                !OidIsValid(binary_upgrade_next_toast_pg_class_oid)))
-               return false;
+       if (!IsBinaryUpgrade)
+       {
+               if (!needs_toast_table(rel))
+                       return false;
+       }
+       else
+       {
+               /*
+                * Check to see whether the table needs a TOAST table.
+                *
+                * If an update-in-place TOAST relfilenode is specified, force TOAST file
+                * creation even if it seems not to need one.  This handles the case
+                * where the old cluster needed a TOAST table but the new cluster
+                * would not normally create one.
+                */
+
+               /*
+                * If a TOAST oid is not specified, skip TOAST creation as we will do
+                * it later so we don't create a TOAST table whose OID later conflicts
+                * with a user-supplied OID.  This handles cases where the old cluster
+                * didn't need a TOAST table, but the new cluster does.
+                */
+               if (!OidIsValid(binary_upgrade_next_toast_pg_class_oid))
+                       return false;
+
+               /*
+                * If a special TOAST value has been passed in, it means we are in
+                * cleanup mode --- we are creating needed TOAST tables after all user
+                * tables with specified OIDs have been created.  We let the system
+                * assign a TOAST oid for us.  The tables are empty so the missing
+                * TOAST tables were not a problem.
+                */
+               if (binary_upgrade_next_toast_pg_class_oid == OPTIONALLY_CREATE_TOAST_OID)
+               {
+                       /* clear as it is not to be used; it is just a flag */
+                       binary_upgrade_next_toast_pg_class_oid = InvalidOid;
+
+                       if (!needs_toast_table(rel))
+                               return false;
+               }
+
+               /* both should be set, or not set */
+               Assert(OidIsValid(binary_upgrade_next_toast_pg_class_oid) ==
+                          OidIsValid(binary_upgrade_next_toast_pg_type_oid));
+       }
 
        /*
         * Create the toast table and its index