Re: pg_upgrade bug found! - Mailing list pgsql-hackers

From Bruce Momjian
Subject Re: pg_upgrade bug found!
Date
Msg-id 201104071616.p37GGu911379@momjian.us
Whole thread Raw
In response to pg_upgrade bug found!  (Bruce Momjian <bruce@momjian.us>)
Responses Re: pg_upgrade bug found!
Re: pg_upgrade bug found!
Re: pg_upgrade bug found!
Re: pg_upgrade bug found!
List pgsql-hackers
Bruce Momjian wrote:
> OK, thanks to RhodiumToad on IRC, I was able to determine the cause of
> the two reported pg_upgrade problems he saw via IRC.  It seems toast
> tables have xids and pg_dump is not preserving the toast relfrozenxids
> as it should.  Heap tables have preserved relfrozenxids, but if you
> update a heap row but don't change the toast value, and the old heap row
> is later removed, the toast table can have an older relfrozenxids than
> the heap table.
>
> The fix for this is to have pg_dump preserve toast relfrozenxids, which
> can be easily added and backpatched.  We might want to push a 9.0.4 for
> this.  Second, we need to find a way for people to detect and fix
> existing systems that have this problem, perhaps looming when the
> pg_class relfrozenxid passes the toast relfrozenxid, and thirdly, we
> need to figure out how to get this information to users.  Perhaps the
> communication comes through the 9.0.4 release announcement.

I am not sure how to interpret the lack of replies to this email.
Either it is confidence, shock, or we told you so.  ;-)

Anyway, the attached patch fixes the problem.  The fix is for pg_dump's
binary upgrade mode.  This would need to be backpatched back to 8.4
because pg_migrator needs this too.

I have added a personal regression test to show which
pg_class.relfrozenxid values are not preserved, and with this patch the
only ones not preserved are toast tables used by system tables, which
are not copied from the old cluster (FirstNormalObjectId = 16384).  I am
attaching that old/new pg_class.relfrozenxid diff as well.

Any idea how to correct existing systems?  Would VACUUM FREEZE of just
the toast tables work?  I perhaps could create a short DO block that
would vacuum freeze just toast tables;  it would have to be run in every
database.

--
  Bruce Momjian  <bruce@momjian.us>        http://momjian.us
  EnterpriseDB                             http://enterprisedb.com

  + It's impossible for everything to be true. +
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
new file mode 100644
index 3f6e77b..1ccdb4d
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
*************** getTables(int *numTables)
*** 3812,3817 ****
--- 3812,3819 ----
      int            i_relhasrules;
      int            i_relhasoids;
      int            i_relfrozenxid;
+     int            i_toastoid;
+     int            i_toastfrozenxid;
      int            i_relpersistence;
      int            i_owning_tab;
      int            i_owning_col;
*************** getTables(int *numTables)
*** 3855,3861 ****
                            "(%s c.relowner) AS rolname, "
                            "c.relchecks, c.relhastriggers, "
                            "c.relhasindex, c.relhasrules, c.relhasoids, "
!                           "c.relfrozenxid, c.relpersistence, "
                            "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS
reloftype," 
                            "d.refobjid AS owning_tab, "
                            "d.refobjsubid AS owning_col, "
--- 3857,3865 ----
                            "(%s c.relowner) AS rolname, "
                            "c.relchecks, c.relhastriggers, "
                            "c.relhasindex, c.relhasrules, c.relhasoids, "
!                           "c.relfrozenxid, tc.oid AS toid, "
!                           "tc.relfrozenxid AS tfrozenxid, "
!                           "c.relpersistence, "
                            "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS
reloftype," 
                            "d.refobjid AS owning_tab, "
                            "d.refobjsubid AS owning_col, "
*************** getTables(int *numTables)
*** 3889,3895 ****
                            "(%s c.relowner) AS rolname, "
                            "c.relchecks, c.relhastriggers, "
                            "c.relhasindex, c.relhasrules, c.relhasoids, "
!                           "c.relfrozenxid, 'p' AS relpersistence, "
                            "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS
reloftype," 
                            "d.refobjid AS owning_tab, "
                            "d.refobjsubid AS owning_col, "
