Skip to content

Commit ad0be1c

Browse files
vyrusshackorum
authored andcommitted
pg_dump: Restore extension config table data before user objects during binary upgrade
pg_upgrade uses pg_dump --schema-only --binary-upgrade, which excludes all table data including extension configuration tables registered via pg_extension_config_dump(). Since binary_upgrade_create_empty_extension() does not populate these tables, any user table whose CREATE TABLE triggers validation against config data will fail. For example, PostGIS tables with SRID-constrained geometry/geography columns fail because spatial_ref_sys is empty during schema restore. Fix by introducing a new dump object type DO_EXTENSION_DATA that dumps extension config table data into SECTION_PRE_DATA during binary upgrade. This puts the data restore between extension creation and user object creation, allowing DDL-time validation to succeed. The data is scaffolding: it is overwritten when pg_upgrade transfers the old cluster's data files to the new cluster. This is not PostGIS-specific and applies to any extension that registers config tables via pg_extension_config_dump() where that data is needed for DDL-time validation.
1 parent 19733fa commit ad0be1c

6 files changed

Lines changed: 89 additions & 5 deletions

File tree

src/bin/pg_dump/pg_backup_archiver.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3300,6 +3300,7 @@ _tocEntryRequired(TocEntry *te, teSection curSection, ArchiveHandle *AH)
33003300
*/
33013301
if (strcmp(te->desc, "SEQUENCE SET") == 0 ||
33023302
strcmp(te->desc, "BLOB") == 0 ||
3303+
strcmp(te->desc, "EXTENSION DATA") == 0 ||
33033304
strcmp(te->desc, "BLOB METADATA") == 0 ||
33043305
(strcmp(te->desc, "ACL") == 0 &&
33053306
strncmp(te->tag, "LARGE OBJECT", 12) == 0) ||
@@ -3341,6 +3342,7 @@ _tocEntryRequired(TocEntry *te, teSection curSection, ArchiveHandle *AH)
33413342
if (!(ropt->sequence_data && strcmp(te->desc, "SEQUENCE SET") == 0) &&
33423343
!(ropt->binary_upgrade &&
33433344
(strcmp(te->desc, "BLOB") == 0 ||
3345+
strcmp(te->desc, "EXTENSION DATA") == 0 ||
33443346
strcmp(te->desc, "BLOB METADATA") == 0 ||
33453347
(strcmp(te->desc, "ACL") == 0 &&
33463348
strncmp(te->tag, "LARGE OBJECT", 12) == 0) ||

src/bin/pg_dump/pg_dump.c

Lines changed: 78 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,7 @@ static void addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx);
352352
static void getDomainConstraints(Archive *fout, TypeInfo *tyinfo);
353353
static void getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind);
354354
static void makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo);
355+
static void makeExtensionDataInfo(DumpOptions *dopt, TableInfo *tbinfo);
355356
static void buildMatViewRefreshDependencies(Archive *fout);
356357
static void getTableDataFKConstraints(void);
357358
static void determineNotNullFlags(Archive *fout, PGresult *res, int r,
@@ -2838,6 +2839,8 @@ dumpTableData(Archive *fout, const TableDataInfo *tdinfo)
28382839
char *tdDefn = NULL;
28392840
char *copyStmt;
28402841
const char *copyFrom;
2842+
const char *description = "TABLE DATA";
2843+
teSection section = SECTION_DATA;
28412844

28422845
/* We had better have loaded per-column details about this table */
28432846
Assert(tbinfo->interesting);
@@ -2884,6 +2887,16 @@ dumpTableData(Archive *fout, const TableDataInfo *tdinfo)
28842887
copyStmt = NULL;
28852888
}
28862889

2890+
/*
2891+
* Extension config table data goes into SECTION_PRE_DATA so it is
2892+
* available before user tables that may need it for validation.
2893+
*/
2894+
if (tdinfo->dobj.objType == DO_EXTENSION_DATA)
2895+
{
2896+
description = "EXTENSION DATA";
2897+
section = SECTION_PRE_DATA;
2898+
}
2899+
28872900
/*
28882901
* Note: although the TableDataInfo is a full DumpableObject, we treat its
28892902
* dependency on its table as "special" and pass it to ArchiveEntry now.
@@ -2897,8 +2910,8 @@ dumpTableData(Archive *fout, const TableDataInfo *tdinfo)
28972910
ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
28982911
.namespace = tbinfo->dobj.namespace->dobj.name,
28992912
.owner = tbinfo->rolname,
2900-
.description = "TABLE DATA",
2901-
.section = SECTION_DATA,
2913+
.description = description,
2914+
.section = section,
29022915
.createStmt = tdDefn,
29032916
.copyStmt = copyStmt,
29042917
.deps = &(tbinfo->dobj.dumpId),
@@ -3079,6 +3092,48 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo)
30793092
tbinfo->interesting = true;
30803093
}
30813094

3095+
/*
3096+
* makeExtensionDataInfo --- create TableDataInfo for extension config table
3097+
*
3098+
* This is used during binary upgrades to ensure extension configuration
3099+
* table data is dumped early (before user tables that may depend on it).
3100+
* For example, PostGIS's spatial_ref_sys must be populated before any
3101+
* table with geometry(Point, 27700) can be created due to SRID validation.
3102+
*/
3103+
static void
3104+
makeExtensionDataInfo(DumpOptions *dopt, TableInfo *tbinfo)
3105+
{
3106+
TableDataInfo *tdinfo;
3107+
3108+
/* Already have a data object? */
3109+
if (tbinfo->dataObj != NULL)
3110+
return;
3111+
3112+
/*
3113+
* Caller ensures that this is only called for RELKIND_RELATION.
3114+
*/
3115+
3116+
/* OK, create the data object */
3117+
tdinfo = (TableDataInfo *) pg_malloc(sizeof(TableDataInfo));
3118+
3119+
tdinfo->dobj.objType = DO_EXTENSION_DATA;
3120+
3121+
tdinfo->dobj.catId.tableoid = 0;
3122+
tdinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
3123+
AssignDumpId(&tdinfo->dobj);
3124+
tdinfo->dobj.name = tbinfo->dobj.name;
3125+
tdinfo->dobj.namespace = tbinfo->dobj.namespace;
3126+
tdinfo->tdtable = tbinfo;
3127+
tdinfo->filtercond = NULL;
3128+
addObjectDependency(&tdinfo->dobj, tbinfo->dobj.dumpId);
3129+
3130+
/* Mark that this object contains data */
3131+
tdinfo->dobj.components |= DUMP_COMPONENT_DATA;
3132+
3133+
tbinfo->dataObj = tdinfo;
3134+
tbinfo->interesting = true;
3135+
}
3136+
30823137
/*
30833138
* The refresh for a materialized view must be dependent on the refresh for
30843139
* any materialized view that this one is dependent on.
@@ -11664,6 +11719,9 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj)
1166411719
case DO_EXTENSION:
1166511720
dumpExtension(fout, (const ExtensionInfo *) dobj);
1166611721
break;
11722+
case DO_EXTENSION_DATA:
11723+
dumpTableData(fout, (const TableDataInfo *) dobj);
11724+
break;
1166711725
case DO_TYPE:
1166811726
dumpType(fout, (const TypeInfo *) dobj);
1166911727
break;
@@ -20125,10 +20183,25 @@ processExtensionTables(Archive *fout, ExtensionInfo extinfo[],
2012520183

2012620184
if (dumpobj)
2012720185
{
20128-
makeTableDataInfo(dopt, configtbl);
20186+
/*
20187+
* For binary upgrades, dump extension config table data
20188+
* before user tables are created so it's available for
20189+
* validation (e.g. PostGIS SRIDs).
20190+
*/
20191+
if (dopt->binary_upgrade &&
20192+
configtbl->relkind == RELKIND_RELATION)
20193+
makeExtensionDataInfo(dopt, configtbl);
20194+
else
20195+
makeTableDataInfo(dopt, configtbl);
2012920196
if (configtbl->dataObj != NULL)
2013020197
{
20131-
if (strlen(extconditionarray[j]) > 0)
20198+
/*
20199+
* For binary upgrade (DO_EXTENSION_DATA), don't apply
20200+
* the filter condition - we need ALL data since the
20201+
* extension won't populate built-in data in binary
20202+
* upgrade mode.
20203+
*/
20204+
if (strlen(extconditionarray[j]) > 0 && !dopt->binary_upgrade)
2013220205
configtbl->dataObj->filtercond = pg_strdup(extconditionarray[j]);
2013320206
}
2013420207
}
@@ -20406,6 +20479,7 @@ addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
2040620479
{
2040720480
case DO_NAMESPACE:
2040820481
case DO_EXTENSION:
20482+
case DO_EXTENSION_DATA:
2040920483
case DO_TYPE:
2041020484
case DO_SHELL_TYPE:
2041120485
case DO_FUNC:

src/bin/pg_dump/pg_dump.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ typedef enum
4040
/* When modifying this enum, update priority tables in pg_dump_sort.c! */
4141
DO_NAMESPACE,
4242
DO_EXTENSION,
43+
DO_EXTENSION_DATA, /* extension config table data for binary upgrade */
4344
DO_TYPE,
4445
DO_SHELL_TYPE,
4546
DO_FUNC,

src/bin/pg_dump/pg_dump_sort.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ enum dbObjectTypePriorities
5858
PRIO_COLLATION,
5959
PRIO_TRANSFORM,
6060
PRIO_EXTENSION,
61+
PRIO_EXTENSION_DATA, /* ext config data: used for binary upgrade */
6162
PRIO_TYPE, /* used for DO_TYPE and DO_SHELL_TYPE */
6263
PRIO_CAST,
6364
PRIO_FUNC,
@@ -106,6 +107,7 @@ static const int dbObjectTypePriority[] =
106107
{
107108
[DO_NAMESPACE] = PRIO_NAMESPACE,
108109
[DO_EXTENSION] = PRIO_EXTENSION,
110+
[DO_EXTENSION_DATA] = PRIO_EXTENSION_DATA,
109111
[DO_TYPE] = PRIO_TYPE,
110112
[DO_SHELL_TYPE] = PRIO_TYPE,
111113
[DO_FUNC] = PRIO_FUNC,
@@ -1525,6 +1527,11 @@ describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
15251527
"EXTENSION %s (ID %d OID %u)",
15261528
obj->name, obj->dumpId, obj->catId.oid);
15271529
return;
1530+
case DO_EXTENSION_DATA:
1531+
snprintf(buf, bufsize,
1532+
"EXTENSION DATA %s (ID %d OID %u)",
1533+
obj->name, obj->dumpId, obj->catId.oid);
1534+
return;
15281535
case DO_TYPE:
15291536
snprintf(buf, bufsize,
15301537
"TYPE %s (ID %d OID %u)",

src/test/modules/test_pg_dump/t/001_base.pl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -515,7 +515,6 @@
515515
extension_schema => 1,
516516
},
517517
unlike => {
518-
binary_upgrade => 1,
519518
exclude_table => 1,
520519
exclude_extension => 1,
521520
exclude_extension_filter => 1,

src/test/modules/test_pg_dump/test_pg_dump--1.0.sql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ CREATE TABLE regress_table_dumpable (
1818
col1 int check (col1 > 0)
1919
);
2020
SELECT pg_catalog.pg_extension_config_dump('regress_table_dumpable', '');
21+
INSERT INTO regress_table_dumpable VALUES (27700);
2122
GRANT SELECT ON regress_table_dumpable TO public;
2223

2324
CREATE SCHEMA regress_pg_dump_schema;

0 commit comments

Comments
 (0)