Skip to content

Commit e72d7d8

Browse files
committed
Handle extension members when first setting object dump flags in pg_dump.
pg_dump's original approach to handling extension member objects was to run around and clear (or set) their dump flags rather late in its data collection process. Unfortunately, quite a lot of code expects those flags to be valid before that; which was an entirely reasonable expectation before we added extensions. In particular, this explains Karsten Hilbert's recent report of pg_upgrade failing on a database in which an extension has been installed into the pg_catalog schema. Its objects are initially marked as not-to-be-dumped on the strength of their schema, and later we change them to must-dump because we're doing a binary upgrade of their extension; but we've already skipped essential tasks like making associated DO_SHELL_TYPE objects. To fix, collect extension membership data first, and incorporate it in the initial setting of the dump flags, so that those are once again correct from the get-go. This has the undesirable side effect of slightly lengthening the time taken before pg_dump acquires table locks, but testing suggests that the increase in that window is not very much. Along the way, get rid of ugly special-case logic for deciding whether to dump procedural languages, FDWs, and foreign servers; dump decisions for those are now correct up-front, too. In 9.3 and up, this also fixes erroneous logic about when to dump event triggers (basically, they were *always* dumped before). In 9.5 and up, transform objects had that problem too. Since this problem came in with extensions, back-patch to all supported versions.
1 parent 5b5fea2 commit e72d7d8

File tree

3 files changed

+358
-189
lines changed

3 files changed

+358
-189
lines changed

src/bin/pg_dump/common.c

Lines changed: 128 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -40,37 +40,38 @@ static int numCatalogIds = 0;
4040

4141
/*
4242
* These variables are static to avoid the notational cruft of having to pass
43-
* them into findTableByOid() and friends. For each of these arrays, we
44-
* build a sorted-by-OID index array immediately after it's built, and then
45-
* we use binary search in findTableByOid() and friends. (qsort'ing the base
46-
* arrays themselves would be simpler, but it doesn't work because pg_dump.c
47-
* may have already established pointers between items.)
48-
*/
49-
static TableInfo *tblinfo;
50-
static TypeInfo *typinfo;
51-
static FuncInfo *funinfo;
52-
static OprInfo *oprinfo;
53-
static NamespaceInfo *nspinfo;
54-
static int numTables;
55-
static int numTypes;
56-
static int numFuncs;
57-
static int numOperators;
58-
static int numCollations;
59-
static int numNamespaces;
43+
* them into findTableByOid() and friends. For each of these arrays, we build
44+
* a sorted-by-OID index array immediately after the objects are fetched,
45+
* and then we use binary search in findTableByOid() and friends. (qsort'ing
46+
* the object arrays themselves would be simpler, but it doesn't work because
47+
* pg_dump.c may have already established pointers between items.)
48+
*/
6049
static DumpableObject **tblinfoindex;
6150
static DumpableObject **typinfoindex;
6251
static DumpableObject **funinfoindex;
6352
static DumpableObject **oprinfoindex;
6453
static DumpableObject **collinfoindex;
6554
static DumpableObject **nspinfoindex;
55+
static DumpableObject **extinfoindex;
56+
static int numTables;
57+
static int numTypes;
58+
static int numFuncs;
59+
static int numOperators;
60+
static int numCollations;
61+
static int numNamespaces;
62+
static int numExtensions;
6663

64+
/* This is an array of object identities, not actual DumpableObjects */
65+
static ExtensionMemberId *extmembers;
66+
static int numextmembers;
6767