--- 3893,3901 ----
                            "(%s c.relowner) AS rolname, "
                            "c.relchecks, c.relhastriggers, "
                            "c.relhasindex, c.relhasrules, c.relhasoids, "
!                           "c.relfrozenxid, tc.oid AS toid, "
!                           "tc.relfrozenxid AS tfrozenxid, "
!                           "'p' AS relpersistence, "
                            "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS
reloftype," 
                            "d.refobjid AS owning_tab, "
                            "d.refobjsubid AS owning_col, "
*************** getTables(int *numTables)
*** 3922,3928 ****
                            "(%s c.relowner) AS rolname, "
                            "c.relchecks, c.relhastriggers, "
                            "c.relhasindex, c.relhasrules, c.relhasoids, "
!                           "c.relfrozenxid, 'p' AS relpersistence, "
                            "NULL AS reloftype, "
                            "d.refobjid AS owning_tab, "
                            "d.refobjsubid AS owning_col, "
--- 3928,3936 ----
                            "(%s c.relowner) AS rolname, "
                            "c.relchecks, c.relhastriggers, "
                            "c.relhasindex, c.relhasrules, c.relhasoids, "
!                           "c.relfrozenxid, tc.oid AS toid, "
!                           "tc.relfrozenxid AS tfrozenxid, "
!                           "'p' AS relpersistence, "
                            "NULL AS reloftype, "
                            "d.refobjid AS owning_tab, "
                            "d.refobjsubid AS owning_col, "
*************** getTables(int *numTables)
*** 3955,3961 ****
                            "(%s relowner) AS rolname, "
                            "relchecks, (reltriggers <> 0) AS relhastriggers, "
                            "relhasindex, relhasrules, relhasoids, "
!                           "relfrozenxid, 'p' AS relpersistence, "
                            "NULL AS reloftype, "
                            "d.refobjid AS owning_tab, "
                            "d.refobjsubid AS owning_col, "
--- 3963,3972 ----
                            "(%s relowner) AS rolname, "
                            "relchecks, (reltriggers <> 0) AS relhastriggers, "
                            "relhasindex, relhasrules, relhasoids, "
!                           "relfrozenxid, "
!                           "0 AS toid, "
!                           "0 AS tfrozenxid, "
!                           "'p' AS relpersistence, "
                            "NULL AS reloftype, "
                            "d.refobjid AS owning_tab, "
                            "d.refobjsubid AS owning_col, "
*************** getTables(int *numTables)
*** 3987,3993 ****
                            "(%s relowner) AS rolname, "
                            "relchecks, (reltriggers <> 0) AS relhastriggers, "
                            "relhasindex, relhasrules, relhasoids, "
!                           "0 AS relfrozenxid, 'p' AS relpersistence, "
                            "NULL AS reloftype, "
                            "d.refobjid AS owning_tab, "
                            "d.refobjsubid AS owning_col, "
--- 3998,4007 ----
                            "(%s relowner) AS rolname, "
                            "relchecks, (reltriggers <> 0) AS relhastriggers, "
                            "relhasindex, relhasrules, relhasoids, "
!                           "0 AS relfrozenxid, "
!                           "0 AS toid, "
!                           "0 AS tfrozenxid, "
!                           "'p' AS relpersistence, "
                            "NULL AS reloftype, "
                            "d.refobjid AS owning_tab, "
                            "d.refobjsubid AS owning_col, "
*************** getTables(int *numTables)
*** 4019,4025 ****
                            "(%s relowner) AS rolname, "
                            "relchecks, (reltriggers <> 0) AS relhastriggers, "
                            "relhasindex, relhasrules, relhasoids, "
!                           "0 AS relfrozenxid, 'p' AS relpersistence, "
                            "NULL AS reloftype, "
                            "d.refobjid AS owning_tab, "
                            "d.refobjsubid AS owning_col, "
--- 4033,4042 ----
                            "(%s relowner) AS rolname, "
                            "relchecks, (reltriggers <> 0) AS relhastriggers, "
                            "relhasindex, relhasrules, relhasoids, "
!                           "0 AS relfrozenxid, "
!                           "0 AS toid, "
!                           "0 AS tfrozenxid, "
!                           "'p' AS relpersistence, "
                            "NULL AS reloftype, "
                            "d.refobjid AS owning_tab, "
                            "d.refobjsubid AS owning_col, "
