aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Momjian <bruce@momjian.us>2014-08-07 14:56:13 -0400
committerBruce Momjian <bruce@momjian.us>2014-08-07 14:56:13 -0400
commit4c6780fd17aa43ed6362aa682499cc2f9712cc8b (patch)
treecd92b289888c04beaf5775ecee391c1a31b10cac
parentec903d20e3b4c1a543dbf057055e4ddbfad4d59e (diff)
downloadpostgresql-4c6780fd17aa43ed6362aa682499cc2f9712cc8b.tar.gz
postgresql-4c6780fd17aa43ed6362aa682499cc2f9712cc8b.zip
pg_upgrade: prevent oid conflicts with new-cluster TOAST tables
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
-rw-r--r--contrib/pg_upgrade/dump.c70
-rw-r--r--contrib/pg_upgrade/pg_upgrade.c2
-rw-r--r--contrib/pg_upgrade/pg_upgrade.h1
-rw-r--r--src/backend/catalog/toasting.c55
-rw-r--r--src/include/catalog/binary_upgrade.h5
5 files changed, 123 insertions, 10 deletions
diff --git a/contrib/pg_upgrade/dump.c b/contrib/pg_upgrade/dump.c
index b112f3a9527..e623a226327 100644
--- a/contrib/pg_upgrade/dump.c
+++ b/contrib/pg_upgrade/dump.c
@@ -12,6 +12,8 @@
#include "pg_upgrade.h"
#include <sys/types.h>
+#include "catalog/binary_upgrade.h"
+
void
generate_old_dump(void)
@@ -67,3 +69,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();
+}
diff --git a/contrib/pg_upgrade/pg_upgrade.c b/contrib/pg_upgrade/pg_upgrade.c
index 3e97b66476f..49ea873099d 100644
--- a/contrib/pg_upgrade/pg_upgrade.c
+++ b/contrib/pg_upgrade/pg_upgrade.c
@@ -363,6 +363,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);
diff --git a/contrib/pg_upgrade/pg_upgrade.h b/contrib/pg_upgrade/pg_upgrade.h
index 61e5de015ca..1175604e375 100644
--- a/contrib/pg_upgrade/pg_upgrade.h
+++ b/contrib/pg_upgrade/pg_upgrade.h
@@ -336,6 +336,7 @@ void disable_old_cluster(void);
/* dump.c */
void generate_old_dump(void);
+void optionally_create_toast_tables(void);
/* exec.c */
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index bdfeb90dd10..94543e1d510 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -165,16 +165,51 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
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));
+ }
/*
* If requested check lockmode is sufficient. This is a cross check in
diff --git a/src/include/catalog/binary_upgrade.h b/src/include/catalog/binary_upgrade.h
index f39017cfdf0..63fa85ed99e 100644
--- a/src/include/catalog/binary_upgrade.h
+++ b/src/include/catalog/binary_upgrade.h
@@ -14,6 +14,11 @@
#ifndef BINARY_UPGRADE_H
#define BINARY_UPGRADE_H
+#include "catalog/pg_authid.h"
+
+/* pick a OID that will never be used for TOAST tables */
+#define OPTIONALLY_CREATE_TOAST_OID BOOTSTRAP_SUPERUSERID
+
extern PGDLLIMPORT Oid binary_upgrade_next_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_array_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_toast_pg_type_oid;