Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Fix pg_dump/pg_restore to restore event triggers later.
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 9 Mar 2020 18:58:11 +0000 (14:58 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 9 Mar 2020 18:58:11 +0000 (14:58 -0400)
Previously, event triggers were restored just after regular triggers
(and FK constraints, which are basically triggers).  This is risky
since an event trigger, once installed, could interfere with subsequent
restore commands.  Worse, because event triggers don't have any
particular dependencies on any post-data objects, a parallel restore
would consider them eligible to be restored the moment the post-data
phase starts, allowing them to also interfere with restoration of a
whole bunch of objects that would have been restored before them in
a serial restore.  There's no way to completely remove the risk of a
misguided event trigger breaking the restore, since if nothing else
it could break other event triggers.  But we can certainly push them
to later in the process to minimize the hazard.

To fix, tweak the RestorePass mechanism introduced by commit 3eb9a5e7c
so that event triggers are handled as part of the post-ACL processing
pass (renaming the "REFRESH" pass to "POST_ACL" to reflect its more
general use).  This will cause them to restore after everything except
matview refreshes, which seems OK since matview refreshes really ought
to run in the post-restore state of the database.  In a parallel
restore, event triggers and matview refreshes might be intermixed,
but that seems all right as well.

Also update the code and comments in pg_dump_sort.c so that its idea
of how things are sorted agrees with what actually happens due to
the RestorePass mechanism.  This is mostly cosmetic: it'll affect the
order of objects in a dump's TOC, but not the actual restore order.
But not changing that would be quite confusing to somebody reading
the code.

Back-patch to all supported branches.

Fabrízio de Royes Mello, tweaked a bit by me

Discussion: https://postgr.es/m/CAFcNs+ow1hmFox8P--3GSdtwz-S3Binb6ZmoP6Vk+Xg=K6eZNA@mail.gmail.com

src/bin/pg_dump/pg_backup_archiver.c
src/bin/pg_dump/pg_backup_archiver.h
src/bin/pg_dump/pg_dump_sort.c

index 51af3fcd69caf90c6325d4a9f311e11bace71530..0611a56f8bf49f15ee10c50c1f39e4ff9c24f5f9 100644 (file)
@@ -650,11 +650,11 @@ RestoreArchive(Archive *AHX)
    {
        /*
         * In serial mode, process everything in three phases: normal items,
-        * then ACLs, then matview refresh items.  We might be able to skip
-        * one or both extra phases in some cases, eg data-only restores.
+        * then ACLs, then post-ACL items.  We might be able to skip one or
+        * both extra phases in some cases, eg data-only restores.
         */
        bool        haveACL = false;
-       bool        haveRefresh = false;
+       bool        havePostACL = false;
 
        for (te = AH->toc->next; te != AH->toc; te = te->next)
        {
@@ -669,8 +669,8 @@ RestoreArchive(Archive *AHX)
                case RESTORE_PASS_ACL:
                    haveACL = true;
                    break;
-               case RESTORE_PASS_REFRESH:
-                   haveRefresh = true;
+               case RESTORE_PASS_POST_ACL:
+                   havePostACL = true;
                    break;
            }
        }
@@ -685,12 +685,12 @@ RestoreArchive(Archive *AHX)
            }
        }
 
-       if (haveRefresh)
+       if (havePostACL)
        {
            for (te = AH->toc->next; te != AH->toc; te = te->next)
            {
                if ((te->reqs & (REQ_SCHEMA | REQ_DATA)) != 0 &&
-                   _tocEntryRestorePass(te) == RESTORE_PASS_REFRESH)
+                   _tocEntryRestorePass(te) == RESTORE_PASS_POST_ACL)
                    (void) restore_toc_entry(AH, te, false);
            }
        }
@@ -2968,8 +2968,9 @@ _tocEntryRestorePass(TocEntry *te)
        strcmp(te->desc, "ACL LANGUAGE") == 0 ||
        strcmp(te->desc, "DEFAULT ACL") == 0)
        return RESTORE_PASS_ACL;
