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;
}
}