Skip to content

Commit 7ee7c1c

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 25388fb commit 7ee7c1c

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);
@@ -930,6 +934,17 @@ findOprByOid(Oid oid)
930934
return (OprInfo *) findObjectByOid(oid, oprinfoindex, numOperators);
931935
}
932936

937+
/*
938+
* findAccessMethodByOid
939+
* finds the DumpableObject for the access method with the given oid
940+
* returns NULL if not found
941+
*/
942+
AccessMethodInfo *
943+
findAccessMethodByOid(Oid oid)
944+
{
945+
return (AccessMethodInfo *) findObjectByOid(oid, aminfoindex, numAccessMethods);
946+
}
947+
933948
/*
934949
* findCollationByOid
935950
* 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
@@ -1899,6 +1899,13 @@ selectDumpableProcLang(ProcLangInfo *plang, Archive *fout)
18991899
static void
19001900
selectDumpableAccessMethod(AccessMethodInfo *method, Archive *fout)
19011901
{
1902+
/* see getAccessMethods() comment about v9.6. */
1903+
if (fout->remoteVersion < 90600)
1904+
{
1905+
method->dobj.dump = DUMP_COMPONENT_NONE;
1906+
return;
1907+
}
1908+
19021909
if (checkExtensionMembership(&method->dobj, fout))
19031910
return; /* extension membership overrides all else */
19041911

@@ -5525,6 +5532,8 @@ getOperators(Archive *fout, int *numOprs)
55255532
int i_oprnamespace;
55265533
int i_rolname;
55275534
int i_oprkind;
5535+
int i_oprleft;
5536+
int i_oprright;
55285537
int i_oprcode;
55295538

55305539
/*
@@ -5536,6 +5545,8 @@ getOperators(Archive *fout, int *numOprs)
55365545
"oprnamespace, "
55375546
"(%s oprowner) AS rolname, "
55385547
"oprkind, "
5548+
"oprleft, "
5549+
"oprright, "
55395550
"oprcode::oid AS oprcode "
55405551
"FROM pg_operator",
55415552
username_subquery);
@@ -5553,6 +5564,8 @@ getOperators(Archive *fout, int *numOprs)
55535564
i_oprnamespace = PQfnumber(res, "oprnamespace");
55545565
i_rolname = PQfnumber(res, "rolname");
55555566
i_oprkind = PQfnumber(res, "oprkind");
5567+
i_oprleft = PQfnumber(res, "oprleft");
5568+
i_oprright = PQfnumber(res, "oprright");
55565569
i_oprcode = PQfnumber(res, "oprcode");
55575570

55585571
for (i = 0; i < ntups; i++)
@@ -5566,6 +5579,8 @@ getOperators(Archive *fout, int *numOprs)
55665579
findNamespace(atooid(PQgetvalue(res, i, i_oprnamespace)));
55675580
oprinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
55685581
oprinfo[i].oprkind = (PQgetvalue(res, i, i_oprkind))[0];
5582+
oprinfo[i].oprleft = atooid(PQgetvalue(res, i, i_oprleft));
5583+
oprinfo[i].oprright = atooid(PQgetvalue(res, i, i_oprright));
55695584
oprinfo[i].oprcode = atooid(PQgetvalue(res, i, i_oprcode));
55705585

55715586
/* Decide whether we want to dump it */
@@ -5606,6 +5621,7 @@ getCollations(Archive *fout, int *numCollations)
56065621
int i_collname;
56075622
int i_collnamespace;
56085623
int i_rolname;
5624+
int i_collencoding;
56095625

56105626
/* Collations didn't exist pre-9.1 */
56115627
if (fout->remoteVersion < 90100)
@@ -5623,7 +5639,8 @@ getCollations(Archive *fout, int *numCollations)
56235639

56245640
appendPQExpBuffer(query, "SELECT tableoid, oid, collname, "
56255641
"collnamespace, "
5626-
"(%s collowner) AS rolname "
5642+
"(%s collowner) AS rolname, "
5643+
"collencoding "
56275644
"FROM pg_collation",
56285645
username_subquery);
56295646

@@ -5639,6 +5656,7 @@ getCollations(Archive *fout, int *numCollations)
56395656
i_collname = PQfnumber(res, "collname");
56405657
i_collnamespace = PQfnumber(res, "collnamespace");
56415658
i_rolname = PQfnumber(res, "rolname");
5659+
i_collencoding = PQfnumber(res, "collencoding");
56425660

56435661
for (i = 0; i < ntups; i++)
56445662
{
@@ -5650,6 +5668,7 @@ getCollations(Archive *fout, int *numCollations)
56505668
collinfo[i].dobj.namespace =
56515669
findNamespace(atooid(PQgetvalue(res, i, i_collnamespace)));
56525670
collinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
5671+
collinfo[i].collencoding = atoi(PQgetvalue(res, i, i_collencoding));
56535672

56545673
/* Decide whether we want to dump it */
56555674
selectDumpableObject(&(collinfo[i].dobj), fout);
@@ -5758,19 +5777,28 @@ getAccessMethods(Archive *fout, int *numAccessMethods)
57585777
int i_amhandler;
57595778
int i_amtype;
57605779

5761-
/* Before 9.6, there are no user-defined access methods */
5762-
if (fout->remoteVersion < 90600)
5763-
{
5764-
*numAccessMethods = 0;
5765-
return NULL;
5766-
}
5767-
57685780
query = createPQExpBuffer();
57695781

5770-
/* Select all access methods from pg_am table */
5771-
appendPQExpBufferStr(query, "SELECT tableoid, oid, amname, amtype, "
5772-
"amhandler::pg_catalog.regproc AS amhandler "
5773-
"FROM pg_am");
5782+
/*
5783+
* Select all access methods from pg_am table. v9.6 introduced CREATE
5784+
* ACCESS METHOD, so earlier versions usually have only built-in access
5785+
* methods. v9.6 also changed the access method API, replacing dozens of
5786+
* pg_am columns with amhandler. Even if a user created an access method
5787+
* by "INSERT INTO pg_am", we have no way to translate pre-v9.6 pg_am
5788+
* columns to a v9.6+ CREATE ACCESS METHOD. Hence, before v9.6, read
5789+
* pg_am just to facilitate findAccessMethodByOid() providing the
5790+
* OID-to-name mapping.
5791+
*/
5792+
appendPQExpBufferStr(query, "SELECT tableoid, oid, amname, ");
5793+
if (fout->remoteVersion >= 90600)
5794+
appendPQExpBufferStr(query,
5795+
"amtype, "
5796+
"amhandler::pg_catalog.regproc AS amhandler ");
5797+
else
5798+
appendPQExpBufferStr(query,
5799+
"'i'::pg_catalog.\"char\" AS amtype, "
5800+
"'-'::pg_catalog.regproc AS amhandler ");
5801+
appendPQExpBufferStr(query, "FROM pg_am");
57745802

57755803
res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
57765804

@@ -5828,6 +5856,7 @@ getOpclasses(Archive *fout, int *numOpclasses)
58285856
OpclassInfo *opcinfo;
58295857
int i_tableoid;
58305858
int i_oid;
5859+
int i_opcmethod;
58315860
int i_opcname;
58325861
int i_opcnamespace;
58335862
int i_rolname;
@@ -5837,11 +5866,20 @@ getOpclasses(Archive *fout, int *numOpclasses)
58375866
* system-defined opclasses at dump-out time.
58385867
*/
58395868

5840-
appendPQExpBuffer(query, "SELECT tableoid, oid, opcname, "
5841-
"opcnamespace, "
5842-
"(%s opcowner) AS rolname "
5843-
"FROM pg_opclass",
5844-
username_subquery);
5869+
if (fout->remoteVersion >= 80300)
5870+
appendPQExpBuffer(query, "SELECT tableoid, oid, "
5871+
"opcmethod, opcname, "
5872+
"opcnamespace, "
5873+
"(%s opcowner) AS rolname "
5874+
"FROM pg_opclass",
5875+
username_subquery);
5876+
else
5877+
appendPQExpBuffer(query, "SELECT tableoid, oid, "
5878+
"opcamid AS opcmethod, opcname, "
5879+
"opcnamespace, "
5880+
"(%s opcowner) AS rolname "
5881+
"FROM pg_opclass",
5882+
username_subquery);
58455883

58465884
res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
58475885

@@ -5852,6 +5890,7 @@ getOpclasses(Archive *fout, int *numOpclasses)
58525890

58535891
i_tableoid = PQfnumber(res, "tableoid");
58545892
i_oid = PQfnumber(res, "oid");
5893+
i_opcmethod = PQfnumber(res, "opcmethod");
58555894
i_opcname = PQfnumber(res, "opcname");
58565895
i_opcnamespace = PQfnumber(res, "opcnamespace");
58575896
i_rolname = PQfnumber(res, "rolname");
@@ -5865,6 +5904,7 @@ getOpclasses(Archive *fout, int *numOpclasses)
58655904
opcinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opcname));
58665905
opcinfo[i].dobj.namespace =
58675906
findNamespace(atooid(PQgetvalue(res, i, i_opcnamespace)));
5907+
opcinfo[i].opcmethod = atooid(PQgetvalue(res, i, i_opcmethod));
58685908
opcinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
58695909