6868
static void flagInhTables(TableInfo *tbinfo, int numTables,
6969
InhInfo *inhinfo, int numInherits);
7070
static void flagInhAttrs(DumpOptions *dopt, TableInfo *tblinfo, int numTables);
7171
static DumpableObject **buildIndexArray(void *objArray, int numObjs,
7272
Size objSize);
7373
static int DOCatalogIdCompare(const void *p1, const void *p2);
74+
static int ExtensionMemberIdCompare(const void *p1, const void *p2);
7475
static void findParentsByOid(TableInfo *self,
7576
InhInfo *inhinfo, int numInherits);
7677
static int strInArray(const char *pattern, char **arr, int arr_size);
@@ -83,10 +84,14 @@ static int strInArray(const char *pattern, char **arr, int arr_size);
8384
TableInfo *
8485
getSchemaData(Archive *fout, int *numTablesPtr)
8586
{
87+
TableInfo *tblinfo;
88+
TypeInfo *typinfo;
89+
FuncInfo *funinfo;
90+
OprInfo *oprinfo;
91+
CollInfo *collinfo;
92+
NamespaceInfo *nspinfo;
8693
ExtensionInfo *extinfo;
8794
InhInfo *inhinfo;
88-
CollInfo *collinfo;
89-
int numExtensions;
9095
int numAggregates;
9196
int numInherits;
9297
int numRules;
@@ -105,6 +110,20 @@ getSchemaData(Archive *fout, int *numTablesPtr)
105110
int numDefaultACLs;
106111
int numEventTriggers;
107112

113+
/*
114+
* We must read extensions and extension membership info first, because
115+
* extension membership needs to be consultable during decisions about
116+
* whether other objects are to be dumped.
117+
*/
118+
if (g_verbose)
119+
write_msg(NULL, "reading extensions\n");
120+
extinfo = getExtensions(fout, &numExtensions);
121+
extinfoindex = buildIndexArray(extinfo, numExtensions, sizeof(ExtensionInfo));
122+
123+
if (g_verbose)
124+
write_msg(NULL, "identifying extension members\n");
125+
getExtensionMembership(fout, extinfo, numExtensions);
126+
108127
if (g_verbose)
109128
write_msg(NULL, "reading schemas\n");
110129
nspinfo = getNamespaces(fout, &numNamespaces);
@@ -124,10 +143,6 @@ getSchemaData(Archive *fout, int *numTablesPtr)
124143
/* Do this after we've built tblinfoindex */
125144
getOwnedSeqs(fout, tblinfo, numTables);
126145

127-
if (g_verbose)
128-
write_msg(NULL, "reading extensions\n");
129-
extinfo = getExtensions(fout, &numExtensions);
130-
131146
if (g_verbose)
132147
write_msg(NULL, "reading user-defined functions\n");
133148
funinfo = getFuncs(fout, &numFuncs);
@@ -214,14 +229,10 @@ getSchemaData(Archive *fout, int *numTablesPtr)
214229
write_msg(NULL, "reading event triggers\n");
215230
getEventTriggers(fout, &numEventTriggers);
216231

217-
/*
218-
* Identify extension member objects and mark them as not to be dumped.
219-
* This must happen after reading all objects that can be direct members
220-
* of extensions, but before we begin to process table subsidiary objects.
221-
*/
232+
/* Identify extension configuration tables that should be dumped */
222233
if (g_verbose)
223-
write_msg(NULL, "finding extension members\n");
224-
getExtensionMembership(fout, extinfo, numExtensions);
234+
write_msg(NULL, "finding extension tables\n");
235+
processExtensionTables(fout, extinfo, numExtensions);
225236

226237
/* Link tables to parents, mark parents of target tables interesting */
227238
if (g_verbose)
@@ -764,6 +775,93 @@ findNamespaceByOid(Oid oid)
764775
return (NamespaceInfo *) findObjectByOid(oid, nspinfoindex, numNamespaces);
765776
}
766777

778+
/*
779+
* findExtensionByOid
780+
* finds the entry (in extinfo) of the extension with the given oid
781+
* returns NULL if not found
782+
*/
783+
ExtensionInfo *
784+
findExtensionByOid(Oid oid)
785+
{
786+
return (ExtensionInfo *) findObjectByOid(oid, extinfoindex, numExtensions);
787+
}
788+
789+
790+
/*
791+
* setExtensionMembership
792+
* accept and save data about which objects belong to extensions
793+
*/
794+
void
795+
setExtensionMembership(ExtensionMemberId *extmems, int nextmems)
796+
{
797+
/* Sort array in preparation for binary searches */
798+
if (nextmems > 1)
799+
qsort((void *) extmems, nextmems, sizeof(ExtensionMemberId),
800+
ExtensionMemberIdCompare);
801+
/* And save */
802+
extmembers = extmems;
803+
numextmembers = nextmems;
804+
}
805+
806+
/*
807+
* findOwningExtension
808+
* return owning extension for specified catalog ID, or NULL if none
809+
*/
810+
ExtensionInfo *
811+
findOwningExtension(CatalogId catalogId)
812+
{
813+
ExtensionMemberId *low;
814+
ExtensionMemberId *high;
815+
816+
/*
817+
* We could use bsearch() here, but the notational cruft of calling
818+
* bsearch is nearly as bad as doing it ourselves; and the generalized
819+
* bsearch function is noticeably slower as well.
820+
*/
821+
if (numextmembers <= 0)
822+
return NULL;
823+
low = extmembers;
824+
high = extmembers + (numextmembers - 1);
825+
while (low <= high)
826+
{
827+
ExtensionMemberId *middle;
828+
int difference;
829+
830+
middle = low + (high - low) / 2;
831+
/* comparison must match ExtensionMemberIdCompare, below */
832+
difference = oidcmp(middle->catId.oid, catalogId.oid);
833+
if (difference == 0)
834+
difference = oidcmp(middle->catId.tableoid, catalogId.tableoid);
835+
if (difference == 0)
836+
return middle->ext;
837+
else if (difference < 0)
838+
low = middle + 1;
839+
else
840+
high = middle - 1;
841+
}
842+
return NULL;
843+
}
844+
845+
/*
846+
* qsort comparator for ExtensionMemberIds
847+
*/
848+
static int
849+
ExtensionMemberIdCompare(const void *p1, const void *p2)
850+
{
851+
const ExtensionMemberId *obj1 = (const ExtensionMemberId *) p1;
852+
const ExtensionMemberId *obj2 = (const ExtensionMemberId *) p2;
853+
int cmpval;
854+
855+
/*
856+
* Compare OID first since it's usually unique, whereas there will only be
857+
* a few distinct values of tableoid.
858+
*/
859+
cmpval = oidcmp(obj1->catId.oid, obj2->catId.oid);
860+
if (cmpval == 0)
861+
cmpval = oidcmp(obj1->catId.tableoid, obj2->catId.tableoid);
862+
return cmpval;
863+
}
864+
767865

768866
/*
769867
* findParentsByOid

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy