PostgreSQL Source Code  git master
dumputils.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * Utility routines for SQL dumping
4  *
5  * Basically this is stuff that is useful in both pg_dump and pg_dumpall.
6  *
7  *
8  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
9  * Portions Copyright (c) 1994, Regents of the University of California
10  *
11  * src/bin/pg_dump/dumputils.c
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres_fe.h"
16 
17 #include "dumputils.h"
18 #include "fe_utils/string_utils.h"
19 
20 
21 static bool parseAclItem(const char *item, const char *type,
22  const char *name, const char *subname, int remoteVersion,
23  PQExpBuffer grantee, PQExpBuffer grantor,
24  PQExpBuffer privs, PQExpBuffer privswgo);
25 static char *copyAclUserName(PQExpBuffer output, char *input);
26 static void AddAcl(PQExpBuffer aclbuf, const char *keyword,
27  const char *subname);
28 
29 
30 /*
31  * Build GRANT/REVOKE command(s) for an object.
32  *
33  * name: the object name, in the form to use in the commands (already quoted)
34  * subname: the sub-object name, if any (already quoted); NULL if none
35  * type: the object type (as seen in GRANT command: must be one of
36  * TABLE, SEQUENCE, FUNCTION, PROCEDURE, LANGUAGE, SCHEMA, DATABASE, TABLESPACE,
37  * FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT)
38  * acls: the ACL string fetched from the database
39  * racls: the ACL string of any initial-but-now-revoked privileges
40  * owner: username of object owner (will be passed through fmtId); can be
41  * NULL or empty string to indicate "no owner known"
42  * prefix: string to prefix to each generated command; typically empty
43  * remoteVersion: version of database
44  *
45  * Returns true if okay, false if could not parse the acl string.
46  * The resulting commands (if any) are appended to the contents of 'sql'.
47  *
48  * Note: when processing a default ACL, prefix is "ALTER DEFAULT PRIVILEGES "
49  * or something similar, and name is an empty string.
50  *
51  * Note: beware of passing a fmtId() result directly as 'name' or 'subname',
52  * since this routine uses fmtId() internally.
53  */
54 bool
55 buildACLCommands(const char *name, const char *subname,
56  const char *type, const char *acls, const char *racls,
57  const char *owner, const char *prefix, int remoteVersion,
58  PQExpBuffer sql)
59 {
60  bool ok = true;
61  char **aclitems = NULL;
62  char **raclitems = NULL;
63  int naclitems = 0;
64  int nraclitems = 0;
65  int i;
66  PQExpBuffer grantee,
67  grantor,
68  privs,
69  privswgo;
70  PQExpBuffer firstsql,
71  secondsql;
72  bool found_owner_privs = false;
73 
74  if (strlen(acls) == 0 && strlen(racls) == 0)
75  return true; /* object has default permissions */
76 
77  /* treat empty-string owner same as NULL */
78  if (owner && *owner == '\0')
79  owner = NULL;
80 
81  if (strlen(acls) != 0)
82  {
83  if (!parsePGArray(acls, &aclitems, &naclitems))
84  {
85  if (aclitems)
86  free(aclitems);
87  return false;
88  }
89  }
90 
91  if (strlen(racls) != 0)
92  {
93  if (!parsePGArray(racls, &raclitems, &nraclitems))
94  {
95  if (raclitems)
96  free(raclitems);
97  return false;
98  }
99  }
100 
101  grantee = createPQExpBuffer();
102  grantor = createPQExpBuffer();
103  privs = createPQExpBuffer();
104  privswgo = createPQExpBuffer();
105 
106  /*
107  * At the end, these two will be pasted together to form the result.
108  *
109  * For older systems we use these to ensure that the owner privileges go
110  * before the other ones, as a GRANT could create the default entry for
111  * the object, which generally includes all rights for the owner. In more
112  * recent versions we normally handle this because the owner rights come
113  * first in the ACLs, but older versions might have them after the PUBLIC
114  * privileges.
115  *
116  * For 9.6 and later systems, much of this changes. With 9.6, we check
117  * the default privileges for the objects at dump time and create two sets
118  * of ACLs- "racls" which are the ACLs to REVOKE from the object (as the
119  * object may have initial privileges on it, along with any default ACLs
120  * which are not part of the current set of privileges), and regular
121  * "acls", which are the ACLs to GRANT to the object. We handle the
122  * REVOKEs first, followed by the GRANTs.
123  */
124  firstsql = createPQExpBuffer();
125  secondsql = createPQExpBuffer();
126 
127  /*
128  * For pre-9.6 systems, we always start with REVOKE ALL FROM PUBLIC, as we
129  * don't wish to make any assumptions about what the default ACLs are, and
130  * we do not collect them during the dump phase (and racls will always be
131  * the empty set, see above).
132  *
133  * For 9.6 and later, if any revoke ACLs have been provided, then include
134  * them in 'firstsql'.
135  *
136  * Revoke ACLs happen when an object starts out life with a set of
137  * privileges (eg: GRANT SELECT ON pg_class TO PUBLIC;) and the user has
138  * decided to revoke those rights. Since those objects come into being
139  * with those default privileges, we have to revoke them to match what the
140  * current state of affairs is. Note that we only started explicitly
141  * tracking such initial rights in 9.6, and prior to that all initial
142  * rights are actually handled by the simple 'REVOKE ALL .. FROM PUBLIC'
143  * case, for initdb-created objects. Prior to 9.6, we didn't handle
144  * extensions correctly, but we do now by tracking their initial
145  * privileges, in the same way we track initdb initial privileges, see
146  * pg_init_privs.
147  */
148  if (remoteVersion < 90600)
149  {
150  Assert(nraclitems == 0);
151 
152  appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
153  if (subname)
154  appendPQExpBuffer(firstsql, "(%s)", subname);
155  appendPQExpBuffer(firstsql, " ON %s %s FROM PUBLIC;\n", type, name);
156  }
157  else
158  {
159  /* Scan individual REVOKE ACL items */
160  for (i = 0; i < nraclitems; i++)
161  {
162  if (!parseAclItem(raclitems[i], type, name, subname, remoteVersion,
163  grantee, grantor, privs, privswgo))
164  {
165  ok = false;
166  break;
167  }
168 
169  if (privs->len > 0 || privswgo->len > 0)
170  {
171  if (privs->len > 0)
172  {
173  appendPQExpBuffer(firstsql, "%sREVOKE %s ON %s %s FROM ",
174  prefix, privs->data, type, name);
175  if (grantee->len == 0)
176  appendPQExpBufferStr(firstsql, "PUBLIC;\n");
177  else if (strncmp(grantee->data, "group ",
178  strlen("group ")) == 0)
179  appendPQExpBuffer(firstsql, "GROUP %s;\n",
180  fmtId(grantee->data + strlen("group ")));
181  else
182  appendPQExpBuffer(firstsql, "%s;\n",
183  fmtId(grantee->data));
184  }
185  if (privswgo->len > 0)
186  {
187  appendPQExpBuffer(firstsql,
188  "%sREVOKE GRANT OPTION FOR %s ON %s %s FROM ",
189  prefix, privswgo->data, type, name);
190  if (grantee->len == 0)
191  appendPQExpBufferStr(firstsql, "PUBLIC");
192  else if (strncmp(grantee->data, "group ",
193  strlen("group ")) == 0)
194  appendPQExpBuffer(firstsql, "GROUP %s",
195  fmtId(grantee->data + strlen("group ")));
196  else
197  appendPQExpBufferStr(firstsql, fmtId(grantee->data));
198  }
199  }
200  }
201  }
202 
203  /*
204  * We still need some hacking though to cover the case where new default
205  * public privileges are added in new versions: the REVOKE ALL will revoke
206  * them, leading to behavior different from what the old version had,
207  * which is generally not what's wanted. So add back default privs if the
208  * source database is too old to have had that particular priv.
209  */
210  if (remoteVersion < 80200 && strcmp(type, "DATABASE") == 0)
211  {
212  /* database CONNECT priv didn't exist before 8.2 */
213  appendPQExpBuffer(firstsql, "%sGRANT CONNECT ON %s %s TO PUBLIC;\n",
214  prefix, type, name);
215  }
216 
217  /* Scan individual ACL items */
218  for (i = 0; i < naclitems; i++)
219  {
220  if (!parseAclItem(aclitems[i], type, name, subname, remoteVersion,
221  grantee, grantor, privs, privswgo))
222  {
223  ok = false;
224  break;
225  }
226 
227  if (grantor->len == 0 && owner)
228  printfPQExpBuffer(grantor, "%s", owner);
229 
230  if (privs->len > 0 || privswgo->len > 0)
231  {
232  /*
233  * Prior to 9.6, we had to handle owner privileges in a special
234  * manner by first REVOKE'ing the rights and then GRANT'ing them
235  * after. With 9.6 and above, what we need to REVOKE and what we
236  * need to GRANT is figured out when we dump and stashed into
237  * "racls" and "acls", respectively. See above.
238  */
239  if (remoteVersion < 90600 && owner
240  && strcmp(grantee->data, owner) == 0
241  && strcmp(grantor->data, owner) == 0)
242  {
243  found_owner_privs = true;
244 
245  /*
246  * For the owner, the default privilege level is ALL WITH
247  * GRANT OPTION.
248  */
249  if (strcmp(privswgo->data, "ALL") != 0)
250  {
251  appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
252  if (subname)
253  appendPQExpBuffer(firstsql, "(%s)", subname);
254  appendPQExpBuffer(firstsql, " ON %s %s FROM %s;\n",
255  type, name, fmtId(grantee->data));
256  if (privs->len > 0)
257  appendPQExpBuffer(firstsql,
258  "%sGRANT %s ON %s %s TO %s;\n",
259  prefix, privs->data, type, name,
260  fmtId(grantee->data));
261  if (privswgo->len > 0)
262  appendPQExpBuffer(firstsql,
263  "%sGRANT %s ON %s %s TO %s WITH GRANT OPTION;\n",
264  prefix, privswgo->data, type, name,
265  fmtId(grantee->data));
266  }
267  }
268  else
269  {
270  /*
271  * For systems prior to 9.6, we can assume we are starting
272  * from no privs at this point.
273  *
274  * For 9.6 and above, at this point we have issued REVOKE
275  * statements for all initial and default privileges which are
276  * no longer present on the object (as they were passed in as
277  * 'racls') and we can simply GRANT the rights which are in
278  * 'acls'.
279  */
280  if (grantor->len > 0
281  && (!owner || strcmp(owner, grantor->data) != 0))
282  appendPQExpBuffer(secondsql, "SET SESSION AUTHORIZATION %s;\n",
283  fmtId(grantor->data));
284 
285  if (privs->len > 0)
286  {
287  appendPQExpBuffer(secondsql, "%sGRANT %s ON %s %s TO ",
288  prefix, privs->data, type, name);
289  if (grantee->len == 0)
290  appendPQExpBufferStr(secondsql, "PUBLIC;\n");
291  else if (strncmp(grantee->data, "group ",
292  strlen("group ")) == 0)
293  appendPQExpBuffer(secondsql, "GROUP %s;\n",
294  fmtId(grantee->data + strlen("group ")));
295  else
296  appendPQExpBuffer(secondsql, "%s;\n", fmtId(grantee->data));
297  }
298  if (privswgo->len > 0)
299  {
300  appendPQExpBuffer(secondsql, "%sGRANT %s ON %s %s TO ",
301  prefix, privswgo->data, type, name);
302  if (grantee->len == 0)
303  appendPQExpBufferStr(secondsql, "PUBLIC");
304  else if (strncmp(grantee->data, "group ",
305  strlen("group ")) == 0)
306  appendPQExpBuffer(secondsql, "GROUP %s",
307  fmtId(grantee->data + strlen("group ")));
308  else
309  appendPQExpBufferStr(secondsql, fmtId(grantee->data));
310  appendPQExpBufferStr(secondsql, " WITH GRANT OPTION;\n");
311  }
312 
313  if (grantor->len > 0
314  && (!owner || strcmp(owner, grantor->data) != 0))
315  appendPQExpBufferStr(secondsql, "RESET SESSION AUTHORIZATION;\n");
316  }
317  }
318  }
319 
320  /*
321  * For systems prior to 9.6, if we didn't find any owner privs, the owner
322  * must have revoked 'em all.
323  *
324  * For 9.6 and above, we handle this through the 'racls'. See above.
325  */
326  if (remoteVersion < 90600 && !found_owner_privs && owner)
327  {
328  appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
329  if (subname)
330  appendPQExpBuffer(firstsql, "(%s)", subname);
331  appendPQExpBuffer(firstsql, " ON %s %s FROM %s;\n",
332  type, name, fmtId(owner));
333  }
334 
335  destroyPQExpBuffer(grantee);
336  destroyPQExpBuffer(grantor);
337  destroyPQExpBuffer(privs);
338  destroyPQExpBuffer(privswgo);
339 
340  appendPQExpBuffer(sql, "%s%s", firstsql->data, secondsql->data);
341  destroyPQExpBuffer(firstsql);
342  destroyPQExpBuffer(secondsql);
343 
344  if (aclitems)
345  free(aclitems);
346 
347  if (raclitems)
348  free(raclitems);
349 
350  return ok;
351 }
352 
353 /*
354  * Build ALTER DEFAULT PRIVILEGES command(s) for single pg_default_acl entry.
355  *
356  * type: the object type (TABLES, FUNCTIONS, etc)
357  * nspname: schema name, or NULL for global default privileges
358  * acls: the ACL string fetched from the database
359  * owner: username of privileges owner (will be passed through fmtId)
360  * remoteVersion: version of database
361  *
362  * Returns true if okay, false if could not parse the acl string.
363  * The resulting commands (if any) are appended to the contents of 'sql'.
364  */
365 bool
366 buildDefaultACLCommands(const char *type, const char *nspname,
367  const char *acls, const char *racls,
368  const char *initacls, const char *initracls,
369  const char *owner,
370  int remoteVersion,
371  PQExpBuffer sql)
372 {
373  PQExpBuffer prefix;
374 
375  prefix = createPQExpBuffer();
376 
377  /*
378  * We incorporate the target role directly into the command, rather than
379  * playing around with SET ROLE or anything like that. This is so that a
380  * permissions error leads to nothing happening, rather than changing
381  * default privileges for the wrong user.
382  */
383  appendPQExpBuffer(prefix, "ALTER DEFAULT PRIVILEGES FOR ROLE %s ",
384  fmtId(owner));
385  if (nspname)
386  appendPQExpBuffer(prefix, "IN SCHEMA %s ", fmtId(nspname));
387 
388  if (strlen(initacls) != 0 || strlen(initracls) != 0)
389  {
390  appendPQExpBuffer(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\n");
391  if (!buildACLCommands("", NULL, type, initacls, initracls, owner,
392  prefix->data, remoteVersion, sql))
393  {
394  destroyPQExpBuffer(prefix);
395  return false;
396  }
397  appendPQExpBuffer(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\n");
398  }
399 
400  if (!buildACLCommands("", NULL, type, acls, racls, owner,
401  prefix->data, remoteVersion, sql))
402  {
403  destroyPQExpBuffer(prefix);
404  return false;
405  }
406 
407  destroyPQExpBuffer(prefix);
408 
409  return true;
410 }
411 
412 /*
413  * This will parse an aclitem string, having the general form
414  * username=privilegecodes/grantor
415  * or
416  * group groupname=privilegecodes/grantor
417  * (the "group" case occurs only with servers before 8.1).
418  *
419  * Returns true on success, false on parse error. On success, the components
420  * of the string are returned in the PQExpBuffer parameters.
421  *
422  * The returned grantee string will be the dequoted username or groupname
423  * (preceded with "group " in the latter case). Note that a grant to PUBLIC
424  * is represented by an empty grantee string. The returned grantor is the
425  * dequoted grantor name. Privilege characters are decoded and split between
426  * privileges with grant option (privswgo) and without (privs).
427  *
428  * Note: for cross-version compatibility, it's important to use ALL to
429  * represent the privilege sets whenever appropriate.
430  */
431 static bool
432 parseAclItem(const char *item, const char *type,
433  const char *name, const char *subname, int remoteVersion,
434  PQExpBuffer grantee, PQExpBuffer grantor,
435  PQExpBuffer privs, PQExpBuffer privswgo)
436 {
437  char *buf;
438  bool all_with_go = true;
439  bool all_without_go = true;
440  char *eqpos;
441  char *slpos;
442  char *pos;
443 
444  buf = strdup(item);
445  if (!buf)
446  return false;
447 
448  /* user or group name is string up to = */
449  eqpos = copyAclUserName(grantee, buf);
450  if (*eqpos != '=')
451  {
452  free(buf);
453  return false;
454  }
455 
456  /* grantor should appear after / */
457  slpos = strchr(eqpos + 1, '/');
458  if (slpos)
459  {
460  *slpos++ = '\0';
461  slpos = copyAclUserName(grantor, slpos);
462  if (*slpos != '\0')
463  {
464  free(buf);
465  return false;
466  }
467  }
468  else
469  {
470  free(buf);
471  return false;
472  }
473 
474  /* privilege codes */
475 #define CONVERT_PRIV(code, keywd) \
476 do { \
477  if ((pos = strchr(eqpos + 1, code))) \
478  { \
479  if (*(pos + 1) == '*') \
480  { \
481  AddAcl(privswgo, keywd, subname); \
482  all_without_go = false; \
483  } \
484  else \
485  { \
486  AddAcl(privs, keywd, subname); \
487  all_with_go = false; \
488  } \
489  } \
490  else \
491  all_with_go = all_without_go = false; \
492 } while (0)
493 
494  resetPQExpBuffer(privs);
495  resetPQExpBuffer(privswgo);
496 
497  if (strcmp(type, "TABLE") == 0 || strcmp(type, "SEQUENCE") == 0 ||
498  strcmp(type, "TABLES") == 0 || strcmp(type, "SEQUENCES") == 0)
499  {
500  CONVERT_PRIV('r', "SELECT");
501 
502  if (strcmp(type, "SEQUENCE") == 0 ||
503  strcmp(type, "SEQUENCES") == 0)
504  /* sequence only */
505  CONVERT_PRIV('U', "USAGE");
506  else
507  {
508  /* table only */
509  CONVERT_PRIV('a', "INSERT");
510  CONVERT_PRIV('x', "REFERENCES");
511  /* rest are not applicable to columns */
512  if (subname == NULL)
513  {
514  CONVERT_PRIV('d', "DELETE");
515  CONVERT_PRIV('t', "TRIGGER");
516  if (remoteVersion >= 80400)
517  CONVERT_PRIV('D', "TRUNCATE");
518  }
519  }
520 
521  /* UPDATE */
522  CONVERT_PRIV('w', "UPDATE");
523  }
524  else if (strcmp(type, "FUNCTION") == 0 ||
525  strcmp(type, "FUNCTIONS") == 0)
526  CONVERT_PRIV('X', "EXECUTE");
527  else if (strcmp(type, "PROCEDURE") == 0 ||
528  strcmp(type, "PROCEDURES") == 0)
529  CONVERT_PRIV('X', "EXECUTE");
530  else if (strcmp(type, "LANGUAGE") == 0)
531  CONVERT_PRIV('U', "USAGE");
532  else if (strcmp(type, "SCHEMA") == 0 ||
533  strcmp(type, "SCHEMAS") == 0)
534  {
535  CONVERT_PRIV('C', "CREATE");
536  CONVERT_PRIV('U', "USAGE");
537  }
538  else if (strcmp(type, "DATABASE") == 0)
539  {
540  CONVERT_PRIV('C', "CREATE");
541  CONVERT_PRIV('c', "CONNECT");
542  CONVERT_PRIV('T', "TEMPORARY");
543  }
544  else if (strcmp(type, "TABLESPACE") == 0)
545  CONVERT_PRIV('C', "CREATE");
546  else if (strcmp(type, "TYPE") == 0 ||
547  strcmp(type, "TYPES") == 0)
548  CONVERT_PRIV('U', "USAGE");
549  else if (strcmp(type, "FOREIGN DATA WRAPPER") == 0)
550  CONVERT_PRIV('U', "USAGE");
551  else if (strcmp(type, "FOREIGN SERVER") == 0)
552  CONVERT_PRIV('U', "USAGE");
553  else if (strcmp(type, "FOREIGN TABLE") == 0)
554  CONVERT_PRIV('r', "SELECT");
555  else if (strcmp(type, "LARGE OBJECT") == 0)
556  {
557  CONVERT_PRIV('r', "SELECT");
558  CONVERT_PRIV('w', "UPDATE");
559  }
560  else
561  abort();
562 
563 #undef CONVERT_PRIV
564 
565  if (all_with_go)
566  {
567  resetPQExpBuffer(privs);
568  printfPQExpBuffer(privswgo, "ALL");
569  if (subname)
570  appendPQExpBuffer(privswgo, "(%s)", subname);
571  }
572  else if (all_without_go)
573  {
574  resetPQExpBuffer(privswgo);
575  printfPQExpBuffer(privs, "ALL");
576  if (subname)
577  appendPQExpBuffer(privs, "(%s)", subname);
578  }
579 
580  free(buf);
581 
582  return true;
583 }
584 
585 /*
586  * Transfer a user or group name starting at *input into the output buffer,
587  * dequoting if needed. Returns a pointer to just past the input name.
588  * The name is taken to end at an unquoted '=' or end of string.
589  */
590 static char *
592 {
593  resetPQExpBuffer(output);
594 
595  while (*input && *input != '=')
596  {
597  /*
598  * If user name isn't quoted, then just add it to the output buffer
599  */
600  if (*input != '"')
601  appendPQExpBufferChar(output, *input++);
602  else
603  {
604  /* Otherwise, it's a quoted username */
605  input++;
606  /* Loop until we come across an unescaped quote */
607  while (!(*input == '"' && *(input + 1) != '"'))
608  {
609  if (*input == '\0')
610  return input; /* really a syntax error... */
611 
612  /*
613  * Quoting convention is to escape " as "". Keep this code in
614  * sync with putid() in backend's acl.c.
615  */
616  if (*input == '"' && *(input + 1) == '"')
617  input++;
618  appendPQExpBufferChar(output, *input++);
619  }
620  input++;
621  }
622  }
623  return input;
624 }
625 
626 /*
627  * Append a privilege keyword to a keyword list, inserting comma if needed.
628  */
629 static void
630 AddAcl(PQExpBuffer aclbuf, const char *keyword, const char *subname)
631 {
632  if (aclbuf->len > 0)
633  appendPQExpBufferChar(aclbuf, ',');
634  appendPQExpBufferStr(aclbuf, keyword);
635  if (subname)
636  appendPQExpBuffer(aclbuf, "(%s)", subname);
637 }
638 
639 
640 /*
641  * buildShSecLabelQuery
642  *
643  * Build a query to retrieve security labels for a shared object.
644  */
645 void
646 buildShSecLabelQuery(PGconn *conn, const char *catalog_name, uint32 objectId,
647  PQExpBuffer sql)
648 {
649  appendPQExpBuffer(sql,
650  "SELECT provider, label FROM pg_catalog.pg_shseclabel "
651  "WHERE classoid = '%s'::pg_catalog.regclass AND "
652  "objoid = %u", catalog_name, objectId);
653 }
654 
655 /*
656  * emitShSecLabels
657  *
658  * Format security label data retrieved by the query generated in
659  * buildShSecLabelQuery.
660  */
661 void
663  const char *target, const char *objname)
664 {
665  int i;
666 
667  for (i = 0; i < PQntuples(res); i++)
668  {
669  char *provider = PQgetvalue(res, i, 0);
670  char *label = PQgetvalue(res, i, 1);
671 
672  /* must use fmtId result before calling it again */
673  appendPQExpBuffer(buffer,
674  "SECURITY LABEL FOR %s ON %s",
675  fmtId(provider), target);
676  appendPQExpBuffer(buffer,
677  " %s IS ",
678  fmtId(objname));
679  appendStringLiteralConn(buffer, label, conn);
680  appendPQExpBufferStr(buffer, ";\n");
681  }
682 }
683 
684 /*
685  * buildACLQueries
686  *
687  * Build the subqueries to extract out the correct set of ACLs to be
688  * GRANT'd and REVOKE'd for the specific kind of object, accounting for any
689  * initial privileges (from pg_init_privs) and based on if we are in binary
690  * upgrade mode or not.
691  *
692  * Also builds subqueries to extract out the set of ACLs to go from the object
693  * default privileges to the privileges in pg_init_privs, if we are in binary
694  * upgrade mode, so that those privileges can be set up and recorded in the new
695  * cluster before the regular privileges are added on top of those.
696  */
697 void
698 buildACLQueries(PQExpBuffer acl_subquery, PQExpBuffer racl_subquery,
699  PQExpBuffer init_acl_subquery, PQExpBuffer init_racl_subquery,
700  const char *acl_column, const char *acl_owner,
701  const char *obj_kind, bool binary_upgrade)
702 {
703  /*
704  * To get the delta from what the permissions were at creation time
705  * (either initdb or CREATE EXTENSION) vs. what they are now, we have to
706  * look at two things:
707  *
708  * What privileges have been added, which we calculate by extracting all
709  * the current privileges (using the set of default privileges for the
710  * object type if current privileges are NULL) and then removing those
711  * which existed at creation time (again, using the set of default
712  * privileges for the object type if there were no creation time
713  * privileges).
714  *
715  * What privileges have been removed, which we calculate by extracting the
716  * privileges as they were at creation time (or the default privileges, as
717  * above), and then removing the current privileges (or the default
718  * privileges, if current privileges are NULL).
719  *
720  * As a good cross-check, both directions of these checks should result in
721  * the empty set if both the current ACL and the initial privs are NULL
722  * (meaning, in practice, that the default ACLs were there at init time
723  * and is what the current privileges are).
724  *
725  * We always perform this delta on all ACLs and expect that by the time
726  * these are run the initial privileges will be in place, even in a binary
727  * upgrade situation (see below).
728  *
729  * Finally, the order in which privileges are in the ACL string (the order
730  * they been GRANT'd in, which the backend maintains) must be preserved to
731  * ensure that GRANTs WITH GRANT OPTION and subsequent GRANTs based on
732  * those are dumped in the correct order.
733  */
734  printfPQExpBuffer(acl_subquery,
735  "(SELECT pg_catalog.array_agg(acl ORDER BY row_n) FROM "
736  "(SELECT acl, row_n FROM "
737  "pg_catalog.unnest(coalesce(%s,pg_catalog.acldefault(%s,%s))) "
738  "WITH ORDINALITY AS perm(acl,row_n) "
739  "WHERE NOT EXISTS ( "
740  "SELECT 1 FROM "
741  "pg_catalog.unnest(coalesce(pip.initprivs,pg_catalog.acldefault(%s,%s))) "
742  "AS init(init_acl) WHERE acl = init_acl)) as foo)",
743  acl_column,
744  obj_kind,
745  acl_owner,
746  obj_kind,
747  acl_owner);
748 
749  printfPQExpBuffer(racl_subquery,
750  "(SELECT pg_catalog.array_agg(acl ORDER BY row_n) FROM "
751  "(SELECT acl, row_n FROM "
752  "pg_catalog.unnest(coalesce(pip.initprivs,pg_catalog.acldefault(%s,%s))) "
753  "WITH ORDINALITY AS initp(acl,row_n) "
754  "WHERE NOT EXISTS ( "
755  "SELECT 1 FROM "
756  "pg_catalog.unnest(coalesce(%s,pg_catalog.acldefault(%s,%s))) "
757  "AS permp(orig_acl) WHERE acl = orig_acl)) as foo)",
758  obj_kind,
759  acl_owner,
760  acl_column,
761  obj_kind,
762  acl_owner);
763 
764  /*
765  * In binary upgrade mode we don't run the extension script but instead
766  * dump out the objects independently and then recreate them. To preserve
767  * the initial privileges which were set on extension objects, we need to
768  * grab the set of GRANT and REVOKE commands necessary to get from the
769  * default privileges of an object to the initial privileges as recorded
770  * in pg_init_privs.
771  *
772  * These will then be run ahead of the regular ACL commands, which were
773  * calculated using the queries above, inside of a block which sets a flag
774  * to indicate that the backend should record the results of these GRANT
775  * and REVOKE statements into pg_init_privs. This is how we preserve the
776  * contents of that catalog across binary upgrades.
777  */
778  if (binary_upgrade)
779  {
780  printfPQExpBuffer(init_acl_subquery,
781  "CASE WHEN privtype = 'e' THEN "
782  "(SELECT pg_catalog.array_agg(acl ORDER BY row_n) FROM "
783  "(SELECT acl, row_n FROM pg_catalog.unnest(pip.initprivs) "
784  "WITH ORDINALITY AS initp(acl,row_n) "
785  "WHERE NOT EXISTS ( "
786  "SELECT 1 FROM "
787  "pg_catalog.unnest(pg_catalog.acldefault(%s,%s)) "
788  "AS privm(orig_acl) WHERE acl = orig_acl)) as foo) END",
789  obj_kind,
790  acl_owner);
791 
792  printfPQExpBuffer(init_racl_subquery,
793  "CASE WHEN privtype = 'e' THEN "
794  "(SELECT pg_catalog.array_agg(acl) FROM "
795  "(SELECT acl, row_n FROM "
796  "pg_catalog.unnest(pg_catalog.acldefault(%s,%s)) "
797  "WITH ORDINALITY AS privp(acl,row_n) "
798  "WHERE NOT EXISTS ( "
799  "SELECT 1 FROM pg_catalog.unnest(pip.initprivs) "
800  "AS initp(init_acl) WHERE acl = init_acl)) as foo) END",
801  obj_kind,
802  acl_owner);
803  }
804  else
805  {
806  printfPQExpBuffer(init_acl_subquery, "NULL");
807  printfPQExpBuffer(init_racl_subquery, "NULL");
808  }
809 }
void printfPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:234
char * PQgetvalue(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3118
bool parsePGArray(const char *atext, char ***itemarray, int *nitems)
Definition: string_utils.c:663
static void AddAcl(PQExpBuffer aclbuf, const char *keyword, const char *subname)
Definition: dumputils.c:630
const char * fmtId(const char *rawid)
Definition: string_utils.c:66
static void output(uint64 loop_count)
void buildShSecLabelQuery(PGconn *conn, const char *catalog_name, uint32 objectId, PQExpBuffer sql)
Definition: dumputils.c:646
void appendPQExpBufferStr(PQExpBuffer str, const char *data)
Definition: pqexpbuffer.c:385
bool buildDefaultACLCommands(const char *type, const char *nspname, const char *acls, const char *racls, const char *initacls, const char *initracls, const char *owner, int remoteVersion, PQExpBuffer sql)
Definition: dumputils.c:366
int PQntuples(const PGresult *res)
Definition: fe-exec.c:2724
static char * copyAclUserName(PQExpBuffer output, char *input)
Definition: dumputils.c:591
void destroyPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:113
PGconn * conn
Definition: streamutil.c:46
void buildACLQueries(PQExpBuffer acl_subquery, PQExpBuffer racl_subquery, PQExpBuffer init_acl_subquery, PQExpBuffer init_racl_subquery, const char *acl_column, const char *acl_owner, const char *obj_kind, bool binary_upgrade)
Definition: dumputils.c:698
#define CONVERT_PRIV(code, keywd)
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:262
static char * buf
Definition: pg_test_fsync.c:67
unsigned int uint32
Definition: c.h:306
PQExpBuffer createPQExpBuffer(void)
Definition: pqexpbuffer.c:71
static char * label
Definition: pg_basebackup.c:82
void appendPQExpBufferChar(PQExpBuffer str, char ch)
Definition: pqexpbuffer.c:396
#define free(a)
Definition: header.h:65
void emitShSecLabels(PGconn *conn, PGresult *res, PQExpBuffer buffer, const char *target, const char *objname)
Definition: dumputils.c:662
#define Assert(condition)
Definition: c.h:680
WalTimeSample buffer[LAG_TRACKER_BUFFER_SIZE]
Definition: walsender.c:214
bool buildACLCommands(const char *name, const char *subname, const char *type, const char *acls, const char *racls, const char *owner, const char *prefix, int remoteVersion, PQExpBuffer sql)
Definition: dumputils.c:55
const char * name
Definition: encode.c:521
static int binary_upgrade
Definition: pg_dumpall.c:69
void appendStringLiteralConn(PQExpBuffer buf, const char *str, PGconn *conn)
Definition: string_utils.c:298
int i
static bool parseAclItem(const char *item, const char *type, const char *name, const char *subname, int remoteVersion, PQExpBuffer grantee, PQExpBuffer grantor, PQExpBuffer privs, PQExpBuffer privswgo)
Definition: dumputils.c:432
void resetPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:145