Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 38b41f1

Browse files
committed
Repair pg_upgrade's failure to preserve relfrozenxid for matviews.
This oversight led to data corruption in matviews, manifesting as "could not access status of transaction" before our most recent releases, and "found xmin from before relfrozenxid" errors since then. The proximate cause of the problem seems to have been confusion between the task of preserving dropped-column status and the task of preserving frozenxid status. Those are required for distinct sets of relkinds, and the reasoning was entirely undocumented in the source code. In hopes of forestalling future errors of the same kind, try to improve the commentary in this area. In passing, also improve the remarkably unhelpful comments around pg_upgrade's set_frozenxids(). That's not actually buggy AFAICS, but good luck figuring out what it does from the old comments. Per report from Claudio Freire. It appears that bug #14852 from Alexey Ermakov is an earlier report of the same issue, and there may be other cases that we failed to identify at the time. Patch by me based on analysis by Andres Freund. The bug dates back to the introduction of matviews, so back-patch to all supported branches. Discussion: https://postgr.es/m/CAGTBQpbrY9CdRGGhyBZ9yqY4jWaGC85rUF4X+R7d-aim=mBNsw@mail.gmail.com Discussion: https://postgr.es/m/20171013115320.28049.86457@wrigleys.postgresql.org
1 parent 29d432e commit 38b41f1

File tree

2 files changed

+50
-14
lines changed

2 files changed

+50
-14
lines changed

src/bin/pg_dump/pg_dump.c

+28-3
Original file line numberDiff line numberDiff line change
@@ -6548,7 +6548,8 @@ getTables(Archive *fout, int *numTables)
65486548
* alterations to parent tables.
65496549
*
65506550
* NOTE: it'd be kinda nice to lock other relations too, not only
6551-
* plain tables, but the backend doesn't presently allow that.
6551+
* plain or partitioned tables, but the backend doesn't presently
6552+
* allow that.
65526553
*
65536554
* We only need to lock the table for certain components; see
65546555
* pg_dump.h
@@ -15898,6 +15899,14 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
1589815899
* column order. That also means we have to take care about setting
1589915900
* attislocal correctly, plus fix up any inherited CHECK constraints.
1590015901
* Analogously, we set up typed tables using ALTER TABLE / OF here.
15902+
*
15903+
* We process foreign and partitioned tables here, even though they
15904+
* lack heap storage, because they can participate in inheritance
15905+
* relationships and we want this stuff to be consistent across the
15906+
* inheritance tree. We can exclude indexes, toast tables, sequences
15907+
* and matviews, even though they have storage, because we don't
15908+
* support altering or dropping columns in them, nor can they be part
15909+
* of inheritance trees.
1590115910
*/
1590215911
if (dopt->binary_upgrade &&
1590315912
(tbinfo->relkind == RELKIND_RELATION ||
@@ -16009,7 +16018,19 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
1600916018
fmtId(tbinfo->dobj.name),
1601016019
tbinfo->reloftype);
1601116020
}
16021+
}
1601216022

16023+
/*
16024+
* In binary_upgrade mode, arrange to restore the old relfrozenxid and
16025+
* relminmxid of all vacuumable relations. (While vacuum.c processes
16026+
* TOAST tables semi-independently, here we see them only as children
16027+
* of other relations; so this "if" lacks RELKIND_TOASTVALUE, and the
16028+
* child toast table is handled below.)
16029+
*/
16030+
if (dopt->binary_upgrade &&
16031+
(tbinfo->relkind == RELKIND_RELATION ||
16032+
tbinfo->relkind == RELKIND_MATVIEW))
16033+
{
1601316034
appendPQExpBufferStr(q, "\n-- For binary upgrade, set heap's relfrozenxid and relminmxid\n");
1601416035
appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
1601516036
"SET relfrozenxid = '%u', relminmxid = '%u'\n"
@@ -16020,7 +16041,10 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
1602016041

1602116042
if (tbinfo->toast_oid)
1602216043
{
16023-
/* We preserve the toast oids, so we can use it during restore */
16044+
/*
16045+
* The toast table will have the same OID at restore, so we
16046+
* can safely target it by OID.
16047+
*/
1602416048
appendPQExpBufferStr(q, "\n-- For binary upgrade, set toast's relfrozenxid and relminmxid\n");
1602516049
appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
1602616050
"SET relfrozenxid = '%u', relminmxid = '%u'\n"
@@ -16034,7 +16058,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
1603416058
* In binary_upgrade mode, restore matviews' populated status by
1603516059
* poking pg_class directly. This is pretty ugly, but we can't use
1603616060
* REFRESH MATERIALIZED VIEW since it's possible that some underlying
16037-
* matview is not populated even though this matview is.
16061+
* matview is not populated even though this matview is; in any case,
16062+
* we want to transfer the matview's heap storage, not run REFRESH.
1603816063
*/
1603916064
if (dopt->binary_upgrade && tbinfo->relkind == RELKIND_MATVIEW &&
1604016065
tbinfo->relispopulated)

src/bin/pg_upgrade/pg_upgrade.c

+22-11
Original file line numberDiff line numberDiff line change
@@ -278,13 +278,13 @@ static void
278278
prepare_new_globals(void)
279279
{
280280
/*
281-
* We set autovacuum_freeze_max_age to its maximum value so autovacuum
282-
* does not launch here and delete clog files, before the frozen xids are
283-
* set.
281+
* Before we restore anything, set frozenxids of initdb-created tables.
284282
*/
285-
286283
set_frozenxids(false);
287284

285+
/*
286+
* Now restore global objects (roles and tablespaces).
287+
*/
288288
prep_status("Restoring global objects in the new cluster");
289289

290290
exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -506,14 +506,25 @@ copy_xact_xlog_xid(void)
506506
/*
507507
* set_frozenxids()
508508
*
509-
* We have frozen all xids, so set datfrozenxid, relfrozenxid, and
510-
* relminmxid to be the old cluster's xid counter, which we just set
511-
* in the new cluster. User-table frozenxid and minmxid values will
512-
* be set by pg_dump --binary-upgrade, but objects not set by the pg_dump
513-
* must have proper frozen counters.
509+
* This is called on the new cluster before we restore anything, with
510+
* minmxid_only = false. Its purpose is to ensure that all initdb-created
511+
* vacuumable tables have relfrozenxid/relminmxid matching the old cluster's
512+
* xid/mxid counters. We also initialize the datfrozenxid/datminmxid of the
513+
* built-in databases to match.
514+
*
515+
* As we create user tables later, their relfrozenxid/relminmxid fields will
516+
* be restored properly by the binary-upgrade restore script. Likewise for
517+
* user-database datfrozenxid/datminmxid. However, if we're upgrading from a
518+
* pre-9.3 database, which does not store per-table or per-DB minmxid, then
519+
* the relminmxid/datminmxid values filled in by the restore script will just
520+
* be zeroes.
521+
*
522+
* Hence, with a pre-9.3 source database, a second call occurs after
523+
* everything is restored, with minmxid_only = true. This pass will
524+
* initialize all tables and databases, both those made by initdb and user
525+
* objects, with the desired minmxid value. frozenxid values are left alone.
514526
*/
515-
static
516-
void
527+
static void
517528
set_frozenxids(bool minmxid_only)
518529
{
519530
int dbnum;

0 commit comments

Comments
 (0)