-   if (strcmp(te->desc, "MATERIALIZED VIEW DATA") == 0)
-       return RESTORE_PASS_REFRESH;
+   if (strcmp(te->desc, "EVENT TRIGGER") == 0 ||
+       strcmp(te->desc, "MATERIALIZED VIEW DATA") == 0)
+       return RESTORE_PASS_POST_ACL;
    return RESTORE_PASS_MAIN;
 }
 
index b4e1d47969d25c24f1f4d03759ffd91782faf477..79942abb310156319a1ee8b0ba3e662e06e9f2e4 100644 (file)
@@ -210,10 +210,14 @@ typedef enum
  * data restore failures.  On the other hand, matview REFRESH commands should
  * come out after ACLs, as otherwise non-superuser-owned matviews might not
  * be able to execute.  (If the permissions at the time of dumping would not
- * allow a REFRESH, too bad; we won't fix that for you.)  These considerations
- * force us to make three passes over the TOC, restoring the appropriate
- * subset of items in each pass.  We assume that the dependency sort resulted
- * in an appropriate ordering of items within each subset.
+ * allow a REFRESH, too bad; we won't fix that for you.)  We also want event
+ * triggers to be restored after ACLs, so that they can't mess those up.
+ *
+ * These considerations force us to make three passes over the TOC,
+ * restoring the appropriate subset of items in each pass.  We assume that
+ * the dependency sort resulted in an appropriate ordering of items within
+ * each subset.
+ *
  * XXX This mechanism should be superseded by tracking dependencies on ACLs
  * properly; but we'll still need it for old dump files even after that.
  */
@@ -221,9 +225,9 @@ typedef enum
 {
    RESTORE_PASS_MAIN = 0,      /* Main pass (most TOC item types) */
    RESTORE_PASS_ACL,           /* ACL item types */
-   RESTORE_PASS_REFRESH        /* Matview REFRESH items */
+   RESTORE_PASS_POST_ACL       /* Event trigger and matview refresh items */
 
-#define RESTORE_PASS_LAST RESTORE_PASS_REFRESH
+#define RESTORE_PASS_LAST RESTORE_PASS_POST_ACL
 } RestorePass;
 
 typedef enum
index 6db0d508ef8e025df56655f2c59631be4c0ff6c6..c40e183777c6fbb03b943cf11f664e1550334dc7 100644 (file)
@@ -84,6 +84,16 @@ static const int oldObjectTypePriority[] =
  * Sort priority for object types when dumping newer databases.
  * Objects are sorted by type, and within a type by name.
  *
+ * Triggers, event triggers, and materialized views are intentionally sorted
+ * late.  Triggers must be restored after all data modifications, so that
+ * they don't interfere with loading data.  Event triggers are restored
+ * next-to-last so that they don't interfere with object creations of any
+ * kind.  Matview refreshes are last because they should execute in the
+ * database's normal state (e.g., they must come after all ACLs are restored;
+ * also, if they choose to look at system catalogs, they should see the final
+ * restore state).  If you think to change this, see also the RestorePass
+ * mechanism in pg_backup_archiver.c.
+ *
  * NOTE: object-type priorities must match the section assignments made in
  * pg_dump.c; that is, PRE_DATA objects must sort before DO_PRE_DATA_BOUNDARY,
  * POST_DATA objects must sort after DO_POST_DATA_BOUNDARY, and DATA objects
@@ -120,15 +130,15 @@ static const int newObjectTypePriority[] =
    15,                         /* DO_TSCONFIG */
    16,                         /* DO_FDW */
    17,                         /* DO_FOREIGN_SERVER */
-   31,                         /* DO_DEFAULT_ACL */
+   32,                         /* DO_DEFAULT_ACL --- done in ACL pass */
    3,                          /* DO_TRANSFORM */
    21,                         /* DO_BLOB */
    24,                         /* DO_BLOB_DATA */
    22,                         /* DO_PRE_DATA_BOUNDARY */
    25,                         /* DO_POST_DATA_BOUNDARY */
-   32,                         /* DO_EVENT_TRIGGER */
-   33,                         /* DO_REFRESH_MATVIEW */
-   34                          /* DO_POLICY */
+   33,                         /* DO_EVENT_TRIGGER --- next to last! */
+   34,                         /* DO_REFRESH_MATVIEW --- last! */
+   31                          /* DO_POLICY */
 };
 
 static DumpId preDataBoundId;