Skip to content

Commit 04bc2c4

Browse files
committed
Sort dump objects independent of OIDs, for the 7 holdout object types.
pg_dump sorts objects by their logical names, e.g. (nspname, relname, tgname), before dependency-driven reordering. That removes one source of logically-identical databases differing in their schema-only dumps. In other words, it helps with schema diffing. The logical name sort ignored essential sort keys for constraints, operators, PUBLICATION ... FOR TABLE, PUBLICATION ... FOR TABLES IN SCHEMA, operator classes, and operator families. pg_dump's sort then depended on object OID, yielding spurious schema diffs. After this change, OIDs affect dump order only in the event of catalog corruption. While pg_dump also wrongly ignored pg_collation.collencoding, CREATE COLLATION restrictions have been keeping that imperceptible in practical use. Use techniques like we use for object types already having full sort key coverage. Where the pertinent queries weren't fetching the ignored sort keys, this adds columns to those queries and stores those keys in memory for the long term. The ignorance of sort keys became more problematic when commit 172259a added a schema diff test sensitive to it. Buildfarm member hippopotamus witnessed that. However, dump order stability isn't a new goal, and this might avoid other dump comparison failures. Hence, back-patch to v13 (all supported versions). Reviewed-by: Robert Haas <robertmhaas@gmail.com> Discussion: https://postgr.es/m/20250707192654.9e.nmisch@google.com Backpatch-through: 13
1 parent cc9a62c commit 04bc2c4

File tree

6 files changed

+309
-39
lines changed

6 files changed

+309
-39
lines changed

src/bin/pg_dump/common.c

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#include <ctype.h>
1919

20+
#include "catalog/pg_am_d.h"
2021
#include "catalog/pg_class_d.h"
2122
#include "fe_utils/string_utils.h"
2223
#include "pg_backup_archiver.h"
@@ -49,6 +50,7 @@ static DumpableObject **tblinfoindex;
4950
static DumpableObject **typinfoindex;
5051
static DumpableObject **funinfoindex;
5152
static DumpableObject **oprinfoindex;
53+
static DumpableObject **aminfoindex;
5254
static DumpableObject **collinfoindex;
5355
static DumpableObject **nspinfoindex;
5456
static DumpableObject **extinfoindex;
@@ -57,6 +59,7 @@ static int numTables;
5759
static int numTypes;
5860
static int numFuncs;
5961
static int numOperators;
62+
static int numAccessMethods;
6063
static int numCollations;
6164
static int numNamespaces;
6265
static int numExtensions;
@@ -92,6 +95,7 @@ getSchemaData(Archive *fout, int *numTablesPtr)
9295
TypeInfo *typinfo;
9396
FuncInfo *funinfo;
9497
OprInfo *oprinfo;
98+
AccessMethodInfo *aminfo;
9599
CollInfo *collinfo;
96100
NamespaceInfo *nspinfo;
97101
ExtensionInfo *extinfo;
@@ -103,7 +107,6 @@ getSchemaData(Archive *fout, int *numTablesPtr)
103107
int numProcLangs;
104108
int numCasts;
105109
int numTransforms;
106-
int numAccessMethods;
107110
int numOpclasses;
108111
int numOpfamilies;
109112
int numConversions;
@@ -166,7 +169,8 @@ getSchemaData(Archive *fout, int *numTablesPtr)
166169
oprinfoindex = buildIndexArray(oprinfo, numOperators, sizeof(OprInfo));
167170

168171
pg_log_info("reading user-defined access methods");
169-
getAccessMethods(fout, &numAccessMethods);
172+
aminfo = getAccessMethods(fout, &numAccessMethods);
173+
aminfoindex = buildIndexArray(aminfo, numAccessMethods, sizeof(AccessMethodInfo));
170174

171175
pg_log_info("reading user-defined operator classes");
172176
getOpclasses(fout, &numOpclasses);
@@ -890,6 +894,17 @@ findOprByOid(Oid oid)
890894
return (OprInfo *) findObjectByOid(oid, oprinfoindex, numOperators);
891895
}
892896

897+
/*
898+
* findAccessMethodByOid
899+
* finds the DumpableObject for the access method with the given oid
900+
* returns NULL if not found
901+
*/
902+
AccessMethodInfo *
903+
findAccessMethodByOid(Oid oid)
904+
{
905+
return (AccessMethodInfo *) findObjectByOid(oid, aminfoindex, numAccessMethods);
906+
}
907+
893908
/*
894909
* findCollationByOid
895910
* finds the entry (in collinfo) of the collation with the given oid

src/bin/pg_dump/pg_dump.c

Lines changed: 61 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1767,6 +1767,13 @@ selectDumpableProcLang(ProcLangInfo *plang, Archive *fout)
17671767
static void
17681768
selectDumpableAccessMethod(AccessMethodInfo *method, Archive *fout)
17691769
{
1770+
/* see getAccessMethods() comment about v9.6. */
1771+
if (fout->remoteVersion < 90600)
1772+
{
1773+
method->dobj.dump = DUMP_COMPONENT_NONE;
1774+
return;
1775+
}
1776+
17701777
if (checkExtensionMembership(&method->dobj, fout))
17711778
return; /* extension membership overrides all else */
17721779

@@ -5325,6 +5332,8 @@ getOperators(Archive *fout, int *numOprs)
53255332
int i_oprnamespace;
53265333
int i_rolname;
53275334
int i_oprkind;
5335+
int i_oprleft;
5336+
int i_oprright;
53285337
int i_oprcode;
53295338

53305339
/*
@@ -5336,6 +5345,8 @@ getOperators(Archive *fout, int *numOprs)
53365345
"oprnamespace, "
53375346
"(%s oprowner) AS rolname, "
53385347
"oprkind, "
5348+
"oprleft, "
5349+
"oprright, "
53395350
"oprcode::oid AS oprcode "
53405351
"FROM pg_operator",
53415352
username_subquery);
@@ -5353,6 +5364,8 @@ getOperators(Archive *fout, int *numOprs)
53535364
i_oprnamespace = PQfnumber(res, "oprnamespace");
53545365
i_rolname = PQfnumber(res, "rolname");
53555366
i_oprkind = PQfnumber(res, "oprkind");
5367+
i_oprleft = PQfnumber(res, "oprleft");
5368+
i_oprright = PQfnumber(res, "oprright");
53565369
i_oprcode = PQfnumber(res, "oprcode");
53575370

53585371
for (i = 0; i < ntups; i++)
@@ -5367,6 +5380,8 @@ getOperators(Archive *fout, int *numOprs)
53675380
atooid(PQgetvalue(res, i, i_oprnamespace)));
53685381
oprinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
53695382
oprinfo[i].oprkind = (PQgetvalue(res, i, i_oprkind))[0];
5383+
oprinfo[i].oprleft = atooid(PQgetvalue(res, i, i_oprleft));
5384+
oprinfo[i].oprright = atooid(PQgetvalue(res, i, i_oprright));
53705385
oprinfo[i].oprcode = atooid(PQgetvalue(res, i, i_oprcode));
53715386

53725387
/* Decide whether we want to dump it */
@@ -5407,6 +5422,7 @@ getCollations(Archive *fout, int *numCollations)
54075422
int i_collname;
54085423
int i_collnamespace;
54095424
int i_rolname;
5425+
int i_collencoding;
54105426

54115427
/* Collations didn't exist pre-9.1 */
54125428
if (fout->remoteVersion < 90100)
@@ -5424,7 +5440,8 @@ getCollations(Archive *fout, int *numCollations)
54245440

54255441
appendPQExpBuffer(query, "SELECT tableoid, oid, collname, "
54265442
"collnamespace, "
5427-
"(%s collowner) AS rolname "
5443+
"(%s collowner) AS rolname, "
5444+
"collencoding "
54285445
"FROM pg_collation",
54295446
username_subquery);
54305447

@@ -5440,6 +5457,7 @@ getCollations(Archive *fout, int *numCollations)
54405457
i_collname = PQfnumber(res, "collname");
54415458
i_collnamespace = PQfnumber(res, "collnamespace");
54425459
i_rolname = PQfnumber(res, "rolname");
5460+
i_collencoding = PQfnumber(res, "collencoding");
54435461

54445462
for (i = 0; i < ntups; i++)
54455463
{
@@ -5452,6 +5470,7 @@ getCollations(Archive *fout, int *numCollations)
54525470
findNamespace(fout,
54535471
atooid(PQgetvalue(res, i, i_collnamespace)));
54545472
collinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
5473+
collinfo[i].collencoding = atoi(PQgetvalue(res, i, i_collencoding));
54555474

54565475
/* Decide whether we want to dump it */
54575476
selectDumpableObject(&(collinfo[i].dobj), fout);
@@ -5561,19 +5580,28 @@ getAccessMethods(Archive *fout, int *numAccessMethods)
55615580
int i_amhandler;
55625581
int i_amtype;
55635582

5564-
/* Before 9.6, there are no user-defined access methods */
5565-
if (fout->remoteVersion < 90600)
5566-
{
5567-
*numAccessMethods = 0;
5568-
return NULL;
5569-
}
5570-
55715583
query = createPQExpBuffer();
55725584

5573-
/* Select all access methods from pg_am table */
5574-
appendPQExpBufferStr(query, "SELECT tableoid, oid, amname, amtype, "
5575-
"amhandler::pg_catalog.regproc AS amhandler "
5576-
"FROM pg_am");
5585+
/*
5586+
* Select all access methods from pg_am table. v9.6 introduced CREATE
5587+
* ACCESS METHOD, so earlier versions usually have only built-in access
5588+
* methods. v9.6 also changed the access method API, replacing dozens of
5589+
* pg_am columns with amhandler. Even if a user created an access method
5590+
* by "INSERT INTO pg_am", we have no way to translate pre-v9.6 pg_am
5591+
* columns to a v9.6+ CREATE ACCESS METHOD. Hence, before v9.6, read
5592+
* pg_am just to facilitate findAccessMethodByOid() providing the
5593+
* OID-to-name mapping.
5594+
*/
5595+
appendPQExpBufferStr(query, "SELECT tableoid, oid, amname, ");
5596+
if (fout->remoteVersion >= 90600)
5597+
appendPQExpBufferStr(query,
5598+
"amtype, "
5599+
"amhandler::pg_catalog.regproc AS amhandler ");
5600+
else
5601+
appendPQExpBufferStr(query,
5602+
"'i'::pg_catalog.\"char\" AS amtype, "
5603+
"'-'::pg_catalog.regproc AS amhandler ");
5604+
appendPQExpBufferStr(query, "FROM pg_am");
55775605

55785606
res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
55795607

@@ -5631,6 +5659,7 @@ getOpclasses(Archive *fout, int *numOpclasses)
56315659
OpclassInfo *opcinfo;
56325660
int i_tableoid;
56335661
int i_oid;
5662+
int i_opcmethod;
56345663
int i_opcname;
56355664
int i_opcnamespace;
56365665
int i_rolname;
@@ -5640,11 +5669,20 @@ getOpclasses(Archive *fout, int *numOpclasses)
56405669
* system-defined opclasses at dump-out time.
56415670
*/
56425671

5643-
appendPQExpBuffer(query, "SELECT tableoid, oid, opcname, "
5644-
"opcnamespace, "
5645-
"(%s opcowner) AS rolname "
5646-
"FROM pg_opclass",
5647-
username_subquery);
5672+
if (fout->remoteVersion >= 80300)
5673+
appendPQExpBuffer(query, "SELECT tableoid, oid, "
5674+
"opcmethod, opcname, "
5675+
"opcnamespace, "
5676+
"(%s opcowner) AS rolname "
5677+
"FROM pg_opclass",
5678+
username_subquery);
5679+
else
5680+
appendPQExpBuffer(query, "SELECT tableoid, oid, "
5681+
"opcamid AS opcmethod, opcname, "
5682+
"opcnamespace, "
5683+
"(%s opcowner) AS rolname "
5684+
"FROM pg_opclass",
5685+
username_subquery);
56485686

56495687
res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
56505688

@@ -5655,6 +5693,7 @@ getOpclasses(Archive *fout, int *numOpclasses)
56555693

56565694
i_tableoid = PQfnumber(res, "tableoid");
56575695
i_oid = PQfnumber(res, "oid");
5696+
i_opcmethod = PQfnumber(res, "opcmethod");
56585697
i_opcname = PQfnumber(res, "opcname");
56595698
i_opcnamespace = PQfnumber(res, "opcnamespace");
56605699
i_rolname = PQfnumber(res, "rolname");
@@ -5669,6 +5708,7 @@ getOpclasses(Archive *fout, int *numOpclasses)
56695708
opcinfo[i].dobj.namespace =
56705709
findNamespace(fout,
56715710
atooid(PQgetvalue(res, i, i_opcnamespace)));
5711+
opcinfo[i].opcmethod = atooid(PQgetvalue(res, i, i_opcmethod));
56725712
opcinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
56735713

56745714
/* Decide whether we want to dump it */
@@ -5706,6 +5746,7 @@ getOpfamilies(Archive *fout, int *numOpfamilies)
57065746
OpfamilyInfo *opfinfo;
57075747
int i_tableoid;
57085748
int i_oid;
5749+
int i_opfmethod;
57095750
int i_opfname;
57105751
int i_opfnamespace;
57115752
int i_rolname;
@@ -5724,7 +5765,7 @@ getOpfamilies(Archive *fout, int *numOpfamilies)
57245765
* system-defined opfamilies at dump-out time.
57255766
*/
57265767

5727-
appendPQExpBuffer(query, "SELECT tableoid, oid, opfname, "
5768+
appendPQExpBuffer(query, "SELECT tableoid, oid, opfmethod, opfname, "
57285769
"opfnamespace, "
57295770
"(%s opfowner) AS rolname "
57305771
"FROM pg_opfamily",
@@ -5740,6 +5781,7 @@ getOpfamilies(Archive *fout, int *numOpfamilies)
57405781
i_tableoid = PQfnumber(res, "tableoid");
57415782
i_oid = PQfnumber(res, "oid");
57425783
i_opfname = PQfnumber(res, "opfname");
5784+
i_opfmethod = PQfnumber(res, "opfmethod");
57435785
i_opfnamespace = PQfnumber(res, "opfnamespace");
57445786
i_rolname = PQfnumber(res, "rolname");
57455787

@@ -5753,6 +5795,7 @@ getOpfamilies(Archive *fout, int *numOpfamilies)
57535795
opfinfo[i].dobj.namespace =
57545796
findNamespace(fout,
57555797
atooid(PQgetvalue(res, i, i_opfnamespace)));
5798+
opfinfo[i].opfmethod = atooid(PQgetvalue(res, i, i_opfmethod));
57565799
opfinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
57575800

57585801
/* Decide whether we want to dump it */

src/bin/pg_dump/pg_dump.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,8 @@ typedef struct _oprInfo
219219
DumpableObject dobj;
220220
char *rolname;
221221
char oprkind;
222+
Oid oprleft;
223+
Oid oprright;
222224
Oid oprcode;
223225
} OprInfo;
224226

@@ -232,19 +234,22 @@ typedef struct _accessMethodInfo
232234
typedef struct _opclassInfo
233235
{
234236
DumpableObject dobj;
237+
Oid opcmethod;
235238
char *rolname;
236239
} OpclassInfo;
237240

238241
typedef struct _opfamilyInfo
239242
{
240243
DumpableObject dobj;
244+
Oid opfmethod;
241245
char *rolname;
242246
} OpfamilyInfo;
243247

244248
typedef struct _collInfo
245249
{
246250
DumpableObject dobj;
247251
char *rolname;
252+
int collencoding;
248253
} CollInfo;
249254

250255
typedef struct _convInfo
@@ -662,6 +667,7 @@ extern TableInfo *findTableByOid(Oid oid);
662667
extern TypeInfo *findTypeByOid(Oid oid);
663668
extern FuncInfo *findFuncByOid(Oid oid);
664669
extern OprInfo *findOprByOid(Oid oid);
670+
extern AccessMethodInfo *findAccessMethodByOid(Oid oid);
665671
extern CollInfo *findCollationByOid(Oid oid);
666672
extern NamespaceInfo *findNamespaceByOid(Oid oid);
667673
extern ExtensionInfo *findExtensionByOid(Oid oid);

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