Skip to content

Commit 0c9d844

Browse files
committedDec 6, 2021
Rethink pg_dump's handling of object ACLs.
Throw away most of the existing logic for this, as it was very inefficient thanks to expensive sub-selects executed to collect ACL data that we very possibly would have no interest in dumping. Reduce the ACL handling in the initial per-object-type queries to be just collection of the catalog ACL fields, as it was originally. Fetch pg_init_privs data separately in a single scan of that catalog, and do the merging calculations on the client side. Remove the separate code path used for pre-9.6 source servers; there is no good reason to treat them differently from newer servers that happen to have empty pg_init_privs. Discussion: https://postgr.es/m/2273648.1634764485@sss.pgh.pa.us Discussion: https://postgr.es/m/7d7eb6128f40401d81b3b7a898b6b4de@W2012-02.nidsa.loc

File tree

7 files changed

+784
-1137
lines changed

7 files changed

+784
-1137
lines changed
 

‎src/bin/pg_dump/dumputils.c‎

Lines changed: 203 additions & 313 deletions
Large diffs are not rendered by default.

‎src/bin/pg_dump/dumputils.h‎

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,26 +37,22 @@
3737

3838

3939
extern bool buildACLCommands(const char *name, const char *subname, const char *nspname,
40-
const char *type, const char *acls, const char *racls,
40+
const char *type, const char *acls, const char *baseacls,
4141
const char *owner, const char *prefix, int remoteVersion,
4242
PQExpBuffer sql);
4343
extern bool buildDefaultACLCommands(const char *type, const char *nspname,
44-
const char *acls, const char *racls,
45-
const char *initacls, const char *initracls,
44+
const char *acls, const char *acldefault,
4645
const char *owner,
4746
int remoteVersion,
4847
PQExpBuffer sql);
48+
49+
extern void quoteAclUserName(PQExpBuffer output, const char *input);
50+
4951
extern void buildShSecLabelQuery(const char *catalog_name,
5052
Oid objectId, PQExpBuffer sql);
5153
extern void emitShSecLabels(PGconn *conn, PGresult *res,
5254
PQExpBuffer buffer, const char *objtype, const char *objname);
5355

54-
extern void buildACLQueries(PQExpBuffer acl_subquery, PQExpBuffer racl_subquery,
55-
PQExpBuffer init_acl_subquery, PQExpBuffer init_racl_subquery,
56-
const char *acl_column, const char *acl_owner,
57-
const char *initprivs_expr,
58-
const char *obj_kind, bool binary_upgrade);
59-
6056
extern bool variable_is_guc_list_quote(const char *name);
6157

6258
extern bool SplitGUCList(char *rawstring, char separator,

‎src/bin/pg_dump/pg_dump.c‎

Lines changed: 470 additions & 729 deletions
Large diffs are not rendered by default.

‎src/bin/pg_dump/pg_dump.h‎

Lines changed: 33 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -146,16 +146,36 @@ typedef struct _dumpableObject
146146
int allocDeps; /* allocated size of dependencies[] */
147147
} DumpableObject;
148148

149+
/*
150+
* Object types that have ACLs must store them in a DumpableAcl sub-struct,
151+
* which must immediately follow the DumpableObject base struct.
152+
*
153+
* Note: when dumping from a pre-9.2 server, which lacks the acldefault()
154+
* function, acldefault will be NULL or empty.
155+
*/
156+
typedef struct _dumpableAcl
157+
{
158+
char *acl; /* the object's actual ACL string */
159+
char *acldefault; /* default ACL for the object's type & owner */
160+
/* these fields come from the object's pg_init_privs entry, if any: */
161+
char privtype; /* entry type, 'i' or 'e'; 0 if no entry */
162+
char *initprivs; /* the object's initial ACL string, or NULL */
163+
} DumpableAcl;
164+
165+
/* Generic struct that can be used to access any object type having an ACL */
166+
typedef struct _dumpableObjectWithAcl
167+
{
168+
DumpableObject dobj;
169+
DumpableAcl dacl;
170+
} DumpableObjectWithAcl;
171+
149172
typedef struct _namespaceInfo
150173
{
151174
DumpableObject dobj;
175+
DumpableAcl dacl;
152176
bool create; /* CREATE SCHEMA, or just set owner? */
153177
Oid nspowner;
154178
char *rolname; /* name of owner, or empty string */
155-
char *nspacl;
156-
char *rnspacl;
157-
char *initnspacl;
158-
char *initrnspacl;
159179
} NamespaceInfo;
160180

