static void processStdStringsEntry(ArchiveHandle *AH, TocEntry *te);
 static void processSearchPathEntry(ArchiveHandle *AH, TocEntry *te);
 static int _tocEntryRequired(TocEntry *te, teSection curSection, ArchiveHandle *AH);
-static RestorePass _tocEntryRestorePass(TocEntry *te);
+static RestorePass _tocEntryRestorePass(ArchiveHandle *AH, TocEntry *te);
 static bool _tocEntryIsACL(TocEntry *te);
 static void _disableTriggersIfNecessary(ArchiveHandle *AH, TocEntry *te);
 static void _enableTriggersIfNecessary(ArchiveHandle *AH, TocEntry *te);
 static void pending_list_remove(TocEntry *te);
 static int TocEntrySizeCompareQsort(const void *p1, const void *p2);
 static int TocEntrySizeCompareBinaryheap(void *p1, void *p2, void *arg);
-static void move_to_ready_heap(TocEntry *pending_list,
+static void move_to_ready_heap(ArchiveHandle *AH,
+                              TocEntry *pending_list,
                               binaryheap *ready_heap,
                               RestorePass pass);
 static TocEntry *pop_next_work_item(binaryheap *ready_heap,
            if ((te->reqs & (REQ_SCHEMA | REQ_DATA | REQ_STATS)) == 0)
                continue;       /* ignore if not to be dumped at all */
 
-           switch (_tocEntryRestorePass(te))
+           switch (_tocEntryRestorePass(AH, te))
            {
                case RESTORE_PASS_MAIN:
                    (void) restore_toc_entry(AH, te, false);
            for (te = AH->toc->next; te != AH->toc; te = te->next)
            {
                if ((te->reqs & (REQ_SCHEMA | REQ_DATA | REQ_STATS)) != 0 &&
-                   _tocEntryRestorePass(te) == RESTORE_PASS_ACL)
+                   _tocEntryRestorePass(AH, te) == RESTORE_PASS_ACL)
                    (void) restore_toc_entry(AH, te, false);
            }
        }
            for (te = AH->toc->next; te != AH->toc; te = te->next)
            {
                if ((te->reqs & (REQ_SCHEMA | REQ_DATA | REQ_STATS)) != 0 &&
-                   _tocEntryRestorePass(te) == RESTORE_PASS_POST_ACL)
+                   _tocEntryRestorePass(AH, te) == RESTORE_PASS_POST_ACL)
                    (void) restore_toc_entry(AH, te, false);
            }
        }
  * See notes with the RestorePass typedef in pg_backup_archiver.h.
  */
 static RestorePass
-_tocEntryRestorePass(TocEntry *te)
+_tocEntryRestorePass(ArchiveHandle *AH, TocEntry *te)
 {
    /* "ACL LANGUAGE" was a crock emitted only in PG 7.4 */
    if (strcmp(te->desc, "ACL") == 0 ||
        strncmp(te->tag, "EVENT TRIGGER ", 14) == 0)
        return RESTORE_PASS_POST_ACL;
 
+   /*
+    * If statistics data is dependent on materialized view data, it must be
+    * deferred to RESTORE_PASS_POST_ACL.
+    */
+   if (strcmp(te->desc, "STATISTICS DATA") == 0)
+   {
+       for (int i = 0; i < te->nDeps; i++)
+       {
+           DumpId      depid = te->dependencies[i];
+
+           if (depid <= AH->maxDumpId && AH->tocsByDumpId[depid] != NULL)
+           {
+               TocEntry   *otherte = AH->tocsByDumpId[depid];
+
+               if (strcmp(otherte->desc, "MATERIALIZED VIEW DATA") == 0)
+                   return RESTORE_PASS_POST_ACL;
+           }
+       }
+   }
+
    /* All else can be handled in the main pass. */
    return RESTORE_PASS_MAIN;
 }
         * not set skipped_some in this case, since by assumption no main-pass
         * items could depend on these.
         */
