@@ -72,7 +72,7 @@ static void processEncodingEntry(ArchiveHandle *AH, TocEntry *te);
72
72
static void processStdStringsEntry (ArchiveHandle * AH , TocEntry * te );
73
73
static void processSearchPathEntry (ArchiveHandle * AH , TocEntry * te );
74
74
static int _tocEntryRequired (TocEntry * te , teSection curSection , ArchiveHandle * AH );
75
- static RestorePass _tocEntryRestorePass (ArchiveHandle * AH , TocEntry * te );
75
+ static RestorePass _tocEntryRestorePass (TocEntry * te );
76
76
static bool _tocEntryIsACL (TocEntry * te );
77
77
static void _disableTriggersIfNecessary (ArchiveHandle * AH , TocEntry * te );
78
78
static void _enableTriggersIfNecessary (ArchiveHandle * AH , TocEntry * te );
@@ -102,8 +102,7 @@ static void pending_list_append(TocEntry *l, TocEntry *te);
102
102
static void pending_list_remove (TocEntry * te );
103
103
static int TocEntrySizeCompareQsort (const void * p1 , const void * p2 );
104
104
static int TocEntrySizeCompareBinaryheap (void * p1 , void * p2 , void * arg );
105
- static void move_to_ready_heap (ArchiveHandle * AH ,
106
- TocEntry * pending_list ,
105
+ static void move_to_ready_heap (TocEntry * pending_list ,
107
106
binaryheap * ready_heap ,
108
107
RestorePass pass );
109
108
static TocEntry * pop_next_work_item (binaryheap * ready_heap ,
@@ -749,7 +748,7 @@ RestoreArchive(Archive *AHX)
749
748
if ((te -> reqs & (REQ_SCHEMA | REQ_DATA | REQ_STATS )) == 0 )
750
749
continue ; /* ignore if not to be dumped at all */
751
750
752
- switch (_tocEntryRestorePass (AH , te ))
751
+ switch (_tocEntryRestorePass (te ))
753
752
{
754
753
case RESTORE_PASS_MAIN :
755
754
(void ) restore_toc_entry (AH , te , false);
@@ -768,7 +767,7 @@ RestoreArchive(Archive *AHX)
768
767
for (te = AH -> toc -> next ; te != AH -> toc ; te = te -> next )
769
768
{
770
769
if ((te -> reqs & (REQ_SCHEMA | REQ_DATA | REQ_STATS )) != 0 &&
771
- _tocEntryRestorePass (AH , te ) == RESTORE_PASS_ACL )
770
+ _tocEntryRestorePass (te ) == RESTORE_PASS_ACL )
772
771
(void ) restore_toc_entry (AH , te , false);
773
772
}
774
773
}
@@ -778,7 +777,7 @@ RestoreArchive(Archive *AHX)
778
777
for (te = AH -> toc -> next ; te != AH -> toc ; te = te -> next )
779
778
{
780
779
if ((te -> reqs & (REQ_SCHEMA | REQ_DATA | REQ_STATS )) != 0 &&
781
- _tocEntryRestorePass (AH , te ) == RESTORE_PASS_POST_ACL )
780
+ _tocEntryRestorePass (te ) == RESTORE_PASS_POST_ACL )
782
781
(void ) restore_toc_entry (AH , te , false);
783
782
}
784
783
}
@@ -1266,6 +1265,9 @@ ArchiveEntry(Archive *AHX, CatalogId catalogId, DumpId dumpId,
1266
1265
newToc -> dataDumperArg = opts -> dumpArg ;
1267
1266
newToc -> hadDumper = opts -> dumpFn ? true : false;
1268
1267
1268
+ newToc -> defnDumper = opts -> defnFn ;
1269
+ newToc -> defnDumperArg = opts -> defnArg ;
1270
+
1269
1271
newToc -> formatData = NULL ;
1270
1272
newToc -> dataLength = 0 ;
1271
1273
@@ -2621,7 +2623,45 @@ WriteToc(ArchiveHandle *AH)
2621
2623
WriteStr (AH , te -> tag );
2622
2624
WriteStr (AH , te -> desc );
2623
2625
WriteInt (AH , te -> section );
2624
- WriteStr (AH , te -> defn );
2626
+
2627
+ if (te -> defnLen )
2628
+ {
2629
+ /*
2630
+ * defnLen should only be set for custom format's second call to
2631
+ * WriteToc(), which rewrites the TOC in place to update data
2632
+ * offsets. Instead of calling the defnDumper a second time
2633
+ * (which could involve re-executing queries), just skip writing
2634
+ * the entry. While regenerating the definition should
2635
+ * theoretically produce the same result as before, it's expensive
2636
+ * and feels risky.
2637
+ *
2638
+ * The custom format only calls WriteToc() a second time if
2639
+ * fseeko() is usable (see _CloseArchive() in pg_backup_custom.c),
2640
+ * so we can safely use it without checking. For other formats,
2641
+ * we fail because one of our assumptions must no longer hold
2642
+ * true.
2643
+ *
2644
+ * XXX This is certainly a layering violation, but the alternative
2645
+ * is an awkward and complicated callback infrastructure for this
2646
+ * special case. This might be worth revisiting in the future.
2647
+ */
2648
+ if (AH -> format != archCustom )
2649
+ pg_fatal ("unexpected TOC entry in WriteToc(): %d %s %s" ,
2650
+ te -> dumpId , te -> desc , te -> tag );
2651
+
2652
+ if (fseeko (AH -> FH , te -> defnLen , SEEK_CUR != 0 ))
2653
+ pg_fatal ("error during file seek: %m" );
2654
+ }
2655
+ else if (te -> defnDumper )
2656
+ {
2657
+ char * defn = te -> defnDumper ((Archive * ) AH , te -> defnDumperArg , te );
2658
+
2659
+ te -> defnLen = WriteStr (AH , defn );
2660
+ pg_free (defn );
2661
+ }
2662
+ else
2663
+ WriteStr (AH , te -> defn );
2664
+
2625
2665
WriteStr (AH , te -> dropStmt );
2626
2666
WriteStr (AH , te -> copyStmt );
2627
2667
WriteStr (AH , te -> namespace );
@@ -3220,7 +3260,7 @@ _tocEntryRequired(TocEntry *te, teSection curSection, ArchiveHandle *AH)
3220
3260
* See notes with the RestorePass typedef in pg_backup_archiver.h.
3221
3261
*/
3222
3262
static RestorePass
3223
- _tocEntryRestorePass (ArchiveHandle * AH , TocEntry * te )
3263
+ _tocEntryRestorePass (TocEntry * te )
3224
3264
{
3225
3265
/* "ACL LANGUAGE" was a crock emitted only in PG 7.4 */
3226
3266
if (strcmp (te -> desc , "ACL" ) == 0 ||
@@ -3243,23 +3283,17 @@ _tocEntryRestorePass(ArchiveHandle *AH, TocEntry *te)
3243
3283
3244
3284
/*
3245
3285
* If statistics data is dependent on materialized view data, it must be
3246
- * deferred to RESTORE_PASS_POST_ACL.
3286
+ * deferred to RESTORE_PASS_POST_ACL. Those entries are already marked
3287
+ * with SECTION_POST_DATA, and some other stats entries (e.g., index
3288
+ * stats) will also be marked SECTION_POST_DATA. Additionally, our
3289
+ * lookahead code in fetchAttributeStats() assumes that we dump all
3290
+ * statistics data entries in TOC order. To ensure this assumption holds,
3291
+ * we move all statistics data entries in SECTION_POST_DATA to
3292
+ * RESTORE_PASS_POST_ACL.
3247
3293
*/
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
- }
3294
+ if (strcmp (te -> desc , "STATISTICS DATA" ) == 0 &&
3295
+ te -> section == SECTION_POST_DATA )
3296
+ return RESTORE_PASS_POST_ACL ;
3263
3297
3264
3298
/* All else can be handled in the main pass. */
3265
3299
return RESTORE_PASS_MAIN ;
@@ -3849,7 +3883,7 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, const char *pfx)
3849
3883
3850
3884
/*
3851
3885
* Actually print the definition. Normally we can just print the defn
3852
- * string if any, but we have three special cases:
3886
+ * string if any, but we have four special cases:
3853
3887
*
3854
3888
* 1. A crude hack for suppressing AUTHORIZATION clause that old pg_dump
3855
3889
* versions put into CREATE SCHEMA. Don't mutate the variant for schema
@@ -3862,6 +3896,11 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, const char *pfx)
3862
3896
* 3. ACL LARGE OBJECTS entries need special processing because they
3863
3897
* contain only one copy of the ACL GRANT/REVOKE commands, which we must
3864
3898
* apply to each large object listed in the associated BLOB METADATA.
3899
+ *
3900
+ * 4. Entries with a defnDumper need to call it to generate the
3901
+ * definition. This is primarily intended to provide a way to save memory
3902
+ * for objects that would otherwise need a lot of it (e.g., statistics
3903
+ * data).
3865
3904
*/
3866
3905
if (ropt -> noOwner &&
3867
3906
strcmp (te -> desc , "SCHEMA" ) == 0 && strncmp (te -> defn , "--" , 2 ) != 0 )
@@ -3877,6 +3916,40 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, const char *pfx)
3877
3916
{
3878
3917
IssueACLPerBlob (AH , te );
3879
3918
}
3919
+ else if (te -> defnLen && AH -> format != archTar )
3920
+ {
3921
+ /*
3922
+ * If defnLen is set, the defnDumper has already been called for this
3923
+ * TOC entry. We don't normally expect a defnDumper to be called for
3924
+ * a TOC entry a second time in _printTocEntry(), but there's an
3925
+ * exception. The tar format first calls WriteToc(), which scans the
3926
+ * entire TOC, and then it later calls RestoreArchive() to generate
3927
+ * restore.sql, which scans the TOC again. There doesn't appear to be
3928
+ * a good way to prevent a second defnDumper call in this case without
3929
+ * storing the definition in memory, which defeats the purpose. This
3930
+ * second defnDumper invocation should generate the same output as the
3931
+ * first, but even if it doesn't, the worst-case scenario is that the
3932
+ * content of the archive and restore.sql (which isn't used by
3933
+ * pg_restore) will differ.
3934
+ *
3935
+ * In all other cases, encountering a TOC entry a second time in
3936
+ * _printTocEntry() is unexpected, so we fail because one of our
3937
+ * assumptions must no longer hold true.
3938
+ *
3939
+ * XXX This is certainly a layering violation, but the alternative is
3940
+ * an awkward and complicated callback infrastructure for this special
3941
+ * case. This might be worth revisiting in the future.
3942
+ */
3943
+ pg_fatal ("unexpected TOC entry in _printTocEntry(): %d %s %s" ,
3944
+ te -> dumpId , te -> desc , te -> tag );
3945
+ }
3946
+ else if (te -> defnDumper )
3947
+ {
3948
+ char * defn = te -> defnDumper ((Archive * ) AH , te -> defnDumperArg , te );
3949
+
3950
+ te -> defnLen = ahprintf (AH , "%s\n\n" , defn );
3951
+ pg_free (defn );
3952
+ }
3880
3953
else if (te -> defn && strlen (te -> defn ) > 0 )
3881
3954
{
3882
3955
ahprintf (AH , "%s\n\n" , te -> defn );
@@ -4270,7 +4343,7 @@ restore_toc_entries_prefork(ArchiveHandle *AH, TocEntry *pending_list)
4270
4343
* not set skipped_some in this case, since by assumption no main-pass
4271
4344
* items could depend on these.
4272
4345
*/
4273
- if (_tocEntryRestorePass (AH , next_work_item ) != RESTORE_PASS_MAIN )
4346
+ if (_tocEntryRestorePass (next_work_item ) != RESTORE_PASS_MAIN )
4274
4347
do_now = false;
4275
4348
4276
4349
if (do_now )
@@ -4352,7 +4425,7 @@ restore_toc_entries_parallel(ArchiveHandle *AH, ParallelState *pstate,
4352
4425
* process in the current restore pass.
4353
4426
*/
4354
4427
AH -> restorePass = RESTORE_PASS_MAIN ;
4355
- move_to_ready_heap (AH , pending_list , ready_heap , AH -> restorePass );
4428
+ move_to_ready_heap (pending_list , ready_heap , AH -> restorePass );
4356
4429
4357
4430
/*
4358
4431
* main parent loop
@@ -4401,7 +4474,7 @@ restore_toc_entries_parallel(ArchiveHandle *AH, ParallelState *pstate,
4401
4474
/* Advance to next restore pass */
4402
4475
AH -> restorePass ++ ;
4403
4476
/* That probably allows some stuff to be made ready */
4404
- move_to_ready_heap (AH , pending_list , ready_heap , AH -> restorePass );
4477
+ move_to_ready_heap (pending_list , ready_heap , AH -> restorePass );
4405
4478
/* Loop around to see if anything's now ready */
4406
4479
continue ;
4407
4480
}
@@ -4572,8 +4645,7 @@ TocEntrySizeCompareBinaryheap(void *p1, void *p2, void *arg)
4572
4645
* which applies the same logic one-at-a-time.)
4573
4646
*/
4574
4647
static void
4575
- move_to_ready_heap (ArchiveHandle * AH ,
4576
- TocEntry * pending_list ,
4648
+ move_to_ready_heap (TocEntry * pending_list ,
4577
4649
binaryheap * ready_heap ,
4578
4650
RestorePass pass )
4579
4651
{
@@ -4586,7 +4658,7 @@ move_to_ready_heap(ArchiveHandle *AH,
4586
4658
next_te = te -> pending_next ;
4587
4659
4588
4660
if (te -> depCount == 0 &&
4589
- _tocEntryRestorePass (AH , te ) == pass )
4661
+ _tocEntryRestorePass (te ) == pass )
4590
4662
{
4591
4663
/* Remove it from pending_list ... */
4592
4664
pending_list_remove (te );
@@ -4980,7 +5052,7 @@ reduce_dependencies(ArchiveHandle *AH, TocEntry *te,
4980
5052
* memberships changed.
4981
5053
*/
4982
5054
if (otherte -> depCount == 0 &&
4983
- _tocEntryRestorePass (AH , otherte ) == AH -> restorePass &&
5055
+ _tocEntryRestorePass (otherte ) == AH -> restorePass &&
4984
5056
otherte -> pending_prev != NULL &&
4985
5057
ready_heap != NULL )
4986
5058
{
0 commit comments