Skip to content

Commit 7393208

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 2281575 commit 7393208

File tree

3 files changed

+312
-163
lines changed

3 files changed

+312
-163
lines changed

src/bin/pg_dump/common.c

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

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

61+
/* This is an array of object identities, not actual DumpableObjects */
62+
static ExtensionMemberId *extmembers;
63+
static int numextmembers;
6464

6565
static void flagInhTables(TableInfo *tbinfo, int numTables,
6666
InhInfo *inhinfo, int numInherits);
6767
static void flagInhAttrs(TableInfo *tblinfo, int numTables);
6868
static DumpableObject **buildIndexArray(void *objArray, int numObjs,
6969
Size objSize);
7070
static int DOCatalogIdCompare(const void *p1, const void *p2);
71+
static int ExtensionMemberIdCompare(const void *p1, const void *p2);
7172
static void findParentsByOid(TableInfo *self,
7273
InhInfo *inhinfo, int numInherits);
7374
static int strInArray(const char *pattern, char **arr, int arr_size);
@@ -80,10 +81,14 @@ static int strInArray(const char *pattern, char **arr, int arr_size);
8081
TableInfo *
8182
getSchemaData(Archive *fout, int *numTablesPtr)
8283
{
84+
TableInfo *tblinfo;
85+
TypeInfo *typinfo;
86+
FuncInfo *funinfo;
87+
OprInfo *oprinfo;
88+
CollInfo *collinfo;
89+
NamespaceInfo *nspinfo;
8390
ExtensionInfo *extinfo;
8491
InhInfo *inhinfo;
85-
CollInfo *collinfo;
86-
int numExtensions;
8792
int numAggregates;
8893
int numInherits;
8994
int numRules;
@@ -101,6 +106,20 @@ getSchemaData(Archive *fout, int *numTablesPtr)
101106
int numDefaultACLs;
102107
int numEventTriggers;
103108

109+
/*
110+
* We must read extensions and extension membership info first, because
111+
* extension membership needs to be consultable during decisions about
112+
* whether other objects are to be dumped.
113+
*/
114+
if (g_verbose)
115+
write_msg(NULL, "reading extensions\n");
116+
extinfo = getExtensions(fout, &numExtensions);
117+
extinfoindex = buildIndexArray(extinfo, numExtensions, sizeof(ExtensionInfo));
118+
119+
if (g_verbose)
120+
write_msg(NULL, "identifying extension members\n");
121+
getExtensionMembership(fout, extinfo, numExtensions);
122+
104123
if (g_verbose)
105124
write_msg(NULL, "reading schemas\n");
106125
nspinfo = getNamespaces(fout, &numNamespaces);
@@ -120,10 +139,6 @@ getSchemaData(Archive *fout, int *numTablesPtr)
120139
/* Do this after we've built tblinfoindex */
121140
getOwnedSeqs(fout, tblinfo, numTables);
122141

123-
if (g_verbose)
124-
write_msg(NULL, "reading extensions\n");
125-
extinfo = getExtensions(fout, &numExtensions);
126-
127142
if (g_verbose)
128143
write_msg(NULL, "reading user-defined functions\n");
129144
funinfo = getFuncs(fout, &numFuncs);
@@ -206,14 +221,10 @@ getSchemaData(Archive *fout, int *numTablesPtr)
206221
write_msg(NULL, "reading event triggers\n");
207222
getEventTriggers(fout, &numEventTriggers);
208223

209-
/*
210-
* Identify extension member objects and mark them as not to be dumped.
211-
* This must happen after reading all objects that can be direct members
212-
* of extensions, but before we begin to process table subsidiary objects.
213-
*/
224+
/* Identify extension configuration tables that should be dumped */
214225
if (g_verbose)
215-
write_msg(NULL, "finding extension members\n");
216-
getExtensionMembership(fout, extinfo, numExtensions);
226+
write_msg(NULL, "finding extension tables\n");
227+
processExtensionTables(fout, extinfo, numExtensions);
217228

218229
/* Link tables to parents, mark parents of target tables interesting */
219230
if (g_verbose)
@@ -752,6 +763,93 @@ findNamespaceByOid(Oid oid)
752763
return (NamespaceInfo *) findObjectByOid(oid, nspinfoindex, numNamespaces);
753764
}
754765

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

756854
/*
757855
* findParentsByOid

0 commit comments

Comments
 (0)