Skip to content

Commit a0a4601

Browse files
committed
Matview statistics depend on matview data.
REFRESH MATERIALIZED VIEW replaces the storage, which resets statistics, so statistics must be restored afterward. If both statistics and data are being dumped for a materialized view, add a dependency from the former to the latter. Defer the statistics to SECTION_POST_DATA, and use RESTORE_PASS_POST_ACL. Reported-by: Ashutosh Bapat <[email protected]> Reviewed-by: Ashutosh Bapat <[email protected]> Discussion: https://postgr.es/m/CAExHW5s47kmubpbbRJzSM-Zfe0Tj2O3GBagB7YAyE8rQ-V24Uw@mail.gmail.com
1 parent 775a06d commit a0a4601

File tree

4 files changed

+91
-60
lines changed

4 files changed

+91
-60
lines changed

src/bin/pg_dump/pg_backup_archiver.c

+34-12
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ static void processEncodingEntry(ArchiveHandle *AH, TocEntry *te);
7272
static void processStdStringsEntry(ArchiveHandle *AH, TocEntry *te);
7373
static void processSearchPathEntry(ArchiveHandle *AH, TocEntry *te);
7474
static int _tocEntryRequired(TocEntry *te, teSection curSection, ArchiveHandle *AH);
75-
static RestorePass _tocEntryRestorePass(TocEntry *te);
75+
static RestorePass _tocEntryRestorePass(ArchiveHandle *AH, TocEntry *te);
7676
static bool _tocEntryIsACL(TocEntry *te);
7777
static void _disableTriggersIfNecessary(ArchiveHandle *AH, TocEntry *te);
7878
static void _enableTriggersIfNecessary(ArchiveHandle *AH, TocEntry *te);
@@ -102,7 +102,8 @@ static void pending_list_append(TocEntry *l, TocEntry *te);
102102
static void pending_list_remove(TocEntry *te);
103103
static int TocEntrySizeCompareQsort(const void *p1, const void *p2);
104104
static int TocEntrySizeCompareBinaryheap(void *p1, void *p2, void *arg);
105-
static void move_to_ready_heap(TocEntry *pending_list,
105+
static void move_to_ready_heap(ArchiveHandle *AH,
106+
TocEntry *pending_list,
106107
binaryheap *ready_heap,
107108
RestorePass pass);
108109
static TocEntry *pop_next_work_item(binaryheap *ready_heap,
@@ -748,7 +749,7 @@ RestoreArchive(Archive *AHX)
748749
if ((te->reqs & (REQ_SCHEMA | REQ_DATA | REQ_STATS)) == 0)
749750
continue; /* ignore if not to be dumped at all */
750751

751-
switch (_tocEntryRestorePass(te))
752+
switch (_tocEntryRestorePass(AH, te))
752753
{
753754
case RESTORE_PASS_MAIN:
754755
(void) restore_toc_entry(AH, te, false);
@@ -767,7 +768,7 @@ RestoreArchive(Archive *AHX)
767768
for (te = AH->toc->next; te != AH->toc; te = te->next)
768769
{
769770
if ((te->reqs & (REQ_SCHEMA | REQ_DATA | REQ_STATS)) != 0 &&
770-
_tocEntryRestorePass(te) == RESTORE_PASS_ACL)
771+
_tocEntryRestorePass(AH, te) == RESTORE_PASS_ACL)
771772
(void) restore_toc_entry(AH, te, false);
772773
}
773774
}
@@ -777,7 +778,7 @@ RestoreArchive(Archive *AHX)
777778
for (te = AH->toc->next; te != AH->toc; te = te->next)
778779
{
779780
if ((te->reqs & (REQ_SCHEMA | REQ_DATA | REQ_STATS)) != 0 &&
780-
_tocEntryRestorePass(te) == RESTORE_PASS_POST_ACL)
781+
_tocEntryRestorePass(AH, te) == RESTORE_PASS_POST_ACL)
781782
(void) restore_toc_entry(AH, te, false);
782783
}
783784
}
@@ -3219,7 +3220,7 @@ _tocEntryRequired(TocEntry *te, teSection curSection, ArchiveHandle *AH)
32193220
* See notes with the RestorePass typedef in pg_backup_archiver.h.
32203221
*/
32213222
static RestorePass
3222-
_tocEntryRestorePass(TocEntry *te)
3223+
_tocEntryRestorePass(ArchiveHandle *AH, TocEntry *te)
32233224
{
32243225
/* "ACL LANGUAGE" was a crock emitted only in PG 7.4 */
32253226
if (strcmp(te->desc, "ACL") == 0 ||
@@ -3240,6 +3241,26 @@ _tocEntryRestorePass(TocEntry *te)
32403241
strncmp(te->tag, "EVENT TRIGGER ", 14) == 0)
32413242
return RESTORE_PASS_POST_ACL;
32423243

3244+
/*
3245+
* If statistics data is dependent on materialized view data, it must be
3246+
* deferred to RESTORE_PASS_POST_ACL.
3247+
*/
3248+
if (strcmp(te->desc, "STATISTICS DATA") == 0)
3249+
{
3250+
for (int i = 0; i < te->nDeps; i++)
3251+
{
3252+
DumpId depid = te->dependencies[i];
3253+
3254+
if (depid <= AH->maxDumpId && AH->tocsByDumpId[depid] != NULL)
3255+
{
3256+
TocEntry *otherte = AH->tocsByDumpId[depid];
3257+
3258+
if (strcmp(otherte->desc, "MATERIALIZED VIEW DATA") == 0)
3259+
return RESTORE_PASS_POST_ACL;
3260+
}
3261+
}
3262+
}
3263+
32433264
/* All else can be handled in the main pass. */
32443265
return RESTORE_PASS_MAIN;
32453266
}
@@ -4249,7 +4270,7 @@ restore_toc_entries_prefork(ArchiveHandle *AH, TocEntry *pending_list)
42494270
* not set skipped_some in this case, since by assumption no main-pass
42504271
* items could depend on these.
42514272
*/
4252-
if (_tocEntryRestorePass(next_work_item) != RESTORE_PASS_MAIN)
4273+
if (_tocEntryRestorePass(AH, next_work_item) != RESTORE_PASS_MAIN)
42534274
do_now = false;
42544275