-       if (_tocEntryRestorePass(next_work_item) != RESTORE_PASS_MAIN)
+       if (_tocEntryRestorePass(AH, next_work_item) != RESTORE_PASS_MAIN)
            do_now = false;
 
        if (do_now)
     * process in the current restore pass.
     */
    AH->restorePass = RESTORE_PASS_MAIN;
-   move_to_ready_heap(pending_list, ready_heap, AH->restorePass);
+   move_to_ready_heap(AH, pending_list, ready_heap, AH->restorePass);
 
    /*
     * main parent loop
            /* Advance to next restore pass */
            AH->restorePass++;
            /* That probably allows some stuff to be made ready */
-           move_to_ready_heap(pending_list, ready_heap, AH->restorePass);
+           move_to_ready_heap(AH, pending_list, ready_heap, AH->restorePass);
            /* Loop around to see if anything's now ready */
            continue;
        }
  * which applies the same logic one-at-a-time.)
  */
 static void
-move_to_ready_heap(TocEntry *pending_list,
+move_to_ready_heap(ArchiveHandle *AH,
+                  TocEntry *pending_list,
                   binaryheap *ready_heap,
                   RestorePass pass)
 {
        next_te = te->pending_next;
 
        if (te->depCount == 0 &&
-           _tocEntryRestorePass(te) == pass)
+           _tocEntryRestorePass(AH, te) == pass)
        {
            /* Remove it from pending_list ... */
            pending_list_remove(te);
         * memberships changed.
         */
        if (otherte->depCount == 0 &&
-           _tocEntryRestorePass(otherte) == AH->restorePass &&
+           _tocEntryRestorePass(AH, otherte) == AH->restorePass &&
            otherte->pending_prev != NULL &&
            ready_heap != NULL)
        {
 
 
    tbinfo->dataObj = tdinfo;
 
+   /*
+    * Materialized view statistics must be restored after the data, because
+    * REFRESH MATERIALIZED VIEW replaces the storage and resets the stats.
+    *
+    * The dependency is added here because the statistics objects are created
+    * first.
+    */
+   if (tbinfo->relkind == RELKIND_MATVIEW && tbinfo->stats != NULL)
+   {
+       tbinfo->stats->section = SECTION_POST_DATA;
+       addObjectDependency(&tbinfo->stats->dobj, tdinfo->dobj.dumpId);
+   }
+
    /* Make sure that we'll collect per-column info for this table. */
    tbinfo->interesting = true;
 }
        info->relkind = relkind;
        info->indAttNames = indAttNames;
        info->nindAttNames = nindAttNames;
-       info->postponed_def = false;
+
+       /*
+        * Ordinarily, stats go in SECTION_DATA for tables and
+        * SECTION_POST_DATA for indexes.
+        *
+        * However, the section may be updated later for materialized view
+        * stats. REFRESH MATERIALIZED VIEW replaces the storage and resets
+        * the stats, so the stats must be restored after the data. Also, the
+        * materialized view definition may be postponed to SECTION_POST_DATA
+        * (see repairMatViewBoundaryMultiLoop()).
+        */
+       switch (info->relkind)
+       {
+           case RELKIND_RELATION:
+           case RELKIND_PARTITIONED_TABLE:
+           case RELKIND_MATVIEW:
+               info->section = SECTION_DATA;
+               break;
+           case RELKIND_INDEX:
+           case RELKIND_PARTITIONED_INDEX:
+               info->section = SECTION_POST_DATA;
+               break;
+           default:
+               pg_fatal("cannot dump statistics for relation kind '%c'",
+                        info->relkind);
+       }
 
        return info;
    }
 
        /* Add statistics */
        if (tblinfo[i].interesting)
-           getRelationStatistics(fout, &tblinfo[i].dobj, tblinfo[i].relpages,
-                                 PQgetvalue(res, i, i_reltuples),
-                                 relallvisible, tblinfo[i].relkind, NULL, 0);
+       {
+           RelStatsInfo *stats;
+
+           stats = getRelationStatistics(fout, &tblinfo[i].dobj,
+                                         tblinfo[i].relpages,
+                                         PQgetvalue(res, i, i_reltuples),
+                                         relallvisible,
+                                         tblinfo[i].relkind, NULL, 0);
+           if (tblinfo[i].relkind == RELKIND_MATVIEW)
+               tblinfo[i].stats = stats;
+       }
 
        /*
         * Read-lock target tables to make sure they aren't DROPPED or altered
    appendPQExpBuffer(out, "::%s", argtype);
 }
 
-/*
- * Decide which section to use based on the relkind of the parent object.
- *
- * NB: materialized views may be postponed from SECTION_PRE_DATA to
- * SECTION_POST_DATA to resolve some kinds of dependency problems. If so, the
- * matview stats will also be postponed to SECTION_POST_DATA. See
- * repairMatViewBoundaryMultiLoop().
- */
-static teSection
-statisticsDumpSection(const RelStatsInfo *rsinfo)
-{
-   switch (rsinfo->relkind)
-   {
-       case RELKIND_RELATION:
-       case RELKIND_PARTITIONED_TABLE:
-       case RELKIND_MATVIEW:
-           return SECTION_DATA;
-       case RELKIND_INDEX:
-       case RELKIND_PARTITIONED_INDEX:
-           return SECTION_POST_DATA;
-       default:
-           pg_fatal("cannot dump statistics for relation kind '%c'",
-                    rsinfo->relkind);
-   }
-
-   return 0;                   /* keep compiler quiet */
-}
-
 /*
  * dumpRelationStats --
  *
    PGresult   *res;
    PQExpBuffer query;
    PQExpBuffer out;
-   DumpId     *deps = NULL;
-   int         ndeps = 0;
    int         i_attname;
    int         i_inherited;
    int         i_null_frac;
    if (!fout->dopt->dumpStatistics)
        return;
 
-   /* dependent on the relation definition, if doing schema */
-   if (fout->dopt->dumpSchema)
-   {
-       deps = dobj->dependencies;
-       ndeps = dobj->nDeps;
-   }
-
    query = createPQExpBuffer();
    if (!fout->is_prepared[PREPQUERY_GETATTRIBUTESTATS])
    {
                 ARCHIVE_OPTS(.tag = dobj->name,
                              .namespace = dobj->namespace->dobj.name,
                              .description = "STATISTICS DATA",
-                             .section = rsinfo->postponed_def ?
-                             SECTION_POST_DATA : statisticsDumpSection(rsinfo),
+                             .section = rsinfo->section,
                              .createStmt = out->data,
-                             .deps = deps,
-                             .nDeps = ndeps));
+                             .deps = dobj->dependencies,
+                             .nDeps = dobj->nDeps));
 
    destroyPQExpBuffer(out);
    destroyPQExpBuffer(query);
                break;
            case DO_REL_STATS:
                /* stats section varies by parent object type, DATA or POST */