58705910
/* Decide whether we want to dump it */
@@ -5902,6 +5942,7 @@ getOpfamilies(Archive *fout, int *numOpfamilies)
59025942
OpfamilyInfo *opfinfo;
59035943
int i_tableoid;
59045944
int i_oid;
5945+
int i_opfmethod;
59055946
int i_opfname;
59065947
int i_opfnamespace;
59075948
int i_rolname;
@@ -5920,7 +5961,7 @@ getOpfamilies(Archive *fout, int *numOpfamilies)
59205961
* system-defined opfamilies at dump-out time.
59215962
*/
59225963

5923-
appendPQExpBuffer(query, "SELECT tableoid, oid, opfname, "
5964+
appendPQExpBuffer(query, "SELECT tableoid, oid, opfmethod, opfname, "
59245965
"opfnamespace, "
59255966
"(%s opfowner) AS rolname "
59265967
"FROM pg_opfamily",
@@ -5936,6 +5977,7 @@ getOpfamilies(Archive *fout, int *numOpfamilies)
59365977
i_tableoid = PQfnumber(res, "tableoid");
59375978
i_oid = PQfnumber(res, "oid");
59385979
i_opfname = PQfnumber(res, "opfname");
5980+
i_opfmethod = PQfnumber(res, "opfmethod");
59395981
i_opfnamespace = PQfnumber(res, "opfnamespace");
59405982
i_rolname = PQfnumber(res, "rolname");
59415983

@@ -5948,6 +5990,7 @@ getOpfamilies(Archive *fout, int *numOpfamilies)
59485990
opfinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opfname));
59495991
opfinfo[i].dobj.namespace =
59505992
findNamespace(atooid(PQgetvalue(res, i, i_opfnamespace)));
5993+
opfinfo[i].opfmethod = atooid(PQgetvalue(res, i, i_opfmethod));
59515994
opfinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
59525995

59535996
/* 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
@@ -222,6 +222,8 @@ typedef struct _oprInfo
222222
DumpableObject dobj;
223223
char *rolname;
224224
char oprkind;
225+
Oid oprleft;
226+
Oid oprright;
225227
Oid oprcode;
226228
} OprInfo;
227229

@@ -235,19 +237,22 @@ typedef struct _accessMethodInfo
235237
typedef struct _opclassInfo
236238
{
237239
DumpableObject dobj;
240+
Oid opcmethod;
238241
char *rolname;
239242
} OpclassInfo;
240243

241244
typedef struct _opfamilyInfo
242245
{
243246
DumpableObject dobj;
247+
Oid opfmethod;
244248
char *rolname;
245249
} OpfamilyInfo;
246250

247251
typedef struct _collInfo
248252
{
249253
DumpableObject dobj;
250254
char *rolname;
255+
int collencoding;
251256
} CollInfo;
252257

253258
typedef struct _convInfo
@@ -675,6 +680,7 @@ extern TableInfo *findTableByOid(Oid oid);
675680
extern TypeInfo *findTypeByOid(Oid oid);
676681
extern FuncInfo *findFuncByOid(Oid oid);
677682
extern OprInfo *findOprByOid(Oid oid);
683+
extern AccessMethodInfo *findAccessMethodByOid(Oid oid);
678684
extern CollInfo *findCollationByOid(Oid oid);
679685
extern NamespaceInfo *findNamespaceByOid(Oid oid);
680686
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