42554276
if (do_now)
@@ -4331,7 +4352,7 @@ restore_toc_entries_parallel(ArchiveHandle *AH, ParallelState *pstate,
43314352
* process in the current restore pass.
43324353
*/
43334354
AH->restorePass = RESTORE_PASS_MAIN;
4334-
move_to_ready_heap(pending_list, ready_heap, AH->restorePass);
4355+
move_to_ready_heap(AH, pending_list, ready_heap, AH->restorePass);
43354356

43364357
/*
43374358
* main parent loop
@@ -4380,7 +4401,7 @@ restore_toc_entries_parallel(ArchiveHandle *AH, ParallelState *pstate,
43804401
/* Advance to next restore pass */
43814402
AH->restorePass++;
43824403
/* That probably allows some stuff to be made ready */
4383-
move_to_ready_heap(pending_list, ready_heap, AH->restorePass);
4404+
move_to_ready_heap(AH, pending_list, ready_heap, AH->restorePass);
43844405
/* Loop around to see if anything's now ready */
43854406
continue;
43864407
}
@@ -4551,7 +4572,8 @@ TocEntrySizeCompareBinaryheap(void *p1, void *p2, void *arg)
45514572
* which applies the same logic one-at-a-time.)
45524573
*/
45534574
static void
4554-
move_to_ready_heap(TocEntry *pending_list,
4575+
move_to_ready_heap(ArchiveHandle *AH,
4576+
TocEntry *pending_list,
45554577
binaryheap *ready_heap,
45564578
RestorePass pass)
45574579
{
@@ -4564,7 +4586,7 @@ move_to_ready_heap(TocEntry *pending_list,
45644586
next_te = te->pending_next;
45654587

45664588
if (te->depCount == 0 &&
4567-
_tocEntryRestorePass(te) == pass)
4589+
_tocEntryRestorePass(AH, te) == pass)
45684590
{
45694591
/* Remove it from pending_list ... */
45704592
pending_list_remove(te);
@@ -4958,7 +4980,7 @@ reduce_dependencies(ArchiveHandle *AH, TocEntry *te,
49584980
* memberships changed.
49594981
*/
49604982
if (otherte->depCount == 0 &&
4961-
_tocEntryRestorePass(otherte) == AH->restorePass &&
4983+
_tocEntryRestorePass(AH, otherte) == AH->restorePass &&
49624984
otherte->pending_prev != NULL &&
49634985
ready_heap != NULL)
49644986
{

src/bin/pg_dump/pg_dump.c

+54-46
Original file line numberDiff line numberDiff line change
@@ -3002,6 +3002,19 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo)
30023002

30033003
tbinfo->dataObj = tdinfo;
30043004

3005+
/*
3006+
* Materialized view statistics must be restored after the data, because
3007+
* REFRESH MATERIALIZED VIEW replaces the storage and resets the stats.
3008+
*
3009+
* The dependency is added here because the statistics objects are created
3010+
* first.
3011+
*/
3012+
if (tbinfo->relkind == RELKIND_MATVIEW && tbinfo->stats != NULL)
3013+
{
3014+
tbinfo->stats->section = SECTION_POST_DATA;
3015+
addObjectDependency(&tbinfo->stats->dobj, tdinfo->dobj.dumpId);
3016+
}
3017+
30053018
/* Make sure that we'll collect per-column info for this table. */
30063019
tbinfo->interesting = true;
30073020
}
@@ -6893,7 +6906,32 @@ getRelationStatistics(Archive *fout, DumpableObject *rel, int32 relpages,
68936906
info->relkind = relkind;
68946907
info->indAttNames = indAttNames;
68956908
info->nindAttNames = nindAttNames;
6896-
info->postponed_def = false;
6909+
6910+
/*
6911+
* Ordinarily, stats go in SECTION_DATA for tables and
6912+
* SECTION_POST_DATA for indexes.
6913+
*
6914+
* However, the section may be updated later for materialized view
6915+
* stats. REFRESH MATERIALIZED VIEW replaces the storage and resets
6916+
* the stats, so the stats must be restored after the data. Also, the
6917+
* materialized view definition may be postponed to SECTION_POST_DATA
6918+
* (see repairMatViewBoundaryMultiLoop()).
6919+
*/
6920+
switch (info->relkind)
6921+
{
6922+
case RELKIND_RELATION:
6923+
case RELKIND_PARTITIONED_TABLE:
6924+
case RELKIND_MATVIEW:
6925+
info->section = SECTION_DATA;
6926+
break;
6927+
case RELKIND_INDEX:
6928+
case RELKIND_PARTITIONED_INDEX:
6929+
info->section = SECTION_POST_DATA;
6930+
break;
6931+
default:
6932+
pg_fatal("cannot dump statistics for relation kind '%c'",
6933+
info->relkind);
6934+
}
68976935

68986936
return info;
68996937
}
@@ -7292,9 +7330,17 @@ getTables(Archive *fout, int *numTables)
72927330

72937331
/* Add statistics */
72947332
if (tblinfo[i].interesting)
7295-
getRelationStatistics(fout, &tblinfo[i].dobj, tblinfo[i].relpages,
7296-
PQgetvalue(res, i, i_reltuples),
7297-
relallvisible, tblinfo[i].relkind, NULL, 0);
7333+
{
7334+
RelStatsInfo *stats;
7335+
7336+
stats = getRelationStatistics(fout, &tblinfo[i].dobj,
7337+
tblinfo[i].relpages,
7338+
PQgetvalue(res, i, i_reltuples),
7339+
relallvisible,
7340+
tblinfo[i].relkind, NULL, 0);
7341+
if (tblinfo[i].relkind == RELKIND_MATVIEW)
7342+
tblinfo[i].stats = stats;
7343+
}
72987344

72997345
/*
73007346
* Read-lock target tables to make sure they aren't DROPPED or altered
@@ -10491,34 +10537,6 @@ appendNamedArgument(PQExpBuffer out, Archive *fout, const char *argname,
1049110537
appendPQExpBuffer(out, "::%s", argtype);
1049210538
}
1049310539

10494-
/*
10495-
* Decide which section to use based on the relkind of the parent object.
10496-
*
10497-
* NB: materialized views may be postponed from SECTION_PRE_DATA to
10498-
* SECTION_POST_DATA to resolve some kinds of dependency problems. If so, the
10499-
* matview stats will also be postponed to SECTION_POST_DATA. See
10500-
* repairMatViewBoundaryMultiLoop().
10501-
*/
10502-
static teSection
10503-
statisticsDumpSection(const RelStatsInfo *rsinfo)
10504-
{
10505-
switch (rsinfo->relkind)
10506-
{
10507-
case RELKIND_RELATION:
10508-
case RELKIND_PARTITIONED_TABLE:
10509-
case RELKIND_MATVIEW:
10510-
return SECTION_DATA;
10511-
case RELKIND_INDEX:
10512-
case RELKIND_PARTITIONED_INDEX:
10513-
return SECTION_POST_DATA;
10514-
default:
10515-
pg_fatal("cannot dump statistics for relation kind '%c'",
10516-
rsinfo->relkind);
10517-
}
10518-
10519-
return 0; /* keep compiler quiet */
10520-
}
10521-
1052210540
/*
1052310541
* dumpRelationStats --
1052410542
*
@@ -10531,8 +10549,6 @@ dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
1053110549
PGresult *res;
1053210550
PQExpBuffer query;
1053310551
PQExpBuffer out;
10534-
DumpId *deps = NULL;
10535-
int ndeps = 0;
1053610552
int i_attname;
1053710553
int i_inherited;
1053810554
int i_null_frac;
@@ -10553,13 +10569,6 @@ dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
1055310569
if (!fout->dopt->dumpStatistics)
1055410570
return;
1055510571

10556-
/* dependent on the relation definition, if doing schema */
10557-
if (fout->dopt->dumpSchema)
10558-
{
10559-
deps = dobj->dependencies;
10560-
ndeps = dobj->nDeps;
10561-
}
10562-
1056310572
query = createPQExpBuffer();
1056410573
if (!fout->is_prepared[PREPQUERY_GETATTRIBUTESTATS])
1056510574
{
@@ -10737,11 +10746,10 @@ dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
1073710746
ARCHIVE_OPTS(.tag = dobj->name,
1073810747
.namespace = dobj->namespace->dobj.name,
1073910748
.description = "STATISTICS DATA",
10740-
.section = rsinfo->postponed_def ?
10741-
SECTION_POST_DATA : statisticsDumpSection(rsinfo),
10749+
.section = rsinfo->section,
1074210750
.createStmt = out->data,
10743-
.deps = deps,
10744-
.nDeps = ndeps));
10751+
.deps = dobj->dependencies,
10752+
.nDeps = dobj->nDeps));
1074510753

1074610754
destroyPQExpBuffer(out);
1074710755
destroyPQExpBuffer(query);
@@ -19429,7 +19437,7 @@ addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
1942919437
break;
1943019438
case DO_REL_STATS:
1943119439
/* stats section varies by parent object type, DATA or POST */
19432-
if (statisticsDumpSection((RelStatsInfo *) dobj) == SECTION_DATA)
19440+
if (((RelStatsInfo *) dobj)->section == SECTION_DATA)
1943319441
{
1943419442
addObjectDependency(dobj, preDataBound->dumpId);
1943519443
addObjectDependency(postDataBound, dobj->dumpId);

src/bin/pg_dump/pg_dump.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,7 @@ typedef struct _tableInfo
369369
bool *notnull_islocal; /* true if NOT NULL has local definition */
370370
struct _attrDefInfo **attrdefs; /* DEFAULT expressions */
371371
struct _constraintInfo *checkexprs; /* CHECK constraints */
372+
struct _relStatsInfo *stats; /* only set for matviews */
372373
bool needs_override; /* has GENERATED ALWAYS AS IDENTITY */
373374
char *amname; /* relation access method */
374375

@@ -449,7 +450,7 @@ typedef struct _relStatsInfo
449450
*/
450451
char **indAttNames; /* attnames of the index, in order */
451452
int32 nindAttNames; /* number of attnames stored (can be 0) */
452-
bool postponed_def; /* stats must be postponed into post-data */
453+
teSection section; /* stats may appear in data or post-data */
453454
} RelStatsInfo;
454455

455456
typedef struct _statsExtInfo

src/bin/pg_dump/pg_dump_sort.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -820,7 +820,7 @@ repairMatViewBoundaryMultiLoop(DumpableObject *boundaryobj,
820820
RelStatsInfo *nextinfo = (RelStatsInfo *) nextobj;
821821

822822
if (nextinfo->relkind == RELKIND_MATVIEW)
823-
nextinfo->postponed_def = true;
823+
nextinfo->section = SECTION_POST_DATA;
824824
}
825825
}
826826

0 commit comments

Comments
 (0)