*************** getTables(int *numTables)
*** 4047,4053 ****
                            "(%s relowner) AS rolname, "
                            "relchecks, (reltriggers <> 0) AS relhastriggers, "
                            "relhasindex, relhasrules, relhasoids, "
!                           "0 AS relfrozenxid, 'p' AS relpersistence, "
                            "NULL AS reloftype, "
                            "NULL::oid AS owning_tab, "
                            "NULL::int4 AS owning_col, "
--- 4064,4073 ----
                            "(%s relowner) AS rolname, "
                            "relchecks, (reltriggers <> 0) AS relhastriggers, "
                            "relhasindex, relhasrules, relhasoids, "
!                           "0 AS relfrozenxid, "
!                           "0 AS toid, "
!                           "0 AS tfrozenxid, "
!                           "'p' AS relpersistence, "
                            "NULL AS reloftype, "
                            "NULL::oid AS owning_tab, "
                            "NULL::int4 AS owning_col, "
*************** getTables(int *numTables)
*** 4070,4076 ****
                            "relchecks, (reltriggers <> 0) AS relhastriggers, "
                            "relhasindex, relhasrules, "
                            "'t'::bool AS relhasoids, "
!                           "0 AS relfrozenxid, 'p' AS relpersistence, "
                            "NULL AS reloftype, "
                            "NULL::oid AS owning_tab, "
                            "NULL::int4 AS owning_col, "
--- 4090,4099 ----
                            "relchecks, (reltriggers <> 0) AS relhastriggers, "
                            "relhasindex, relhasrules, "
                            "'t'::bool AS relhasoids, "
!                           "0 AS relfrozenxid, "
!                           "0 AS toid, "
!                           "0 AS tfrozenxid, "
!                           "'p' AS relpersistence, "
                            "NULL AS reloftype, "
                            "NULL::oid AS owning_tab, "
                            "NULL::int4 AS owning_col, "
*************** getTables(int *numTables)
*** 4103,4109 ****
                            "relchecks, (reltriggers <> 0) AS relhastriggers, "
                            "relhasindex, relhasrules, "
                            "'t'::bool AS relhasoids, "
!                           "0 as relfrozenxid, 'p' AS relpersistence, "
                            "NULL AS reloftype, "
                            "NULL::oid AS owning_tab, "
                            "NULL::int4 AS owning_col, "
--- 4126,4135 ----
                            "relchecks, (reltriggers <> 0) AS relhastriggers, "
                            "relhasindex, relhasrules, "
                            "'t'::bool AS relhasoids, "
!                           "0 as relfrozenxid, "
!                           "0 AS toid, "
!                           "0 AS tfrozenxid, "
!                           "'p' AS relpersistence, "
                            "NULL AS reloftype, "
                            "NULL::oid AS owning_tab, "
                            "NULL::int4 AS owning_col, "
*************** getTables(int *numTables)
*** 4149,4154 ****
--- 4175,4182 ----
      i_relhasrules = PQfnumber(res, "relhasrules");
      i_relhasoids = PQfnumber(res, "relhasoids");
      i_relfrozenxid = PQfnumber(res, "relfrozenxid");
+     i_toastoid = PQfnumber(res, "toid");
+     i_toastfrozenxid = PQfnumber(res, "tfrozenxid");
      i_relpersistence = PQfnumber(res, "relpersistence");
      i_owning_tab = PQfnumber(res, "owning_tab");
      i_owning_col = PQfnumber(res, "owning_col");
*************** getTables(int *numTables)
*** 4190,4195 ****
--- 4218,4225 ----
          tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0);
          tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0);
          tblinfo[i].frozenxid = atooid(PQgetvalue(res, i, i_relfrozenxid));
+         tblinfo[i].toast_oid = atooid(PQgetvalue(res, i, i_toastoid));
+         tblinfo[i].toast_frozenxid = atooid(PQgetvalue(res, i, i_toastfrozenxid));
          if (PQgetisnull(res, i, i_reloftype))
              tblinfo[i].reloftype = NULL;
          else
*************** dumpTableSchema(Archive *fout, TableInfo
*** 12221,12233 ****
                  }
              }