161181
typedef struct _extensionInfo
@@ -171,6 +191,7 @@ typedef struct _extensionInfo
171191
typedef struct _typeInfo
172192
{
173193
DumpableObject dobj;
194+
DumpableAcl dacl;
174195

175196
/*
176197
* Note: dobj.name is the raw pg_type.typname entry. ftypname is the
@@ -179,10 +200,6 @@ typedef struct _typeInfo
179200
*/
180201
char *ftypname;
181202
char *rolname; /* name of owner, or empty string */
182-
char *typacl;
183-
char *rtypacl;
184-
char *inittypacl;
185-
char *initrtypacl;
186203
Oid typelem;
187204
Oid typrelid;
188205
char typrelkind; /* 'r', 'v', 'c', etc */
@@ -207,15 +224,12 @@ typedef struct _shellTypeInfo
207224
typedef struct _funcInfo
208225
{
209226
DumpableObject dobj;
227+
DumpableAcl dacl;
210228
char *rolname; /* name of owner, or empty string */
211229
Oid lang;
212230
int nargs;
213231
Oid *argtypes;
214232
Oid prorettype;
215-
char *proacl;
216-
char *rproacl;
217-
char *initproacl;
218-
char *initrproacl;
219233
} FuncInfo;
220234

221235
/* AggInfo is a superset of FuncInfo */
@@ -270,11 +284,8 @@ typedef struct _tableInfo
270284
* These fields are collected for every table in the database.
271285
*/
272286
DumpableObject dobj;
287+
DumpableAcl dacl;
273288
char *rolname; /* name of owner, or empty string */
274-
char *relacl;
275-
char *rrelacl;
276-
char *initrelacl;
277-
char *initrrelacl;
278289
char relkind;
279290
char relpersistence; /* relation persistence */
280291
bool relispopulated; /* relation is populated */
@@ -286,6 +297,7 @@ typedef struct _tableInfo
286297
bool hasindex; /* does it have any indexes? */
287298
bool hasrules; /* does it have any rules? */
288299
bool hastriggers; /* does it have any triggers? */
300+
bool hascolumnACLs; /* do any columns have non-default ACLs? */
289301
bool rowsec; /* is row security enabled? */
290302
bool forcerowsec; /* is row security forced? */
291303
bool hasoids; /* does it have OIDs? */
@@ -478,14 +490,11 @@ typedef struct _constraintInfo
478490
typedef struct _procLangInfo
479491
{
480492
DumpableObject dobj;
493+
DumpableAcl dacl;
481494
bool lanpltrusted;
482495
Oid lanplcallfoid;
483496
Oid laninline;
484497
Oid lanvalidator;
485-
char *lanacl;
486-
char *rlanacl;
487-
char *initlanacl;
488-
char *initrlanacl;
489498
char *lanowner; /* name of owner, or empty string */
490499
} ProcLangInfo;
491500

@@ -550,49 +559,37 @@ typedef struct _cfgInfo
550559
typedef struct _fdwInfo
551560
{
552561
DumpableObject dobj;
562+
DumpableAcl dacl;
553563
char *rolname;
554564
char *fdwhandler;
555565
char *fdwvalidator;
556566
char *fdwoptions;
557-
char *fdwacl;
558-
char *rfdwacl;
559-
char *initfdwacl;
560-
char *initrfdwacl;
561567
} FdwInfo;
562568

563569
typedef struct _foreignServerInfo
564570
{
565571
DumpableObject dobj;
572+
DumpableAcl dacl;
566573
char *rolname;
567574
Oid srvfdw;
568575
char *srvtype;
569576
char *srvversion;
570-
char *srvacl;
571-
char *rsrvacl;
572-
char *initsrvacl;
573-
char *initrsrvacl;
574577
char *srvoptions;
575578
} ForeignServerInfo;
576579

577580
typedef struct _defaultACLInfo
578581
{
579582
DumpableObject dobj;
583+
DumpableAcl dacl;
580584
char *defaclrole;
581585
char defaclobjtype;
582-
char *defaclacl;
583-
char *rdefaclacl;
584-
char *initdefaclacl;
585-
char *initrdefaclacl;
586586
} DefaultACLInfo;
587587

588588
typedef struct _blobInfo
589589
{
590590
DumpableObject dobj;
591+
DumpableAcl dacl;
591592
char *rolname;
592-
char *blobacl;
593-
char *rblobacl;
594-
char *initblobacl;
595-
char *initrblobacl;
596593
} BlobInfo;
597594

598595
/*

‎src/bin/pg_dump/pg_dumpall.c‎

Lines changed: 9 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1166,55 +1166,12 @@ dumpTablespaces(PGconn *conn)
11661166
/*
11671167
* Get all tablespaces except built-in ones (which we assume are named
11681168
* pg_xxx)
1169-
*
1170-
* For the tablespace ACLs, as of 9.6, we extract both the positive (as
1171-
* spcacl) and negative (as rspcacl) ACLs, relative to the default ACL for
1172-
* tablespaces, which are then passed to buildACLCommands() below.
1173-
*
1174-
* See buildACLQueries() and buildACLCommands().
1175-
*
1176-
* The order in which privileges are in the ACL string (the order they
1177-
* have been GRANT'd in, which the backend maintains) must be preserved to
1178-
* ensure that GRANTs WITH GRANT OPTION and subsequent GRANTs based on
1179-
* those are dumped in the correct order.
1180-
*
1181-
* Note that we do not support initial privileges (pg_init_privs) on
1182-
* tablespaces, so this logic cannot make use of buildACLQueries().
11831169
*/
1184-
if (server_version >= 90600)
1185-
res = executeQuery(conn, "SELECT oid, spcname, "
1186-
"pg_catalog.pg_get_userbyid(spcowner) AS spcowner, "
1187-
"pg_catalog.pg_tablespace_location(oid), "
1188-
"(SELECT array_agg(acl ORDER BY row_n) FROM "
1189-
" (SELECT acl, row_n FROM "
1190-
" unnest(coalesce(spcacl,acldefault('t',spcowner))) "
1191-
" WITH ORDINALITY AS perm(acl,row_n) "
1192-
" WHERE NOT EXISTS ( "
1193-
" SELECT 1 "
1194-
" FROM unnest(acldefault('t',spcowner)) "
1195-
" AS init(init_acl) "
1196-
" WHERE acl = init_acl)) AS spcacls) "
1197-
" AS spcacl, "
1198-
"(SELECT array_agg(acl ORDER BY row_n) FROM "
1199-
" (SELECT acl, row_n FROM "
1200-
" unnest(acldefault('t',spcowner)) "
1201-
" WITH ORDINALITY AS initp(acl,row_n) "
1202-
" WHERE NOT EXISTS ( "
1203-
" SELECT 1 "
1204-
" FROM unnest(coalesce(spcacl,acldefault('t',spcowner))) "
1205-
" AS permp(orig_acl) "
1206-
" WHERE acl = orig_acl)) AS rspcacls) "
1207-
" AS rspcacl, "
1208-
"array_to_string(spcoptions, ', '),"
1209-
"pg_catalog.shobj_description(oid, 'pg_tablespace') "
1210-
"FROM pg_catalog.pg_tablespace "
1211-
"WHERE spcname !~ '^pg_' "
1212-
"ORDER BY 1");
1213-
else if (server_version >= 90200)
1170+
if (server_version >= 90200)
12141171
res = executeQuery(conn, "SELECT oid, spcname, "
12151172
"pg_catalog.pg_get_userbyid(spcowner) AS spcowner, "
12161173
"pg_catalog.pg_tablespace_location(oid), "
1217-
"spcacl, '' as rspcacl, "
1174+
"spcacl, acldefault('t', spcowner) AS acldefault, "
12181175
"array_to_string(spcoptions, ', '),"
12191176
"pg_catalog.shobj_description(oid, 'pg_tablespace') "
12201177
"FROM pg_catalog.pg_tablespace "
@@ -1223,7 +1180,7 @@ dumpTablespaces(PGconn *conn)
12231180
else if (server_version >= 90000)
12241181
res = executeQuery(conn, "SELECT oid, spcname, "
12251182
"pg_catalog.pg_get_userbyid(spcowner) AS spcowner, "
1226-
"spclocation, spcacl, '' as rspcacl, "
1183+
"spclocation, spcacl, NULL AS acldefault, "
12271184
"array_to_string(spcoptions, ', '),"
12281185
"pg_catalog.shobj_description(oid, 'pg_tablespace') "
12291186
"FROM pg_catalog.pg_tablespace "
@@ -1232,15 +1189,15 @@ dumpTablespaces(PGconn *conn)
12321189
else if (server_version >= 80200)
12331190
res = executeQuery(conn, "SELECT oid, spcname, "
12341191
"pg_catalog.pg_get_userbyid(spcowner) AS spcowner, "
1235-
"spclocation, spcacl, '' as rspcacl, null, "
1192+
"spclocation, spcacl, NULL AS acldefault, null, "
12361193
"pg_catalog.shobj_description(oid, 'pg_tablespace') "
12371194
"FROM pg_catalog.pg_tablespace "
12381195
"WHERE spcname !~ '^pg_' "
12391196
"ORDER BY 1");
12401197
else
12411198
res = executeQuery(conn, "SELECT oid, spcname, "
12421199
"pg_catalog.pg_get_userbyid(spcowner) AS spcowner, "
1243-
"spclocation, spcacl, '' as rspcacl, "
1200+
"spclocation, spcacl, NULL AS acldefault, "
12441201
"null, null "
12451202
"FROM pg_catalog.pg_tablespace "
12461203
"WHERE spcname !~ '^pg_' "
@@ -1257,7 +1214,7 @@ dumpTablespaces(PGconn *conn)
12571214
char *spcowner = PQgetvalue(res, i, 2);
12581215
char *spclocation = PQgetvalue(res, i, 3);
12591216
char *spcacl = PQgetvalue(res, i, 4);
1260-
char *rspcacl = PQgetvalue(res, i, 5);
1217+
char *acldefault = PQgetvalue(res, i, 5);
12611218
char *spcoptions = PQgetvalue(res, i, 6);
12621219
char *spccomment = PQgetvalue(res, i, 7);
12631220
char *fspcname;
@@ -1276,9 +1233,11 @@ dumpTablespaces(PGconn *conn)
12761233
appendPQExpBuffer(buf, "ALTER TABLESPACE %s SET (%s);\n",
12771234
fspcname, spcoptions);
12781235

1236+
/* tablespaces can't have initprivs */
1237+
12791238
if (!skip_acls &&
12801239
!buildACLCommands(fspcname, NULL, NULL, "TABLESPACE",
1281-
spcacl, rspcacl,
1240+
spcacl, acldefault,
12821241
spcowner, "", server_version, buf))
12831242
{
12841243
pg_log_error("could not parse ACL list (%s) for tablespace \"%s\"",

‎src/fe_utils/string_utils.c‎

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -726,6 +726,69 @@ parsePGArray(const char *atext, char ***itemarray, int *nitems)
726726
}
727727

728728

729+
/*
730+
* Append one element to the text representation of a 1-dimensional Postgres
731+
* array.
732+
*
733+
* The caller must provide the initial '{' and closing '}' of the array.
734+
* This function handles all else, including insertion of commas and
735+
* quoting of values.
736+
*
737+
* We assume that typdelim is ','.
738+
*/
739+
void
740+
appendPGArray(PQExpBuffer buffer, const char *value)
741+
{
742+
bool needquote;
743+
const char *tmp;
744+
745+
if (buffer->data[buffer->len - 1] != '{')
746+
appendPQExpBufferChar(buffer, ',');
747+
748+
/* Decide if we need quotes; this should match array_out()'s choices. */
749+
if (value[0] == '\0')
750+
needquote = true; /* force quotes for empty string */
751+
else if (pg_strcasecmp(value, "NULL") == 0)
752+
needquote = true; /* force quotes for literal NULL */
753+
else
754+
needquote = false;
755+
756+
if (!needquote)
757+
{
758+
for (tmp = value; *tmp; tmp++)
759+
{
760+
char ch = *tmp;
761+
762+
if (ch == '"' || ch == '\\' ||
763+
ch == '{' || ch == '}' || ch == ',' ||
764+
/* these match array_isspace(): */
765+
ch == ' ' || ch == '\t' || ch == '\n' ||
766+
ch == '\r' || ch == '\v' || ch == '\f')
767+
{
768+
needquote = true;
769+
break;
770+
}
771+
}
772+
}
773+
774+
if (needquote)
775+
{
776+
appendPQExpBufferChar(buffer, '"');
777+
for (tmp = value; *tmp; tmp++)
778+
{
779+
char ch = *tmp;
780+
781+
if (ch == '"' || ch == '\\')
782+
appendPQExpBufferChar(buffer, '\\');
783+
appendPQExpBufferChar(buffer, ch);
784+
}
785+
appendPQExpBufferChar(buffer, '"');
786+
}
787+
else
788+
appendPQExpBufferStr(buffer, value);
789+
}
790+
791+
729792
/*
730793
* Format a reloptions array and append it to the given buffer.
731794
*

‎src/include/fe_utils/string_utils.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ extern void appendConnStrVal(PQExpBuffer buf, const char *str);
4646
extern void appendPsqlMetaConnect(PQExpBuffer buf, const char *dbname);
4747

4848
extern bool parsePGArray(const char *atext, char ***itemarray, int *nitems);
49+
extern void appendPGArray(PQExpBuffer buffer, const char *value);
4950

5051
extern bool appendReloptionsArray(PQExpBuffer buffer, const char *reloptions,
5152
const char *prefix, int encoding, bool std_strings);

0 commit comments

Comments
 (0)
Please sign in to comment.