-               if (statisticsDumpSection((RelStatsInfo *) dobj) == SECTION_DATA)
+               if (((RelStatsInfo *) dobj)->section == SECTION_DATA)
                {
                    addObjectDependency(dobj, preDataBound->dumpId);
                    addObjectDependency(postDataBound, dobj->dumpId);
 
    bool       *notnull_islocal;    /* true if NOT NULL has local definition */
    struct _attrDefInfo **attrdefs; /* DEFAULT expressions */
    struct _constraintInfo *checkexprs; /* CHECK constraints */
+   struct _relStatsInfo *stats;    /* only set for matviews */
    bool        needs_override; /* has GENERATED ALWAYS AS IDENTITY */
    char       *amname;         /* relation access method */
 
     */
    char      **indAttNames;    /* attnames of the index, in order */
    int32       nindAttNames;   /* number of attnames stored (can be 0) */
-   bool        postponed_def;  /* stats must be postponed into post-data */
+   teSection   section;        /* stats may appear in data or post-data */
 } RelStatsInfo;
 
 typedef struct _statsExtInfo
 
        RelStatsInfo *nextinfo = (RelStatsInfo *) nextobj;
 
        if (nextinfo->relkind == RELKIND_MATVIEW)
-           nextinfo->postponed_def = true;
+           nextinfo->section = SECTION_POST_DATA;
    }
 }