!             appendPQExpBuffer(q, "\n-- For binary upgrade, set relfrozenxid\n");
              appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
                                "SET relfrozenxid = '%u'\n"
                                "WHERE oid = ",
                                tbinfo->frozenxid);
              appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout);
              appendPQExpBuffer(q, "::pg_catalog.regclass;\n");
          }

          /* Loop dumping statistics and storage statements */
--- 12251,12273 ----
                  }
              }

!             appendPQExpBuffer(q, "\n-- For binary upgrade, set heap's relfrozenxid\n");
              appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
                                "SET relfrozenxid = '%u'\n"
                                "WHERE oid = ",
                                tbinfo->frozenxid);
              appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout);
              appendPQExpBuffer(q, "::pg_catalog.regclass;\n");
+
+             if (tbinfo->toast_oid)
+             {
+                 /* We preserve the toast oids, so we can use it during restore */
+                 appendPQExpBuffer(q, "\n-- For binary upgrade, set toast's relfrozenxid\n");
+                 appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
+                                   "SET relfrozenxid = '%u'\n"
+                                   "WHERE oid = '%u';\n",
+                                   tbinfo->toast_frozenxid, tbinfo->toast_oid);
+             }
          }

          /* Loop dumping statistics and storage statements */
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
new file mode 100644
index 113ecb1..6559e23
*** a/src/bin/pg_dump/pg_dump.h
--- b/src/bin/pg_dump/pg_dump.h
*************** typedef struct _tableInfo
*** 248,253 ****
--- 248,255 ----
      bool        hastriggers;    /* does it have any triggers? */
      bool        hasoids;        /* does it have OIDs? */
      uint32        frozenxid;        /* for restore frozen xid */
+     Oid            toast_oid;        /* for restore toast frozen xid */
+     uint32        toast_frozenxid;/* for restore toast frozen xid */
      int            ncheck;            /* # of CHECK expressions */
      char       *reloftype;        /* underlying type for typed table */
      /* these two are set only if table is a sequence owned by a column: */
3c3
< postgres|654
---
> postgres|1457
6,7c6,7
< template0|654
< template1|654
---
> template0|1457
> template1|1457
11,19c11,19
< pg_toast|pg_toast_11454|654
< pg_toast|pg_toast_11459|654
< pg_toast|pg_toast_11464|654
< pg_toast|pg_toast_11469|654
< pg_toast|pg_toast_11474|654
< pg_toast|pg_toast_11479|654
< pg_toast|pg_toast_11484|654
< pg_toast|pg_toast_1255|654
< pg_toast|pg_toast_1262|654
---
> pg_toast|pg_toast_11511|1457
> pg_toast|pg_toast_11516|1457
> pg_toast|pg_toast_11521|1457
> pg_toast|pg_toast_11526|1457
> pg_toast|pg_toast_11531|1457
> pg_toast|pg_toast_11536|1457
> pg_toast|pg_toast_11541|1457
> pg_toast|pg_toast_1255|1457
> pg_toast|pg_toast_1262|1457
96,103c96,104
< pg_toast|pg_toast_2396|654
< pg_toast|pg_toast_2604|654
< pg_toast|pg_toast_2606|654
< pg_toast|pg_toast_2609|654
< pg_toast|pg_toast_2618|654
< pg_toast|pg_toast_2619|654
< pg_toast|pg_toast_2620|654
< pg_toast|pg_toast_2964|654
---
> pg_toast|pg_toast_2396|1457
> pg_toast|pg_toast_2604|1457
> pg_toast|pg_toast_2606|1457
> pg_toast|pg_toast_2609|1457
> pg_toast|pg_toast_2618|1457
> pg_toast|pg_toast_2619|1457
> pg_toast|pg_toast_2620|1457
> pg_toast|pg_toast_2964|1457
> pg_toast|pg_toast_3596|1457
279c280
< (268 rows)
---
> (269 rows)

pgsql-hackers by date:

Previous
From: Andres Freund
Date:
Subject: Re: Back-patch plpgsql fix for rowtypes with dropped columns?
Next
From: Josh Berkus
Date:
Subject: Fwd: [TESTERS] [PostgreSQL 9.1 alpha5] OpenBSD and Loongson