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)
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