const char *name, const char *subname, int remoteVersion,
PQExpBuffer grantee, PQExpBuffer grantor,
PQExpBuffer privs, PQExpBuffer privswgo);
-static char *copyAclUserName(PQExpBuffer output, char *input);
+static char *dequoteAclUserName(PQExpBuffer output, char *input);
static void AddAcl(PQExpBuffer aclbuf, const char *keyword,
const char *subname);
* TABLE, SEQUENCE, FUNCTION, PROCEDURE, LANGUAGE, SCHEMA, DATABASE, TABLESPACE,
* FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT)
* acls: the ACL string fetched from the database
- * racls: the ACL string of any initial-but-now-revoked privileges
+ * baseacls: the initial ACL string for this object; can be
+ * NULL or empty string to indicate "not available from server"
* owner: username of object owner (will be passed through fmtId); can be
* NULL or empty string to indicate "no owner known"
* prefix: string to prefix to each generated command; typically empty
* Returns true if okay, false if could not parse the acl string.
* The resulting commands (if any) are appended to the contents of 'sql'.
*
+ * baseacls is typically the result of acldefault() for the object's type
+ * and owner. However, if there is a pg_init_privs entry for the object,
+ * it should instead be the initprivs ACLs. When acls is itself a
+ * pg_init_privs entry, baseacls is what to dump that relative to; then
+ * it can be either an acldefault() value or an empty ACL "{}".
+ *
* Note: when processing a default ACL, prefix is "ALTER DEFAULT PRIVILEGES "
* or something similar, and name is an empty string.
*
*/
bool
buildACLCommands(const char *name, const char *subname, const char *nspname,
- const char *type, const char *acls, const char *racls,
+ const char *type, const char *acls, const char *baseacls,
const char *owner, const char *prefix, int remoteVersion,
PQExpBuffer sql)
{
bool ok = true;
char **aclitems = NULL;
- char **raclitems = NULL;
+ char **baseitems = NULL;
+ char **grantitems = NULL;
+ char **revokeitems = NULL;
int naclitems = 0;
- int nraclitems = 0;
+ int nbaseitems = 0;
+ int ngrantitems = 0;
+ int nrevokeitems = 0;
int i;
PQExpBuffer grantee,
grantor,
privswgo;
PQExpBuffer firstsql,
secondsql;
- bool found_owner_privs = false;
- if (strlen(acls) == 0 && strlen(racls) == 0)
+ /*
+ * If the acl was NULL (initial default state), we need do nothing. Note
+ * that this is distinguishable from all-privileges-revoked, which will
+ * look like an empty array ("{}").
+ */
+ if (acls == NULL || *acls == '\0')
return true; /* object has default permissions */
/* treat empty-string owner same as NULL */
if (owner && *owner == '\0')
owner = NULL;
- if (strlen(acls) != 0)
+ /* Parse the acls array */
+ if (!parsePGArray(acls, &aclitems, &naclitems))
+ {
+ if (aclitems)
+ free(aclitems);
+ return false;
+ }
+
+ /* Parse the baseacls, if provided */
+ if (baseacls && *baseacls != '\0')
{
- if (!parsePGArray(acls, &aclitems, &naclitems))
+ if (!parsePGArray(baseacls, &baseitems, &nbaseitems))
{
if (aclitems)
free(aclitems);
+ if (baseitems)
+ free(baseitems);
return false;
}
}
- if (strlen(racls) != 0)
+ /*
+ * Compare the actual ACL with the base ACL, extracting the privileges
+ * that need to be granted (i.e., are in the actual ACL but not the base
+ * ACL) and the ones that need to be revoked (the reverse). We use plain
+ * string comparisons to check for matches. In principle that could be
+ * fooled by extraneous issues such as whitespace, but since all these
+ * strings are the work of aclitemout(), it should be OK in practice.
+ * Besides, a false mismatch will just cause the output to be a little
+ * more verbose than it really needed to be.
+ *
+ * (If we weren't given a base ACL, this stanza winds up with all the
+ * ACL's items in grantitems and nothing in revokeitems. It's not worth
+ * special-casing that.)
+ */
+ grantitems = (char **) pg_malloc(naclitems * sizeof(char *));
+ for (i = 0; i < naclitems; i++)
{
- if (!parsePGArray(racls, &raclitems, &nraclitems))
+ bool found = false;
+
+ for (int j = 0; j < nbaseitems; j++)
{
- if (aclitems)
- free(aclitems);
- if (raclitems)
- free(raclitems);
- return false;
+ if (strcmp(aclitems[i], baseitems[j]) == 0)
+ {
+ found = true;
+ break;
+ }
}
+ if (!found)
+ grantitems[ngrantitems++] = aclitems[i];
}
+ revokeitems = (char **) pg_malloc(nbaseitems * sizeof(char *));
+ for (i = 0; i < nbaseitems; i++)
+ {
+ bool found = false;
+ for (int j = 0; j < naclitems; j++)
+ {
+ if (strcmp(baseitems[i], aclitems[j]) == 0)
+ {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ revokeitems[nrevokeitems++] = baseitems[i];
+ }
+
+ /* Prepare working buffers */
grantee = createPQExpBuffer();
grantor = createPQExpBuffer();
privs = createPQExpBuffer();
/*
* At the end, these two will be pasted together to form the result.
- *
- * For older systems we use these to ensure that the owner privileges go
- * before the other ones, as a GRANT could create the default entry for
- * the object, which generally includes all rights for the owner. In more
- * recent versions we normally handle this because the owner rights come
- * first in the ACLs, but older versions might have them after the PUBLIC
- * privileges.
- *
- * For 9.6 and later systems, much of this changes. With 9.6, we check
- * the default privileges for the objects at dump time and create two sets
- * of ACLs- "racls" which are the ACLs to REVOKE from the object (as the
- * object may have initial privileges on it, along with any default ACLs
- * which are not part of the current set of privileges), and regular
- * "acls", which are the ACLs to GRANT to the object. We handle the
- * REVOKEs first, followed by the GRANTs.
*/
firstsql = createPQExpBuffer();
secondsql = createPQExpBuffer();
/*
- * For pre-9.6 systems, we always start with REVOKE ALL FROM PUBLIC, as we
- * don't wish to make any assumptions about what the default ACLs are, and
- * we do not collect them during the dump phase (and racls will always be
- * the empty set, see above).
- *
- * For 9.6 and later, if any revoke ACLs have been provided, then include
- * them in 'firstsql'.
+ * If we weren't given baseacls information, we just revoke everything and
+ * then grant what's listed in the ACL. This avoids having to embed
+ * detailed knowledge about what the defaults are/were, and it's not very
+ * expensive since servers lacking acldefault() are now rare.
*
- * Revoke ACLs happen when an object starts out life with a set of
- * privileges (eg: GRANT SELECT ON pg_class TO PUBLIC;) and the user has
- * decided to revoke those rights. Since those objects come into being
- * with those default privileges, we have to revoke them to match what the
- * current state of affairs is. Note that we only started explicitly
- * tracking such initial rights in 9.6, and prior to that all initial
- * rights are actually handled by the simple 'REVOKE ALL .. FROM PUBLIC'
- * case, for initdb-created objects. Prior to 9.6, we didn't handle
- * extensions correctly, but we do now by tracking their initial
- * privileges, in the same way we track initdb initial privileges, see
- * pg_init_privs.
+ * Otherwise, we need only revoke what's listed in revokeitems.
*/
- if (remoteVersion < 90600)
+ if (baseacls == NULL || *baseacls == '\0')
{
- Assert(nraclitems == 0);
-
+ /* We assume the old defaults only involved the owner and PUBLIC */
appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
if (subname)
appendPQExpBuffer(firstsql, "(%s)", subname);
if (nspname && *nspname)
appendPQExpBuffer(firstsql, "%s.", fmtId(nspname));
appendPQExpBuffer(firstsql, "%s FROM PUBLIC;\n", name);
+ if (owner)
+ {
+ appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
+ if (subname)
+ appendPQExpBuffer(firstsql, "(%s)", subname);
+ appendPQExpBuffer(firstsql, " ON %s ", type);
+ if (nspname && *nspname)
+ appendPQExpBuffer(firstsql, "%s.", fmtId(nspname));
+ appendPQExpBuffer(firstsql, "%s FROM %s;\n", name, fmtId(owner));
+ }
}
else
{
/* Scan individual REVOKE ACL items */
- for (i = 0; i < nraclitems; i++)
+ for (i = 0; i < nrevokeitems; i++)
{
- if (!parseAclItem(raclitems[i], type, name, subname, remoteVersion,
+ if (!parseAclItem(revokeitems[i],
+ type, name, subname, remoteVersion,
grantee, grantor, privs, NULL))
{
ok = false;
}
/*
+ * At this point we have issued REVOKE statements for all initial and
+ * default privileges that are no longer present on the object, so we are
+ * almost ready to GRANT the privileges listed in grantitems[].
+ *
* We still need some hacking though to cover the case where new default
* public privileges are added in new versions: the REVOKE ALL will revoke
* them, leading to behavior different from what the old version had,
prefix, type, name);
}
- /* Scan individual ACL items */
- for (i = 0; i < naclitems; i++)
+ /*
+ * Scan individual ACL items to be granted.
+ *
+ * The order in which privileges appear in the ACL string (the order they
+ * have been GRANT'd in, which the backend maintains) must be preserved to
+ * ensure that GRANTs WITH GRANT OPTION and subsequent GRANTs based on
+ * those are dumped in the correct order. However, some old server
+ * versions will show grants to PUBLIC before the owner's own grants; for
+ * consistency's sake, force the owner's grants to be output first.
+ */
+ for (i = 0; i < ngrantitems; i++)
{
- if (!parseAclItem(aclitems[i], type, name, subname, remoteVersion,
- grantee, grantor, privs, privswgo))
- {
- ok = false;
- break;
- }
-
- if (grantor->len == 0 && owner)
- printfPQExpBuffer(grantor, "%s", owner);
-
- if (privs->len > 0 || privswgo->len > 0)
+ if (parseAclItem(grantitems[i], type, name, subname, remoteVersion,
+ grantee, grantor, privs, privswgo))
{
/*
- * Prior to 9.6, we had to handle owner privileges in a special
- * manner by first REVOKE'ing the rights and then GRANT'ing them
- * after. With 9.6 and above, what we need to REVOKE and what we
- * need to GRANT is figured out when we dump and stashed into
- * "racls" and "acls", respectively. See above.
+ * If the grantor isn't the owner, we'll need to use SET SESSION
+ * AUTHORIZATION to become the grantor. Issue the SET/RESET only
+ * if there's something useful to do.
*/
- if (remoteVersion < 90600 && owner
- && strcmp(grantee->data, owner) == 0
- && strcmp(grantor->data, owner) == 0)
+ if (privs->len > 0 || privswgo->len > 0)
{
- found_owner_privs = true;
+ PQExpBuffer thissql;
+
+ /* Set owner as grantor if that's not explicit in the ACL */
+ if (grantor->len == 0 && owner)
+ printfPQExpBuffer(grantor, "%s", owner);
+
+ /* Make sure owner's own grants are output before others */
+ if (owner &&
+ strcmp(grantee->data, owner) == 0 &&
+ strcmp(grantor->data, owner) == 0)
+ thissql = firstsql;
+ else
+ thissql = secondsql;
- /*
- * For the owner, the default privilege level is ALL WITH
- * GRANT OPTION.
- */
- if (strcmp(privswgo->data, "ALL") != 0)
- {
- appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
- if (subname)
- appendPQExpBuffer(firstsql, "(%s)", subname);
- appendPQExpBuffer(firstsql, " ON %s ", type);
- if (nspname && *nspname)
- appendPQExpBuffer(firstsql, "%s.", fmtId(nspname));
- appendPQExpBuffer(firstsql, "%s FROM %s;\n",
- name, fmtId(grantee->data));
- if (privs->len > 0)
- {
- appendPQExpBuffer(firstsql,
- "%sGRANT %s ON %s ",
- prefix, privs->data, type);
- if (nspname && *nspname)
- appendPQExpBuffer(firstsql, "%s.", fmtId(nspname));
- appendPQExpBuffer(firstsql,
- "%s TO %s;\n",
- name, fmtId(grantee->data));
- }
- if (privswgo->len > 0)
- {
- appendPQExpBuffer(firstsql,
- "%sGRANT %s ON %s ",
- prefix, privswgo->data, type);
- if (nspname && *nspname)
- appendPQExpBuffer(firstsql, "%s.", fmtId(nspname));
- appendPQExpBuffer(firstsql,
- "%s TO %s WITH GRANT OPTION;\n",
- name, fmtId(grantee->data));
- }
- }
- }
- else
- {
- /*
- * For systems prior to 9.6, we can assume we are starting
- * from no privs at this point.
- *
- * For 9.6 and above, at this point we have issued REVOKE
- * statements for all initial and default privileges which are
- * no longer present on the object (as they were passed in as
- * 'racls') and we can simply GRANT the rights which are in
- * 'acls'.
- */
if (grantor->len > 0
&& (!owner || strcmp(owner, grantor->data) != 0))
- appendPQExpBuffer(secondsql, "SET SESSION AUTHORIZATION %s;\n",
+ appendPQExpBuffer(thissql, "SET SESSION AUTHORIZATION %s;\n",
fmtId(grantor->data));
if (privs->len > 0)
{
- appendPQExpBuffer(secondsql, "%sGRANT %s ON %s ",
+ appendPQExpBuffer(thissql, "%sGRANT %s ON %s ",
prefix, privs->data, type);
if (nspname && *nspname)
- appendPQExpBuffer(secondsql, "%s.", fmtId(nspname));
- appendPQExpBuffer(secondsql, "%s TO ", name);
+ appendPQExpBuffer(thissql, "%s.", fmtId(nspname));
+ appendPQExpBuffer(thissql, "%s TO ", name);
if (grantee->len == 0)
- appendPQExpBufferStr(secondsql, "PUBLIC;\n");
+ appendPQExpBufferStr(thissql, "PUBLIC;\n");
else if (strncmp(grantee->data, "group ",
strlen("group ")) == 0)
- appendPQExpBuffer(secondsql, "GROUP %s;\n",
+ appendPQExpBuffer(thissql, "GROUP %s;\n",
fmtId(grantee->data + strlen("group ")));
else
- appendPQExpBuffer(secondsql, "%s;\n", fmtId(grantee->data));
+ appendPQExpBuffer(thissql, "%s;\n", fmtId(grantee->data));
}
if (privswgo->len > 0)
{
- appendPQExpBuffer(secondsql, "%sGRANT %s ON %s ",
+ appendPQExpBuffer(thissql, "%sGRANT %s ON %s ",
prefix, privswgo->data, type);
if (nspname && *nspname)
- appendPQExpBuffer(secondsql, "%s.", fmtId(nspname));
- appendPQExpBuffer(secondsql, "%s TO ", name);
+ appendPQExpBuffer(thissql, "%s.", fmtId(nspname));
+ appendPQExpBuffer(thissql, "%s TO ", name);
if (grantee->len == 0)
- appendPQExpBufferStr(secondsql, "PUBLIC");
+ appendPQExpBufferStr(thissql, "PUBLIC");
else if (strncmp(grantee->data, "group ",
strlen("group ")) == 0)
- appendPQExpBuffer(secondsql, "GROUP %s",
+ appendPQExpBuffer(thissql, "GROUP %s",
fmtId(grantee->data + strlen("group ")));
else
- appendPQExpBufferStr(secondsql, fmtId(grantee->data));
- appendPQExpBufferStr(secondsql, " WITH GRANT OPTION;\n");
+ appendPQExpBufferStr(thissql, fmtId(grantee->data));
+ appendPQExpBufferStr(thissql, " WITH GRANT OPTION;\n");
}
if (grantor->len > 0
&& (!owner || strcmp(owner, grantor->data) != 0))
- appendPQExpBufferStr(secondsql, "RESET SESSION AUTHORIZATION;\n");
+ appendPQExpBufferStr(thissql, "RESET SESSION AUTHORIZATION;\n");
}
}
- }
-
- /*
- * For systems prior to 9.6, if we didn't find any owner privs, the owner
- * must have revoked 'em all.
- *
- * For 9.6 and above, we handle this through the 'racls'. See above.
- */
- if (remoteVersion < 90600 && !found_owner_privs && owner)
- {
- appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
- if (subname)
- appendPQExpBuffer(firstsql, "(%s)", subname);
- appendPQExpBuffer(firstsql, " ON %s ", type);
- if (nspname && *nspname)
- appendPQExpBuffer(firstsql, "%s.", fmtId(nspname));
- appendPQExpBuffer(firstsql, "%s FROM %s;\n",
- name, fmtId(owner));
+ else
+ {
+ /* parseAclItem failed, give up */
+ ok = false;
+ break;
+ }
}
destroyPQExpBuffer(grantee);
if (aclitems)
free(aclitems);
-
- if (raclitems)
- free(raclitems);
+ if (baseitems)
+ free(baseitems);
+ if (grantitems)
+ free(grantitems);
+ if (revokeitems)
+ free(revokeitems);
return ok;
}
/*
- * Build ALTER DEFAULT PRIVILEGES command(s) for single pg_default_acl entry.
+ * Build ALTER DEFAULT PRIVILEGES command(s) for a single pg_default_acl entry.
*
* type: the object type (TABLES, FUNCTIONS, etc)
* nspname: schema name, or NULL for global default privileges
* acls: the ACL string fetched from the database
+ * acldefault: the appropriate default ACL for the object type and owner
* owner: username of privileges owner (will be passed through fmtId)
* remoteVersion: version of database
*
*/
bool
buildDefaultACLCommands(const char *type, const char *nspname,
- const char *acls, const char *racls,
- const char *initacls, const char *initracls,
+ const char *acls, const char *acldefault,
const char *owner,
int remoteVersion,
PQExpBuffer sql)
if (nspname)
appendPQExpBuffer(prefix, "IN SCHEMA %s ", fmtId(nspname));
- if (strlen(initacls) != 0 || strlen(initracls) != 0)
- {
- appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\n");
- if (!buildACLCommands("", NULL, NULL, type,
- initacls, initracls, owner,
- prefix->data, remoteVersion, sql))
- {
- destroyPQExpBuffer(prefix);
- return false;
- }
- appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\n");
- }
-
+ /*
+ * There's no such thing as initprivs for a default ACL, so the base ACL
+ * is always just the object-type-specific default.
+ */
if (!buildACLCommands("", NULL, NULL, type,
- acls, racls, owner,
+ acls, acldefault, owner,
prefix->data, remoteVersion, sql))
{
destroyPQExpBuffer(prefix);
buf = pg_strdup(item);
/* user or group name is string up to = */
- eqpos = copyAclUserName(grantee, buf);
+ eqpos = dequoteAclUserName(grantee, buf);
if (*eqpos != '=')
{
pg_free(buf);
if (slpos)
{
*slpos++ = '\0';
- slpos = copyAclUserName(grantor, slpos);
+ slpos = dequoteAclUserName(grantor, slpos);
if (*slpos != '\0')
{
pg_free(buf);
return true;
}
+/*
+ * Transfer the role name at *input into the output buffer, adding
+ * quoting according to the same rules as putid() in backend's acl.c.
+ */
+void
+quoteAclUserName(PQExpBuffer output, const char *input)
+{
+ const char *src;
+ bool safe = true;
+
+ for (src = input; *src; src++)
+ {
+ /* This test had better match what putid() does */
+ if (!isalnum((unsigned char) *src) && *src != '_')
+ {
+ safe = false;
+ break;
+ }
+ }
+ if (!safe)
+ appendPQExpBufferChar(output, '"');
+ for (src = input; *src; src++)
+ {
+ /* A double quote character in a username is encoded as "" */
+ if (*src == '"')
+ appendPQExpBufferChar(output, '"');
+ appendPQExpBufferChar(output, *src);
+ }
+ if (!safe)
+ appendPQExpBufferChar(output, '"');
+}
+
/*
* Transfer a user or group name starting at *input into the output buffer,
* dequoting if needed. Returns a pointer to just past the input name.
* The name is taken to end at an unquoted '=' or end of string.
+ * Note: unlike quoteAclUserName(), this first clears the output buffer.
*/
static char *
-copyAclUserName(PQExpBuffer output, char *input)
+dequoteAclUserName(PQExpBuffer output, char *input)
{
resetPQExpBuffer(output);
}
}
-/*
- * buildACLQueries
- *
- * Build the subqueries to extract out the correct set of ACLs to be
- * GRANT'd and REVOKE'd for the specific kind of object, accounting for any
- * initial privileges (from pg_init_privs) and based on if we are in binary
- * upgrade mode or not.
- *
- * Also builds subqueries to extract out the set of ACLs to go from the object
- * default privileges to the privileges in pg_init_privs, if we are in binary
- * upgrade mode, so that those privileges can be set up and recorded in the new
- * cluster before the regular privileges are added on top of those.
- */
-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 *initprivs_expr,
- const char *obj_kind, bool binary_upgrade)
-{
- /*
- * To get the delta from what the permissions were at creation time
- * (either initdb or CREATE EXTENSION) vs. what they are now, we have to
- * look at two things:
- *
- * What privileges have been added, which we calculate by extracting all
- * the current privileges (using the set of default privileges for the
- * object type if current privileges are NULL) and then removing those
- * which existed at creation time (again, using the set of default
- * privileges for the object type if there were no creation time
- * privileges).
- *
- * What privileges have been removed, which we calculate by extracting the
- * privileges as they were at creation time (or the default privileges, as
- * above), and then removing the current privileges (or the default
- * privileges, if current privileges are NULL).
- *
- * As a good cross-check, both directions of these checks should result in
- * the empty set if both the current ACL and the initial privs are NULL
- * (meaning, in practice, that the default ACLs were there at init time
- * and is what the current privileges are).
- *
- * We always perform this delta on all ACLs and expect that by the time
- * these are run the initial privileges will be in place, even in a binary
- * upgrade situation (see below).
- *
- * Finally, the order in which privileges are in the ACL string (the order
- * they been GRANT'd in, which the backend maintains) must be preserved to
- * ensure that GRANTs WITH GRANT OPTION and subsequent GRANTs based on
- * those are dumped in the correct order.
- */
- printfPQExpBuffer(acl_subquery,
- "(SELECT pg_catalog.array_agg(acl ORDER BY row_n) FROM "
- "(SELECT acl, row_n FROM "
- "pg_catalog.unnest(coalesce(%s,pg_catalog.acldefault(%s,%s))) "
- "WITH ORDINALITY AS perm(acl,row_n) "
- "WHERE NOT EXISTS ( "
- "SELECT 1 FROM "
- "pg_catalog.unnest(coalesce(%s,pg_catalog.acldefault(%s,%s))) "
- "AS init(init_acl) WHERE acl = init_acl)) as foo)",
- acl_column,
- obj_kind,
- acl_owner,
- initprivs_expr,
- obj_kind,
- acl_owner);
-
- printfPQExpBuffer(racl_subquery,
- "(SELECT pg_catalog.array_agg(acl ORDER BY row_n) FROM "
- "(SELECT acl, row_n FROM "
- "pg_catalog.unnest(coalesce(%s,pg_catalog.acldefault(%s,%s))) "
- "WITH ORDINALITY AS initp(acl,row_n) "
- "WHERE NOT EXISTS ( "
- "SELECT 1 FROM "
- "pg_catalog.unnest(coalesce(%s,pg_catalog.acldefault(%s,%s))) "
- "AS permp(orig_acl) WHERE acl = orig_acl)) as foo)",
- initprivs_expr,
- obj_kind,
- acl_owner,
- acl_column,
- obj_kind,
- acl_owner);
-
- /*
- * In binary upgrade mode we don't run the extension script but instead
- * dump out the objects independently and then recreate them. To preserve
- * the initial privileges which were set on extension objects, we need to
- * grab the set of GRANT and REVOKE commands necessary to get from the
- * default privileges of an object to the initial privileges as recorded
- * in pg_init_privs.
- *
- * These will then be run ahead of the regular ACL commands, which were
- * calculated using the queries above, inside of a block which sets a flag
- * to indicate that the backend should record the results of these GRANT
- * and REVOKE statements into pg_init_privs. This is how we preserve the
- * contents of that catalog across binary upgrades.
- */
- if (binary_upgrade)
- {
- printfPQExpBuffer(init_acl_subquery,
- "CASE WHEN privtype = 'e' THEN "
- "(SELECT pg_catalog.array_agg(acl ORDER BY row_n) FROM "
- "(SELECT acl, row_n FROM pg_catalog.unnest(%s) "
- "WITH ORDINALITY AS initp(acl,row_n) "
- "WHERE NOT EXISTS ( "
- "SELECT 1 FROM "
- "pg_catalog.unnest(pg_catalog.acldefault(%s,%s)) "
- "AS privm(orig_acl) WHERE acl = orig_acl)) as foo) END",
- initprivs_expr,
- obj_kind,
- acl_owner);
-
- printfPQExpBuffer(init_racl_subquery,
- "CASE WHEN privtype = 'e' THEN "
- "(SELECT pg_catalog.array_agg(acl) FROM "
- "(SELECT acl, row_n FROM "
- "pg_catalog.unnest(pg_catalog.acldefault(%s,%s)) "
- "WITH ORDINALITY AS privp(acl,row_n) "
- "WHERE NOT EXISTS ( "
- "SELECT 1 FROM pg_catalog.unnest(%s) "
- "AS initp(init_acl) WHERE acl = init_acl)) as foo) END",
- obj_kind,
- acl_owner,
- initprivs_expr);
- }
- else
- {
- printfPQExpBuffer(init_acl_subquery, "NULL");
- printfPQExpBuffer(init_racl_subquery, "NULL");
- }
-}
/*
* Detect whether the given GUC variable is of GUC_LIST_QUOTE type.
static void dumpTableData(Archive *fout, const TableDataInfo *tdinfo);
static void refreshMatViewData(Archive *fout, const TableDataInfo *tdinfo);
static void guessConstraintInheritance(TableInfo *tblinfo, int numTables);
+static void getAdditionalACLs(Archive *fout);
static void dumpCommentExtended(Archive *fout, const char *type,
const char *name, const char *namespace,
const char *owner, CatalogId catalogId,
static DumpId dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
const char *type, const char *name, const char *subname,
const char *nspname, const char *owner,
- const char *acls, const char *racls,
- const char *initacls, const char *initracls);
+ const DumpableAcl *dacl);
static void getDependencies(Archive *fout);
static void BuildArchiveDependencies(Archive *fout);
getDependencies(fout);
/*
- * Collect comments and security labels, if wanted.
+ * Collect ACLs, comments, and security labels, if wanted.
*/
+ if (!dopt.aclsSkip)
+ getAdditionalACLs(fout);
if (!dopt.no_comments)
collectComments(fout);
if (!dopt.no_security_labels)
i_frozenxid,
i_minmxid,
i_datacl,
- i_rdatacl,
+ i_acldefault,
i_datistemplate,
i_datconnlimit,
i_tablespace;
CatalogId dbCatId;
DumpId dbDumpId;
+ DumpableAcl dbdacl;
const char *datname,
*dba,
*encoding,
*collate,
*ctype,
- *datacl,
- *rdatacl,
*datistemplate,
*datconnlimit,
*tablespace;
/*
* Fetch the database-level properties for this database.
- *
- * The order in which privileges are in the ACL string (the order they
- * have been GRANT'd in, which the backend maintains) must be preserved to
- * ensure that GRANTs WITH GRANT OPTION and subsequent GRANTs based on
- * those are dumped in the correct order. Note that initial privileges
- * (pg_init_privs) are not supported on databases, so this logic cannot
- * make use of buildACLQueries().
*/
- if (fout->remoteVersion >= 90600)
+ if (fout->remoteVersion >= 90300)
{
appendPQExpBuffer(dbQry, "SELECT tableoid, oid, datname, "
"(%s datdba) AS dba, "
"pg_encoding_to_char(encoding) AS encoding, "
"datcollate, datctype, datfrozenxid, datminmxid, "
- "(SELECT array_agg(acl ORDER BY row_n) FROM "
- " (SELECT acl, row_n FROM "
- " unnest(coalesce(datacl,acldefault('d',datdba))) "
- " WITH ORDINALITY AS perm(acl,row_n) "
- " WHERE NOT EXISTS ( "
- " SELECT 1 "
- " FROM unnest(acldefault('d',datdba)) "
- " AS init(init_acl) "
- " WHERE acl = init_acl)) AS datacls) "
- " AS datacl, "
- "(SELECT array_agg(acl ORDER BY row_n) FROM "
- " (SELECT acl, row_n FROM "
- " unnest(acldefault('d',datdba)) "
- " WITH ORDINALITY AS initp(acl,row_n) "
- " WHERE NOT EXISTS ( "
- " SELECT 1 "
- " FROM unnest(coalesce(datacl,acldefault('d',datdba))) "
- " AS permp(orig_acl) "
- " WHERE acl = orig_acl)) AS rdatacls) "
- " AS rdatacl, "
+ "datacl, acldefault('d', datdba) AS acldefault, "
"datistemplate, datconnlimit, "
"(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
"shobj_description(oid, 'pg_database') AS description "
"WHERE datname = current_database()",
username_subquery);
}
- else if (fout->remoteVersion >= 90300)
+ else if (fout->remoteVersion >= 90200)
{
appendPQExpBuffer(dbQry, "SELECT tableoid, oid, datname, "
"(%s datdba) AS dba, "
"pg_encoding_to_char(encoding) AS encoding, "
- "datcollate, datctype, datfrozenxid, datminmxid, "
- "datacl, '' as rdatacl, datistemplate, datconnlimit, "
+ "datcollate, datctype, datfrozenxid, 0 AS datminmxid, "
+ "datacl, acldefault('d', datdba) AS acldefault, "
+ "datistemplate, datconnlimit, "
"(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
"shobj_description(oid, 'pg_database') AS description "
"(%s datdba) AS dba, "
"pg_encoding_to_char(encoding) AS encoding, "
"datcollate, datctype, datfrozenxid, 0 AS datminmxid, "
- "datacl, '' as rdatacl, datistemplate, datconnlimit, "
+ "datacl, NULL AS acldefault, "
+ "datistemplate, datconnlimit, "
"(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
"shobj_description(oid, 'pg_database') AS description "
"(%s datdba) AS dba, "
"pg_encoding_to_char(encoding) AS encoding, "
"NULL AS datcollate, NULL AS datctype, datfrozenxid, 0 AS datminmxid, "
- "datacl, '' as rdatacl, datistemplate, datconnlimit, "
+ "datacl, NULL AS acldefault, "
+ "datistemplate, datconnlimit, "
"(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
"shobj_description(oid, 'pg_database') AS description "
"(%s datdba) AS dba, "
"pg_encoding_to_char(encoding) AS encoding, "
"NULL AS datcollate, NULL AS datctype, datfrozenxid, 0 AS datminmxid, "
- "datacl, '' as rdatacl, datistemplate, "
- "-1 as datconnlimit, "
+ "datacl, NULL AS acldefault, "
+ "datistemplate, -1 AS datconnlimit, "
"(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace "
"FROM pg_database "
"WHERE datname = current_database()",
i_frozenxid = PQfnumber(res, "datfrozenxid");
i_minmxid = PQfnumber(res, "datminmxid");
i_datacl = PQfnumber(res, "datacl");
- i_rdatacl = PQfnumber(res, "rdatacl");
+ i_acldefault = PQfnumber(res, "acldefault");
i_datistemplate = PQfnumber(res, "datistemplate");
i_datconnlimit = PQfnumber(res, "datconnlimit");
i_tablespace = PQfnumber(res, "tablespace");
ctype = PQgetvalue(res, 0, i_ctype);
frozenxid = atooid(PQgetvalue(res, 0, i_frozenxid));
minmxid = atooid(PQgetvalue(res, 0, i_minmxid));
- datacl = PQgetvalue(res, 0, i_datacl);
- rdatacl = PQgetvalue(res, 0, i_rdatacl);
+ dbdacl.acl = PQgetvalue(res, 0, i_datacl);
+ dbdacl.acldefault = PQgetvalue(res, 0, i_acldefault);
datistemplate = PQgetvalue(res, 0, i_datistemplate);
datconnlimit = PQgetvalue(res, 0, i_datconnlimit);
tablespace = PQgetvalue(res, 0, i_tablespace);
* Dump ACL if any. Note that we do not support initial privileges
* (pg_init_privs) on databases.
*/
+ dbdacl.privtype = 0;
+ dbdacl.initprivs = NULL;
+
dumpACL(fout, dbDumpId, InvalidDumpId, "DATABASE",
qdatname, NULL, NULL,
- dba, datacl, rdatacl, "", "");
+ dba, &dbdacl);
/*
* Now construct a DATABASE PROPERTIES archive entry to restore any
int i_oid;
int i_lomowner;
int i_lomacl;
- int i_rlomacl;
- int i_initlomacl;
- int i_initrlomacl;
+ int i_acldefault;
pg_log_info("reading large objects");
/* Fetch BLOB OIDs, and owner/ACL data if >= 9.0 */
- if (fout->remoteVersion >= 90600)
+ if (fout->remoteVersion >= 90200)
{
- PQExpBuffer acl_subquery = createPQExpBuffer();
- PQExpBuffer racl_subquery = createPQExpBuffer();
- PQExpBuffer init_acl_subquery = createPQExpBuffer();
- PQExpBuffer init_racl_subquery = createPQExpBuffer();
-
- buildACLQueries(acl_subquery, racl_subquery, init_acl_subquery,
- init_racl_subquery, "l.lomacl", "l.lomowner",
- "pip.initprivs", "'L'", dopt->binary_upgrade);
-
appendPQExpBuffer(blobQry,
- "SELECT l.oid, (%s l.lomowner) AS rolname, "
- "%s AS lomacl, "
- "%s AS rlomacl, "
- "%s AS initlomacl, "
- "%s AS initrlomacl "
- "FROM pg_largeobject_metadata l "
- "LEFT JOIN pg_init_privs pip ON "
- "(l.oid = pip.objoid "
- "AND pip.classoid = 'pg_largeobject'::regclass "
- "AND pip.objsubid = 0) ",
- username_subquery,
- acl_subquery->data,
- racl_subquery->data,
- init_acl_subquery->data,
- init_racl_subquery->data);
-
- destroyPQExpBuffer(acl_subquery);
- destroyPQExpBuffer(racl_subquery);
- destroyPQExpBuffer(init_acl_subquery);
- destroyPQExpBuffer(init_racl_subquery);
+ "SELECT oid, (%s lomowner) AS rolname, lomacl, "
+ "acldefault('L', lomowner) AS acldefault "
+ "FROM pg_largeobject_metadata",
+ username_subquery);
}
else if (fout->remoteVersion >= 90000)
appendPQExpBuffer(blobQry,
"SELECT oid, (%s lomowner) AS rolname, lomacl, "
- "NULL AS rlomacl, NULL AS initlomacl, "
- "NULL AS initrlomacl "
- " FROM pg_largeobject_metadata",
+ "NULL AS acldefault "
+ "FROM pg_largeobject_metadata",
username_subquery);
else
appendPQExpBufferStr(blobQry,
"SELECT DISTINCT loid AS oid, "
"NULL::name AS rolname, NULL::oid AS lomacl, "
- "NULL::oid AS rlomacl, NULL::oid AS initlomacl, "
- "NULL::oid AS initrlomacl "
+ "NULL::oid AS acldefault "
" FROM pg_largeobject");
res = ExecuteSqlQuery(fout, blobQry->data, PGRES_TUPLES_OK);
i_oid = PQfnumber(res, "oid");
i_lomowner = PQfnumber(res, "rolname");
i_lomacl = PQfnumber(res, "lomacl");
- i_rlomacl = PQfnumber(res, "rlomacl");
- i_initlomacl = PQfnumber(res, "initlomacl");
- i_initrlomacl = PQfnumber(res, "initrlomacl");
+ i_acldefault = PQfnumber(res, "acldefault");
ntups = PQntuples(res);
AssignDumpId(&binfo[i].dobj);
binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid));
+ binfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_lomacl));
+ binfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
+ binfo[i].dacl.privtype = 0;
+ binfo[i].dacl.initprivs = NULL;
binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner));
- binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl));
- binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl));
- binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl));
- binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl));
/* Blobs have data */
binfo[i].dobj.components |= DUMP_COMPONENT_DATA;
/* Mark whether blob has an ACL */
- if (!(PQgetisnull(res, i, i_lomacl) &&
- PQgetisnull(res, i, i_rlomacl) &&
- PQgetisnull(res, i, i_initlomacl) &&
- PQgetisnull(res, i, i_initrlomacl)))
+ if (!PQgetisnull(res, i, i_lomacl))
binfo[i].dobj.components |= DUMP_COMPONENT_ACL;
/*
if (binfo->dobj.dump & DUMP_COMPONENT_ACL)
dumpACL(fout, binfo->dobj.dumpId, InvalidDumpId, "LARGE OBJECT",
binfo->dobj.name, NULL,
- NULL, binfo->rolname, binfo->blobacl, binfo->rblobacl,
- binfo->initblobacl, binfo->initrblobacl);
+ NULL, binfo->rolname, &binfo->dacl);
destroyPQExpBuffer(cquery);
destroyPQExpBuffer(dquery);
NamespaceInfo *
getNamespaces(Archive *fout, int *numNamespaces)
{
- DumpOptions *dopt = fout->dopt;
PGresult *res;
int ntups;
int i;
int i_nspowner;
int i_rolname;
int i_nspacl;
- int i_rnspacl;
- int i_initnspacl;
- int i_initrnspacl;
+ int i_acldefault;
query = createPQExpBuffer();
* we fetch all namespaces including system ones, so that every object we
* read in can be linked to a containing namespace.
*/
- if (fout->remoteVersion >= 90600)
- {
- PQExpBuffer acl_subquery = createPQExpBuffer();
- PQExpBuffer racl_subquery = createPQExpBuffer();
- PQExpBuffer init_acl_subquery = createPQExpBuffer();
- PQExpBuffer init_racl_subquery = createPQExpBuffer();
-
- /*
- * Bypass pg_init_privs.initprivs for the public schema, for several
- * reasons. First, dropping and recreating the schema detaches it
- * from its pg_init_privs row, but an empty destination database
- * starts with this ACL nonetheless. Second, we support dump/reload
- * of public schema ownership changes. ALTER SCHEMA OWNER filters
- * nspacl through aclnewowner(), but initprivs continues to reflect
- * the initial owner. Hence, synthesize the value that nspacl will
- * have after the restore's ALTER SCHEMA OWNER. Third, this makes the
- * destination database match the source's ACL, even if the latter was
- * an initdb-default ACL, which changed in v15. An upgrade pulls in
- * changes to most system object ACLs that the DBA had not customized.
- * We've made the public schema depart from that, because changing its
- * ACL so easily breaks applications.
- */
- buildACLQueries(acl_subquery, racl_subquery, init_acl_subquery,
- init_racl_subquery, "n.nspacl", "n.nspowner",
- "CASE WHEN n.nspname = 'public' THEN array["
- " format('%s=UC/%s', "
- " n.nspowner::regrole, n.nspowner::regrole),"
- " format('=U/%s', n.nspowner::regrole)]::aclitem[] "
- "ELSE pip.initprivs END",
- "'n'", dopt->binary_upgrade);
-
+ if (fout->remoteVersion >= 90200)
appendPQExpBuffer(query, "SELECT n.tableoid, n.oid, n.nspname, "
"n.nspowner, "
"(%s nspowner) AS rolname, "
- "%s as nspacl, "
- "%s as rnspacl, "
- "%s as initnspacl, "
- "%s as initrnspacl "
- "FROM pg_namespace n "
- "LEFT JOIN pg_init_privs pip "
- "ON (n.oid = pip.objoid "
- "AND pip.classoid = 'pg_namespace'::regclass "
- "AND pip.objsubid = 0",
- username_subquery,
- acl_subquery->data,
- racl_subquery->data,
- init_acl_subquery->data,
- init_racl_subquery->data);
-
- appendPQExpBufferStr(query, ") ");
-
- destroyPQExpBuffer(acl_subquery);
- destroyPQExpBuffer(racl_subquery);
- destroyPQExpBuffer(init_acl_subquery);
- destroyPQExpBuffer(init_racl_subquery);
- }
+ "n.nspacl, "
+ "acldefault('n', n.nspowner) AS acldefault "
+ "FROM pg_namespace n",
+ username_subquery);
else
appendPQExpBuffer(query, "SELECT tableoid, oid, nspname, nspowner, "
"(%s nspowner) AS rolname, "
- "nspacl, NULL as rnspacl, "
- "NULL AS initnspacl, NULL as initrnspacl "
+ "nspacl, NULL AS acldefault "
"FROM pg_namespace",
username_subquery);
i_nspowner = PQfnumber(res, "nspowner");
i_rolname = PQfnumber(res, "rolname");
i_nspacl = PQfnumber(res, "nspacl");
- i_rnspacl = PQfnumber(res, "rnspacl");
- i_initnspacl = PQfnumber(res, "initnspacl");
- i_initrnspacl = PQfnumber(res, "initrnspacl");
+ i_acldefault = PQfnumber(res, "acldefault");
for (i = 0; i < ntups; i++)
{
nsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
AssignDumpId(&nsinfo[i].dobj);
nsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_nspname));
+ nsinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_nspacl));
+ nsinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
+ nsinfo[i].dacl.privtype = 0;
+ nsinfo[i].dacl.initprivs = NULL;
nsinfo[i].nspowner = atooid(PQgetvalue(res, i, i_nspowner));
nsinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
- nsinfo[i].nspacl = pg_strdup(PQgetvalue(res, i, i_nspacl));
- nsinfo[i].rnspacl = pg_strdup(PQgetvalue(res, i, i_rnspacl));
- nsinfo[i].initnspacl = pg_strdup(PQgetvalue(res, i, i_initnspacl));
- nsinfo[i].initrnspacl = pg_strdup(PQgetvalue(res, i, i_initrnspacl));
/* Decide whether to dump this namespace */
selectDumpableNamespace(&nsinfo[i], fout);
/* Mark whether namespace has an ACL */
- if (!(PQgetisnull(res, i, i_nspacl) &&
- PQgetisnull(res, i, i_rnspacl) &&
- PQgetisnull(res, i, i_initnspacl) &&
- PQgetisnull(res, i, i_initrnspacl)))
+ if (!PQgetisnull(res, i, i_nspacl))
+ nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
+
+ /*
+ * We ignore any pg_init_privs.initprivs entry for the public schema
+ * and assume a predetermined default, for several reasons. First,
+ * dropping and recreating the schema removes its pg_init_privs entry,
+ * but an empty destination database starts with this ACL nonetheless.
+ * Second, we support dump/reload of public schema ownership changes.
+ * ALTER SCHEMA OWNER filters nspacl through aclnewowner(), but
+ * initprivs continues to reflect the initial owner. Hence,
+ * synthesize the value that nspacl will have after the restore's
+ * ALTER SCHEMA OWNER. Third, this makes the destination database
+ * match the source's ACL, even if the latter was an initdb-default
+ * ACL, which changed in v15. An upgrade pulls in changes to most
+ * system object ACLs that the DBA had not customized. We've made the
+ * public schema depart from that, because changing its ACL so easily
+ * breaks applications.
+ */
+ if (strcmp(nsinfo[i].dobj.name, "public") == 0)
+ {
+ PQExpBuffer aclarray = createPQExpBuffer();
+ PQExpBuffer aclitem = createPQExpBuffer();
+
+ /* Standard ACL as of v15 is {owner=UC/owner,=U/owner} */
+ appendPQExpBufferChar(aclarray, '{');
+ quoteAclUserName(aclitem, nsinfo[i].rolname);
+ appendPQExpBufferStr(aclitem, "=UC/");
+ quoteAclUserName(aclitem, nsinfo[i].rolname);
+ appendPGArray(aclarray, aclitem->data);
+ resetPQExpBuffer(aclitem);
+ appendPQExpBufferStr(aclitem, "=U/");
+ quoteAclUserName(aclitem, nsinfo[i].rolname);
+ appendPGArray(aclarray, aclitem->data);
+ appendPQExpBufferChar(aclarray, '}');
+
+ nsinfo[i].dacl.privtype = 'i';
+ nsinfo[i].dacl.initprivs = pstrdup(aclarray->data);
nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
+ destroyPQExpBuffer(aclarray);
+ destroyPQExpBuffer(aclitem);
+ }
+
if (strlen(nsinfo[i].rolname) == 0)
pg_log_warning("owner of schema \"%s\" appears to be invalid",
nsinfo[i].dobj.name);
TypeInfo *
getTypes(Archive *fout, int *numTypes)
{
- DumpOptions *dopt = fout->dopt;
PGresult *res;
int ntups;
int i;
int i_typname;
int i_typnamespace;
int i_typacl;
- int i_rtypacl;
- int i_inittypacl;
- int i_initrtypacl;
+ int i_acldefault;
int i_rolname;
int i_typelem;
int i_typrelid;
* cost of the subselect probe for all standard types. This would have to
* be revisited if the backend ever allows renaming of array types.
*/
-
- if (fout->remoteVersion >= 90600)
- {
- PQExpBuffer acl_subquery = createPQExpBuffer();
- PQExpBuffer racl_subquery = createPQExpBuffer();
- PQExpBuffer initacl_subquery = createPQExpBuffer();
- PQExpBuffer initracl_subquery = createPQExpBuffer();
-
- buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
- initracl_subquery, "t.typacl", "t.typowner",
- "pip.initprivs", "'T'", dopt->binary_upgrade);
-
- appendPQExpBuffer(query, "SELECT t.tableoid, t.oid, t.typname, "
- "t.typnamespace, "
- "%s AS typacl, "
- "%s AS rtypacl, "
- "%s AS inittypacl, "
- "%s AS initrtypacl, "
- "(%s t.typowner) AS rolname, "
- "t.typelem, t.typrelid, "
- "CASE WHEN t.typrelid = 0 THEN ' '::\"char\" "
- "ELSE (SELECT relkind FROM pg_class WHERE oid = t.typrelid) END AS typrelkind, "
- "t.typtype, t.typisdefined, "
- "t.typname[0] = '_' AND t.typelem != 0 AND "
- "(SELECT typarray FROM pg_type te WHERE oid = t.typelem) = t.oid AS isarray "
- "FROM pg_type t "
- "LEFT JOIN pg_init_privs pip ON "
- "(t.oid = pip.objoid "
- "AND pip.classoid = 'pg_type'::regclass "
- "AND pip.objsubid = 0) ",
- acl_subquery->data,
- racl_subquery->data,
- initacl_subquery->data,
- initracl_subquery->data,
- username_subquery);
-
- destroyPQExpBuffer(acl_subquery);
- destroyPQExpBuffer(racl_subquery);
- destroyPQExpBuffer(initacl_subquery);
- destroyPQExpBuffer(initracl_subquery);
- }
- else if (fout->remoteVersion >= 90200)
+ if (fout->remoteVersion >= 90200)
{
appendPQExpBuffer(query, "SELECT tableoid, oid, typname, "
- "typnamespace, typacl, NULL as rtypacl, "
- "NULL AS inittypacl, NULL AS initrtypacl, "
+ "typnamespace, typacl, "
+ "acldefault('T', typowner) AS acldefault, "
"(%s typowner) AS rolname, "
"typelem, typrelid, "
"CASE WHEN typrelid = 0 THEN ' '::\"char\" "
else if (fout->remoteVersion >= 80300)
{
appendPQExpBuffer(query, "SELECT tableoid, oid, typname, "
- "typnamespace, NULL AS typacl, NULL as rtypacl, "
- "NULL AS inittypacl, NULL AS initrtypacl, "
+ "typnamespace, NULL AS typacl, NULL AS acldefault, "
"(%s typowner) AS rolname, "
"typelem, typrelid, "
"CASE WHEN typrelid = 0 THEN ' '::\"char\" "
else
{
appendPQExpBuffer(query, "SELECT tableoid, oid, typname, "
- "typnamespace, NULL AS typacl, NULL as rtypacl, "
- "NULL AS inittypacl, NULL AS initrtypacl, "
+ "typnamespace, NULL AS typacl, NULL AS acldefault, "
"(%s typowner) AS rolname, "
"typelem, typrelid, "
"CASE WHEN typrelid = 0 THEN ' '::\"char\" "
i_typname = PQfnumber(res, "typname");
i_typnamespace = PQfnumber(res, "typnamespace");
i_typacl = PQfnumber(res, "typacl");
- i_rtypacl = PQfnumber(res, "rtypacl");
- i_inittypacl = PQfnumber(res, "inittypacl");
- i_initrtypacl = PQfnumber(res, "initrtypacl");
+ i_acldefault = PQfnumber(res, "acldefault");
i_rolname = PQfnumber(res, "rolname");
i_typelem = PQfnumber(res, "typelem");
i_typrelid = PQfnumber(res, "typrelid");
tyinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_typname));
tyinfo[i].dobj.namespace =
findNamespace(atooid(PQgetvalue(res, i, i_typnamespace)));
+ tyinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_typacl));
+ tyinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
+ tyinfo[i].dacl.privtype = 0;
+ tyinfo[i].dacl.initprivs = NULL;
tyinfo[i].ftypname = NULL; /* may get filled later */
tyinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
- tyinfo[i].typacl = pg_strdup(PQgetvalue(res, i, i_typacl));
- tyinfo[i].rtypacl = pg_strdup(PQgetvalue(res, i, i_rtypacl));
- tyinfo[i].inittypacl = pg_strdup(PQgetvalue(res, i, i_inittypacl));
- tyinfo[i].initrtypacl = pg_strdup(PQgetvalue(res, i, i_initrtypacl));
tyinfo[i].typelem = atooid(PQgetvalue(res, i, i_typelem));
tyinfo[i].typrelid = atooid(PQgetvalue(res, i, i_typrelid));
tyinfo[i].typrelkind = *PQgetvalue(res, i, i_typrelkind);
selectDumpableType(&tyinfo[i], fout);
/* Mark whether type has an ACL */
- if (!(PQgetisnull(res, i, i_typacl) &&
- PQgetisnull(res, i, i_rtypacl) &&
- PQgetisnull(res, i, i_inittypacl) &&
- PQgetisnull(res, i, i_initrtypacl)))
+ if (!PQgetisnull(res, i, i_typacl))
tyinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
/*
int i_proargtypes;
int i_rolname;
int i_aggacl;
- int i_raggacl;
- int i_initaggacl;
- int i_initraggacl;
+ int i_acldefault;
/*
* Find all interesting aggregates. See comment in getFuncs() for the
*/
if (fout->remoteVersion >= 90600)
{
- PQExpBuffer acl_subquery = createPQExpBuffer();
- PQExpBuffer racl_subquery = createPQExpBuffer();
- PQExpBuffer initacl_subquery = createPQExpBuffer();
- PQExpBuffer initracl_subquery = createPQExpBuffer();
const char *agg_check;
- buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
- initracl_subquery, "p.proacl", "p.proowner",
- "pip.initprivs", "'f'", dopt->binary_upgrade);
-
agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
: "p.proisagg");
"p.pronamespace AS aggnamespace, "
"p.pronargs, p.proargtypes, "
"(%s p.proowner) AS rolname, "
- "%s AS aggacl, "
- "%s AS raggacl, "
- "%s AS initaggacl, "
- "%s AS initraggacl "
+ "p.proacl AS aggacl, "
+ "acldefault('f', p.proowner) AS acldefault "
"FROM pg_proc p "
"LEFT JOIN pg_init_privs pip ON "
"(p.oid = pip.objoid "
"WHERE nspname = 'pg_catalog') OR "
"p.proacl IS DISTINCT FROM pip.initprivs",
username_subquery,
- acl_subquery->data,
- racl_subquery->data,
- initacl_subquery->data,
- initracl_subquery->data,
agg_check);
if (dopt->binary_upgrade)
appendPQExpBufferStr(query,
"refclassid = 'pg_extension'::regclass AND "
"deptype = 'e')");
appendPQExpBufferChar(query, ')');
-
- destroyPQExpBuffer(acl_subquery);
- destroyPQExpBuffer(racl_subquery);
- destroyPQExpBuffer(initacl_subquery);
- destroyPQExpBuffer(initracl_subquery);
+ }
+ else if (fout->remoteVersion >= 90200)
+ {
+ appendPQExpBuffer(query, "SELECT tableoid, oid, proname AS aggname, "
+ "pronamespace AS aggnamespace, "
+ "pronargs, proargtypes, "
+ "(%s proowner) AS rolname, "
+ "proacl AS aggacl, "
+ "acldefault('f', proowner) AS acldefault "
+ "FROM pg_proc p "
+ "WHERE proisagg AND ("
+ "pronamespace != "
+ "(SELECT oid FROM pg_namespace "
+ "WHERE nspname = 'pg_catalog')",
+ username_subquery);
+ if (dopt->binary_upgrade)
+ appendPQExpBufferStr(query,
+ " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
+ "classid = 'pg_proc'::regclass AND "
+ "objid = p.oid AND "
+ "refclassid = 'pg_extension'::regclass AND "
+ "deptype = 'e')");
+ appendPQExpBufferChar(query, ')');
}
else if (fout->remoteVersion >= 80200)
{
"pronargs, proargtypes, "
"(%s proowner) AS rolname, "
"proacl AS aggacl, "
- "NULL AS raggacl, "
- "NULL AS initaggacl, NULL AS initraggacl "
+ "NULL AS acldefault "
"FROM pg_proc p "
"WHERE proisagg AND ("
"pronamespace != "
"proargtypes, "
"(%s proowner) AS rolname, "
"proacl AS aggacl, "
- "NULL AS raggacl, "
- "NULL AS initaggacl, NULL AS initraggacl "
+ "NULL AS acldefault "
"FROM pg_proc "
"WHERE proisagg "
"AND pronamespace != "
i_proargtypes = PQfnumber(res, "proargtypes");
i_rolname = PQfnumber(res, "rolname");
i_aggacl = PQfnumber(res, "aggacl");
- i_raggacl = PQfnumber(res, "raggacl");
- i_initaggacl = PQfnumber(res, "initaggacl");
- i_initraggacl = PQfnumber(res, "initraggacl");
+ i_acldefault = PQfnumber(res, "acldefault");
for (i = 0; i < ntups; i++)
{
agginfo[i].aggfn.dobj.name = pg_strdup(PQgetvalue(res, i, i_aggname));
agginfo[i].aggfn.dobj.namespace =
findNamespace(atooid(PQgetvalue(res, i, i_aggnamespace)));
+ agginfo[i].aggfn.dacl.acl = pg_strdup(PQgetvalue(res, i, i_aggacl));
+ agginfo[i].aggfn.dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
+ agginfo[i].aggfn.dacl.privtype = 0;
+ agginfo[i].aggfn.dacl.initprivs = NULL;
agginfo[i].aggfn.rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
if (strlen(agginfo[i].aggfn.rolname) == 0)
pg_log_warning("owner of aggregate function \"%s\" appears to be invalid",
agginfo[i].aggfn.dobj.name);
agginfo[i].aggfn.lang = InvalidOid; /* not currently interesting */
agginfo[i].aggfn.prorettype = InvalidOid; /* not saved */
- agginfo[i].aggfn.proacl = pg_strdup(PQgetvalue(res, i, i_aggacl));
- agginfo[i].aggfn.rproacl = pg_strdup(PQgetvalue(res, i, i_raggacl));
- agginfo[i].aggfn.initproacl = pg_strdup(PQgetvalue(res, i, i_initaggacl));
- agginfo[i].aggfn.initrproacl = pg_strdup(PQgetvalue(res, i, i_initraggacl));
agginfo[i].aggfn.nargs = atoi(PQgetvalue(res, i, i_pronargs));
if (agginfo[i].aggfn.nargs == 0)
agginfo[i].aggfn.argtypes = NULL;
selectDumpableObject(&(agginfo[i].aggfn.dobj), fout);
/* Mark whether aggregate has an ACL */
- if (!(PQgetisnull(res, i, i_aggacl) &&
- PQgetisnull(res, i, i_raggacl) &&
- PQgetisnull(res, i, i_initaggacl) &&
- PQgetisnull(res, i, i_initraggacl)))
+ if (!PQgetisnull(res, i, i_aggacl))
agginfo[i].aggfn.dobj.components |= DUMP_COMPONENT_ACL;
}
int i_proargtypes;
int i_prorettype;
int i_proacl;
- int i_rproacl;
- int i_initproacl;
- int i_initrproacl;
+ int i_acldefault;
/*
* Find all interesting functions. This is a bit complicated:
* to gather the information about them, though they won't be dumped if
* they are built-in. Also, in 9.6 and up, include functions in
* pg_catalog if they have an ACL different from what's shown in
- * pg_init_privs.
+ * pg_init_privs (so we have to join to pg_init_privs; annoying).
*/
if (fout->remoteVersion >= 90600)
{
- PQExpBuffer acl_subquery = createPQExpBuffer();
- PQExpBuffer racl_subquery = createPQExpBuffer();
- PQExpBuffer initacl_subquery = createPQExpBuffer();
- PQExpBuffer initracl_subquery = createPQExpBuffer();
const char *not_agg_check;
- buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
- initracl_subquery, "p.proacl", "p.proowner",
- "pip.initprivs", "'f'", dopt->binary_upgrade);
-
not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
: "NOT p.proisagg");
appendPQExpBuffer(query,
"SELECT p.tableoid, p.oid, p.proname, p.prolang, "
"p.pronargs, p.proargtypes, p.prorettype, "
- "%s AS proacl, "
- "%s AS rproacl, "
- "%s AS initproacl, "
- "%s AS initrproacl, "
+ "p.proacl, "
+ "acldefault('f', p.proowner) AS acldefault, "
"p.pronamespace, "
"(%s p.proowner) AS rolname "
"FROM pg_proc p "
"\n WHERE pg_transform.oid > %u AND "
"\n (p.oid = pg_transform.trffromsql"
"\n OR p.oid = pg_transform.trftosql))",
- acl_subquery->data,
- racl_subquery->data,
- initacl_subquery->data,
- initracl_subquery->data,
username_subquery,
not_agg_check,
g_last_builtin_oid,
appendPQExpBufferStr(query,
"\n OR p.proacl IS DISTINCT FROM pip.initprivs");
appendPQExpBufferChar(query, ')');
-
- destroyPQExpBuffer(acl_subquery);
- destroyPQExpBuffer(racl_subquery);
- destroyPQExpBuffer(initacl_subquery);
- destroyPQExpBuffer(initracl_subquery);
}
else
{
+ const char *acldefault_call;
+
+ acldefault_call = (fout->remoteVersion >= 90200 ?
+ "acldefault('f', proowner)" : "NULL");
+
appendPQExpBuffer(query,
"SELECT tableoid, oid, proname, prolang, "
"pronargs, proargtypes, prorettype, proacl, "
- "NULL as rproacl, "
- "NULL as initproacl, NULL AS initrproacl, "
+ "%s AS acldefault, "
"pronamespace, "
"(%s proowner) AS rolname "
"FROM pg_proc p "
"WHERE NOT proisagg",
+ acldefault_call,
username_subquery);
if (fout->remoteVersion >= 90200)
appendPQExpBufferStr(query,
i_proargtypes = PQfnumber(res, "proargtypes");
i_prorettype = PQfnumber(res, "prorettype");
i_proacl = PQfnumber(res, "proacl");
- i_rproacl = PQfnumber(res, "rproacl");
- i_initproacl = PQfnumber(res, "initproacl");
- i_initrproacl = PQfnumber(res, "initrproacl");
+ i_acldefault = PQfnumber(res, "acldefault");
for (i = 0; i < ntups; i++)
{
finfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_proname));
finfo[i].dobj.namespace =
findNamespace(atooid(PQgetvalue(res, i, i_pronamespace)));
+ finfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_proacl));
+ finfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
+ finfo[i].dacl.privtype = 0;
+ finfo[i].dacl.initprivs = NULL;
finfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
finfo[i].lang = atooid(PQgetvalue(res, i, i_prolang));
finfo[i].prorettype = atooid(PQgetvalue(res, i, i_prorettype));
- finfo[i].proacl = pg_strdup(PQgetvalue(res, i, i_proacl));
- finfo[i].rproacl = pg_strdup(PQgetvalue(res, i, i_rproacl));
- finfo[i].initproacl = pg_strdup(PQgetvalue(res, i, i_initproacl));
- finfo[i].initrproacl = pg_strdup(PQgetvalue(res, i, i_initrproacl));
finfo[i].nargs = atoi(PQgetvalue(res, i, i_pronargs));
if (finfo[i].nargs == 0)
finfo[i].argtypes = NULL;
selectDumpableObject(&(finfo[i].dobj), fout);
/* Mark whether function has an ACL */
- if (!(PQgetisnull(res, i, i_proacl) &&
- PQgetisnull(res, i, i_rproacl) &&
- PQgetisnull(res, i, i_initproacl) &&
- PQgetisnull(res, i, i_initrproacl)))
+ if (!PQgetisnull(res, i, i_proacl))
finfo[i].dobj.components |= DUMP_COMPONENT_ACL;
if (strlen(finfo[i].rolname) == 0)
int i_amname;
int i_is_identity_sequence;
int i_relacl;
- int i_rrelacl;
- int i_initrelacl;
- int i_initrrelacl;
- int i_changed_acl;
+ int i_acldefault;
int i_partkeydef;
int i_ispartition;
int i_partbound;
appendPQExpBufferStr(query,
"false AS is_identity_sequence, ");
- if (fout->remoteVersion >= 90600)
- {
- PQExpBuffer acl_subquery = createPQExpBuffer();
- PQExpBuffer racl_subquery = createPQExpBuffer();
- PQExpBuffer initacl_subquery = createPQExpBuffer();
- PQExpBuffer initracl_subquery = createPQExpBuffer();
- PQExpBuffer attacl_subquery = createPQExpBuffer();
- PQExpBuffer attracl_subquery = createPQExpBuffer();
- PQExpBuffer attinitacl_subquery = createPQExpBuffer();
- PQExpBuffer attinitracl_subquery = createPQExpBuffer();
-
- buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
- initracl_subquery, "c.relacl", "c.relowner",
- "pip.initprivs",
- "CASE WHEN c.relkind = " CppAsString2(RELKIND_SEQUENCE)
- " THEN 's' ELSE 'r' END::\"char\"",
- dopt->binary_upgrade);
-
- buildACLQueries(attacl_subquery, attracl_subquery, attinitacl_subquery,
- attinitracl_subquery, "at.attacl", "c.relowner",
- "pip.initprivs", "'c'", dopt->binary_upgrade);
-
- appendPQExpBuffer(query,
- "%s AS relacl, %s as rrelacl, "
- "%s AS initrelacl, %s as initrrelacl, ",
- acl_subquery->data,
- racl_subquery->data,
- initacl_subquery->data,
- initracl_subquery->data);
-
- appendPQExpBuffer(query,
- "EXISTS (SELECT 1 FROM pg_attribute at LEFT JOIN pg_init_privs pip ON "
- "(c.oid = pip.objoid "
- "AND pip.classoid = 'pg_class'::regclass "
- "AND pip.objsubid = at.attnum)"
- "WHERE at.attrelid = c.oid AND ("
- "%s IS NOT NULL "
- "OR %s IS NOT NULL "
- "OR %s IS NOT NULL "
- "OR %s IS NOT NULL"
- "))"
- "AS changed_acl, ",
- attacl_subquery->data,
- attracl_subquery->data,
- attinitacl_subquery->data,
- attinitracl_subquery->data);
-
- destroyPQExpBuffer(acl_subquery);
- destroyPQExpBuffer(racl_subquery);
- destroyPQExpBuffer(initacl_subquery);
- destroyPQExpBuffer(initracl_subquery);
- destroyPQExpBuffer(attacl_subquery);
- destroyPQExpBuffer(attracl_subquery);
- destroyPQExpBuffer(attinitacl_subquery);
- destroyPQExpBuffer(attinitracl_subquery);
- }
+ if (fout->remoteVersion >= 90200)
+ appendPQExpBufferStr(query,
+ "c.relacl, "
+ "acldefault(CASE WHEN c.relkind = " CppAsString2(RELKIND_SEQUENCE)
+ " THEN 's'::\"char\" ELSE 'r'::\"char\" END, c.relowner) AS acldefault, ");
else
appendPQExpBufferStr(query,
- "c.relacl, NULL as rrelacl, "
- "NULL AS initrelacl, NULL AS initrrelacl, "
- "false AS changed_acl, ");
+ "c.relacl, NULL AS acldefault, ");
if (fout->remoteVersion >= 100000)
appendPQExpBufferStr(query,
"\nFROM pg_class c\n"
"LEFT JOIN pg_depend d ON "
"(c.relkind = " CppAsString2(RELKIND_SEQUENCE) " AND "
- "d.classid = c.tableoid AND d.objid = c.oid AND "
+ "d.classid = 'pg_class'::regclass AND d.objid = c.oid AND "
"d.objsubid = 0 AND "
- "d.refclassid = c.tableoid AND d.deptype IN ('a', 'i'))\n"
+ "d.refclassid = 'pg_class'::regclass AND d.deptype IN ('a', 'i'))\n"
"LEFT JOIN pg_tablespace tsp ON (tsp.oid = c.reltablespace)\n");
/*
- * In 9.6 and up, left join to pg_init_privs to detect if any privileges
- * are still as-set-at-init, in which case we won't dump out ACL commands
- * for those. We also are interested in the amname as of 9.6.
+ * In 9.6 and up, left join to pg_am to pick up the amname.
*/
if (fout->remoteVersion >= 90600)
appendPQExpBufferStr(query,
- "LEFT JOIN pg_init_privs pip ON "
- "(c.oid = pip.objoid "
- "AND pip.classoid = 'pg_class'::regclass "
- "AND pip.objsubid = 0)\n"
"LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
/*
*/
if (fout->remoteVersion >= 80200)
appendPQExpBufferStr(query,
- "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid AND c.relkind <> " CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n");
+ "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid"
+ " AND tc.relkind = " CppAsString2(RELKIND_TOASTVALUE)
+ " AND c.relkind <> " CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n");
/*
* Restrict to interesting relkinds (in particular, not indexes). Not all
i_amname = PQfnumber(res, "amname");
i_is_identity_sequence = PQfnumber(res, "is_identity_sequence");
i_relacl = PQfnumber(res, "relacl");
- i_rrelacl = PQfnumber(res, "rrelacl");
- i_initrelacl = PQfnumber(res, "initrelacl");
- i_initrrelacl = PQfnumber(res, "initrrelacl");
- i_changed_acl = PQfnumber(res, "changed_acl");
+ i_acldefault = PQfnumber(res, "acldefault");
i_partkeydef = PQfnumber(res, "partkeydef");
i_ispartition = PQfnumber(res, "ispartition");
i_partbound = PQfnumber(res, "partbound");
tblinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_relname));
tblinfo[i].dobj.namespace =
findNamespace(atooid(PQgetvalue(res, i, i_relnamespace)));
+ tblinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_relacl));
+ tblinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
+ tblinfo[i].dacl.privtype = 0;
+ tblinfo[i].dacl.initprivs = NULL;
tblinfo[i].relkind = *(PQgetvalue(res, i, i_relkind));
tblinfo[i].reltype = atooid(PQgetvalue(res, i, i_reltype));
tblinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
else
tblinfo[i].amname = pg_strdup(PQgetvalue(res, i, i_amname));
tblinfo[i].is_identity_sequence = (strcmp(PQgetvalue(res, i, i_is_identity_sequence), "t") == 0);
- tblinfo[i].relacl = pg_strdup(PQgetvalue(res, i, i_relacl));
- tblinfo[i].rrelacl = pg_strdup(PQgetvalue(res, i, i_rrelacl));
- tblinfo[i].initrelacl = pg_strdup(PQgetvalue(res, i, i_initrelacl));
- tblinfo[i].initrrelacl = pg_strdup(PQgetvalue(res, i, i_initrrelacl));
tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef));
tblinfo[i].ispartition = (strcmp(PQgetvalue(res, i, i_ispartition), "t") == 0);
tblinfo[i].partbound = pg_strdup(PQgetvalue(res, i, i_partbound));
/* Tables have data */
tblinfo[i].dobj.components |= DUMP_COMPONENT_DATA;
- /*
- * Mark whether table has an ACL.
- *
- * If the table-level and all column-level ACLs for this table are
- * unchanged, then we don't need to worry about including the ACLs for
- * this table. If any column-level ACLs have been changed, the
- * 'changed_acl' column from the query will indicate that.
- *
- * This can result in a significant performance improvement in cases
- * where we are only looking to dump out the ACL (eg: pg_catalog).
- */
- if (!(PQgetisnull(res, i, i_relacl) &&
- PQgetisnull(res, i, i_rrelacl) &&
- PQgetisnull(res, i, i_initrelacl) &&
- PQgetisnull(res, i, i_initrrelacl) &&
- strcmp(PQgetvalue(res, i, i_changed_acl), "f") == 0))
+ /* Mark whether table has an ACL */
+ if (!PQgetisnull(res, i, i_relacl))
tblinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
+ tblinfo[i].hascolumnACLs = false; /* may get set later */
/*
* Read-lock target tables to make sure they aren't DROPPED or altered
ProcLangInfo *
getProcLangs(Archive *fout, int *numProcLangs)
{
- DumpOptions *dopt = fout->dopt;
PGresult *res;
int ntups;
int i;
int i_laninline;
int i_lanvalidator;
int i_lanacl;
- int i_rlanacl;
- int i_initlanacl;
- int i_initrlanacl;
+ int i_acldefault;
int i_lanowner;
- if (fout->remoteVersion >= 90600)
+ if (fout->remoteVersion >= 90200)
{
- PQExpBuffer acl_subquery = createPQExpBuffer();
- PQExpBuffer racl_subquery = createPQExpBuffer();
- PQExpBuffer initacl_subquery = createPQExpBuffer();
- PQExpBuffer initracl_subquery = createPQExpBuffer();
-
- buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
- initracl_subquery, "l.lanacl", "l.lanowner",
- "pip.initprivs", "'l'", dopt->binary_upgrade);
-
- /* pg_language has a laninline column */
- appendPQExpBuffer(query, "SELECT l.tableoid, l.oid, "
- "l.lanname, l.lanpltrusted, l.lanplcallfoid, "
- "l.laninline, l.lanvalidator, "
- "%s AS lanacl, "
- "%s AS rlanacl, "
- "%s AS initlanacl, "
- "%s AS initrlanacl, "
- "(%s l.lanowner) AS lanowner "
- "FROM pg_language l "
- "LEFT JOIN pg_init_privs pip ON "
- "(l.oid = pip.objoid "
- "AND pip.classoid = 'pg_language'::regclass "
- "AND pip.objsubid = 0) "
- "WHERE l.lanispl "
- "ORDER BY l.oid",
- acl_subquery->data,
- racl_subquery->data,
- initacl_subquery->data,
- initracl_subquery->data,
+ /* acldefault() exists */
+ appendPQExpBuffer(query, "SELECT tableoid, oid, "
+ "lanname, lanpltrusted, lanplcallfoid, "
+ "laninline, lanvalidator, "
+ "lanacl, "
+ "acldefault('l', lanowner) AS acldefault, "
+ "(%s lanowner) AS lanowner "
+ "FROM pg_language "
+ "WHERE lanispl "
+ "ORDER BY oid",
username_subquery);
-
- destroyPQExpBuffer(acl_subquery);
- destroyPQExpBuffer(racl_subquery);
- destroyPQExpBuffer(initacl_subquery);
- destroyPQExpBuffer(initracl_subquery);
}
else if (fout->remoteVersion >= 90000)
{
/* pg_language has a laninline column */
appendPQExpBuffer(query, "SELECT tableoid, oid, "
"lanname, lanpltrusted, lanplcallfoid, "
- "laninline, lanvalidator, lanacl, NULL AS rlanacl, "
- "NULL AS initlanacl, NULL AS initrlanacl, "
+ "laninline, lanvalidator, "
+ "lanacl, NULL AS acldefault, "
"(%s lanowner) AS lanowner "
"FROM pg_language "
"WHERE lanispl "
appendPQExpBuffer(query, "SELECT tableoid, oid, "
"lanname, lanpltrusted, lanplcallfoid, "
"0 AS laninline, lanvalidator, lanacl, "
- "NULL AS rlanacl, "
- "NULL AS initlanacl, NULL AS initrlanacl, "
+ "NULL AS acldefault, "
"(%s lanowner) AS lanowner "
"FROM pg_language "
"WHERE lanispl "
appendPQExpBuffer(query, "SELECT tableoid, oid, "
"lanname, lanpltrusted, lanplcallfoid, "
"0 AS laninline, lanvalidator, lanacl, "
- "NULL AS rlanacl, "
- "NULL AS initlanacl, NULL AS initrlanacl, "
+ "NULL AS acldefault, "
"(%s '10') AS lanowner "
"FROM pg_language "
"WHERE lanispl "
appendPQExpBuffer(query, "SELECT tableoid, oid, "
"lanname, lanpltrusted, lanplcallfoid, "
"0 AS laninline, lanvalidator, lanacl, "
- "NULL AS rlanacl, "
- "NULL AS initlanacl, NULL AS initrlanacl, "
+ "NULL AS acldefault, "
"(%s '1') AS lanowner "
"FROM pg_language "
"WHERE lanispl "
i_laninline = PQfnumber(res, "laninline");
i_lanvalidator = PQfnumber(res, "lanvalidator");
i_lanacl = PQfnumber(res, "lanacl");
- i_rlanacl = PQfnumber(res, "rlanacl");
- i_initlanacl = PQfnumber(res, "initlanacl");
- i_initrlanacl = PQfnumber(res, "initrlanacl");
+ i_acldefault = PQfnumber(res, "acldefault");
i_lanowner = PQfnumber(res, "lanowner");
for (i = 0; i < ntups; i++)
AssignDumpId(&planginfo[i].dobj);
planginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_lanname));
+ planginfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_lanacl));
+ planginfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
+ planginfo[i].dacl.privtype = 0;
+ planginfo[i].dacl.initprivs = NULL;
planginfo[i].lanpltrusted = *(PQgetvalue(res, i, i_lanpltrusted)) == 't';
planginfo[i].lanplcallfoid = atooid(PQgetvalue(res, i, i_lanplcallfoid));
planginfo[i].laninline = atooid(PQgetvalue(res, i, i_laninline));
planginfo[i].lanvalidator = atooid(PQgetvalue(res, i, i_lanvalidator));
- planginfo[i].lanacl = pg_strdup(PQgetvalue(res, i, i_lanacl));
- planginfo[i].rlanacl = pg_strdup(PQgetvalue(res, i, i_rlanacl));
- planginfo[i].initlanacl = pg_strdup(PQgetvalue(res, i, i_initlanacl));
- planginfo[i].initrlanacl = pg_strdup(PQgetvalue(res, i, i_initrlanacl));
planginfo[i].lanowner = pg_strdup(PQgetvalue(res, i, i_lanowner));
/* Decide whether we want to dump it */
selectDumpableProcLang(&(planginfo[i]), fout);
/* Mark whether language has an ACL */
- if (!(PQgetisnull(res, i, i_lanacl) &&
- PQgetisnull(res, i, i_rlanacl) &&
- PQgetisnull(res, i, i_initlanacl) &&
- PQgetisnull(res, i, i_initrlanacl)))
+ if (!PQgetisnull(res, i, i_lanacl))
planginfo[i].dobj.components |= DUMP_COMPONENT_ACL;
}
FdwInfo *
getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers)
{
- DumpOptions *dopt = fout->dopt;
PGresult *res;
int ntups;
int i;
int i_fdwhandler;
int i_fdwvalidator;
int i_fdwacl;
- int i_rfdwacl;
- int i_initfdwacl;
- int i_initrfdwacl;
+ int i_acldefault;
int i_fdwoptions;
/* Before 8.4, there are no foreign-data wrappers */
query = createPQExpBuffer();
- if (fout->remoteVersion >= 90600)
+ if (fout->remoteVersion >= 90200)
{
- PQExpBuffer acl_subquery = createPQExpBuffer();
- PQExpBuffer racl_subquery = createPQExpBuffer();
- PQExpBuffer initacl_subquery = createPQExpBuffer();
- PQExpBuffer initracl_subquery = createPQExpBuffer();
-
- buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
- initracl_subquery, "f.fdwacl", "f.fdwowner",
- "pip.initprivs", "'F'", dopt->binary_upgrade);
-
- appendPQExpBuffer(query, "SELECT f.tableoid, f.oid, f.fdwname, "
- "(%s f.fdwowner) AS rolname, "
- "f.fdwhandler::pg_catalog.regproc, "
- "f.fdwvalidator::pg_catalog.regproc, "
- "%s AS fdwacl, "
- "%s AS rfdwacl, "
- "%s AS initfdwacl, "
- "%s AS initrfdwacl, "
+ appendPQExpBuffer(query, "SELECT tableoid, oid, fdwname, "
+ "(%s fdwowner) AS rolname, "
+ "fdwhandler::pg_catalog.regproc, "
+ "fdwvalidator::pg_catalog.regproc, "
+ "fdwacl, "
+ "acldefault('F', fdwowner) AS acldefault, "
"array_to_string(ARRAY("
"SELECT quote_ident(option_name) || ' ' || "
"quote_literal(option_value) "
- "FROM pg_options_to_table(f.fdwoptions) "
+ "FROM pg_options_to_table(fdwoptions) "
"ORDER BY option_name"
"), E',\n ') AS fdwoptions "
- "FROM pg_foreign_data_wrapper f "
- "LEFT JOIN pg_init_privs pip ON "
- "(f.oid = pip.objoid "
- "AND pip.classoid = 'pg_foreign_data_wrapper'::regclass "
- "AND pip.objsubid = 0) ",
- username_subquery,
- acl_subquery->data,
- racl_subquery->data,
- initacl_subquery->data,
- initracl_subquery->data);
-
- destroyPQExpBuffer(acl_subquery);
- destroyPQExpBuffer(racl_subquery);
- destroyPQExpBuffer(initacl_subquery);
- destroyPQExpBuffer(initracl_subquery);
+ "FROM pg_foreign_data_wrapper",
+ username_subquery);
}
else if (fout->remoteVersion >= 90100)
{
"(%s fdwowner) AS rolname, "
"fdwhandler::pg_catalog.regproc, "
"fdwvalidator::pg_catalog.regproc, fdwacl, "
- "NULL as rfdwacl, "
- "NULL as initfdwacl, NULL AS initrfdwacl, "
+ "NULL AS acldefault, "
"array_to_string(ARRAY("
"SELECT quote_ident(option_name) || ' ' || "
"quote_literal(option_value) "
"(%s fdwowner) AS rolname, "
"'-' AS fdwhandler, "
"fdwvalidator::pg_catalog.regproc, fdwacl, "
- "NULL as rfdwacl, "
- "NULL as initfdwacl, NULL AS initrfdwacl, "
+ "NULL AS acldefault, "
"array_to_string(ARRAY("
"SELECT quote_ident(option_name) || ' ' || "
"quote_literal(option_value) "
i_fdwhandler = PQfnumber(res, "fdwhandler");
i_fdwvalidator = PQfnumber(res, "fdwvalidator");
i_fdwacl = PQfnumber(res, "fdwacl");
- i_rfdwacl = PQfnumber(res, "rfdwacl");
- i_initfdwacl = PQfnumber(res, "initfdwacl");
- i_initrfdwacl = PQfnumber(res, "initrfdwacl");
+ i_acldefault = PQfnumber(res, "acldefault");
i_fdwoptions = PQfnumber(res, "fdwoptions");
for (i = 0; i < ntups; i++)
AssignDumpId(&fdwinfo[i].dobj);
fdwinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_fdwname));
fdwinfo[i].dobj.namespace = NULL;
+ fdwinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_fdwacl));
+ fdwinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
+ fdwinfo[i].dacl.privtype = 0;
+ fdwinfo[i].dacl.initprivs = NULL;
fdwinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
fdwinfo[i].fdwhandler = pg_strdup(PQgetvalue(res, i, i_fdwhandler));
fdwinfo[i].fdwvalidator = pg_strdup(PQgetvalue(res, i, i_fdwvalidator));
fdwinfo[i].fdwoptions = pg_strdup(PQgetvalue(res, i, i_fdwoptions));
- fdwinfo[i].fdwacl = pg_strdup(PQgetvalue(res, i, i_fdwacl));
- fdwinfo[i].rfdwacl = pg_strdup(PQgetvalue(res, i, i_rfdwacl));
- fdwinfo[i].initfdwacl = pg_strdup(PQgetvalue(res, i, i_initfdwacl));
- fdwinfo[i].initrfdwacl = pg_strdup(PQgetvalue(res, i, i_initrfdwacl));
/* Decide whether we want to dump it */
selectDumpableObject(&(fdwinfo[i].dobj), fout);
/* Mark whether FDW has an ACL */
- if (!(PQgetisnull(res, i, i_fdwacl) &&
- PQgetisnull(res, i, i_rfdwacl) &&
- PQgetisnull(res, i, i_initfdwacl) &&
- PQgetisnull(res, i, i_initrfdwacl)))
+ if (!PQgetisnull(res, i, i_fdwacl))
fdwinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
}
ForeignServerInfo *
getForeignServers(Archive *fout, int *numForeignServers)
{
- DumpOptions *dopt = fout->dopt;
PGresult *res;
int ntups;
int i;
int i_srvtype;
int i_srvversion;
int i_srvacl;
- int i_rsrvacl;
- int i_initsrvacl;
- int i_initrsrvacl;
+ int i_acldefault;
int i_srvoptions;
/* Before 8.4, there are no foreign servers */
query = createPQExpBuffer();
- if (fout->remoteVersion >= 90600)
+ if (fout->remoteVersion >= 90200)
{
- PQExpBuffer acl_subquery = createPQExpBuffer();
- PQExpBuffer racl_subquery = createPQExpBuffer();
- PQExpBuffer initacl_subquery = createPQExpBuffer();
- PQExpBuffer initracl_subquery = createPQExpBuffer();
-
- buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
- initracl_subquery, "f.srvacl", "f.srvowner",
- "pip.initprivs", "'S'", dopt->binary_upgrade);
-
- appendPQExpBuffer(query, "SELECT f.tableoid, f.oid, f.srvname, "
- "(%s f.srvowner) AS rolname, "
- "f.srvfdw, f.srvtype, f.srvversion, "
- "%s AS srvacl, "
- "%s AS rsrvacl, "
- "%s AS initsrvacl, "
- "%s AS initrsrvacl, "
+ appendPQExpBuffer(query, "SELECT tableoid, oid, srvname, "
+ "(%s srvowner) AS rolname, "
+ "srvfdw, srvtype, srvversion, srvacl, "
+ "acldefault('S', srvowner) AS acldefault, "
"array_to_string(ARRAY("
"SELECT quote_ident(option_name) || ' ' || "
"quote_literal(option_value) "
- "FROM pg_options_to_table(f.srvoptions) "
+ "FROM pg_options_to_table(srvoptions) "
"ORDER BY option_name"
"), E',\n ') AS srvoptions "
- "FROM pg_foreign_server f "
- "LEFT JOIN pg_init_privs pip "
- "ON (f.oid = pip.objoid "
- "AND pip.classoid = 'pg_foreign_server'::regclass "
- "AND pip.objsubid = 0) ",
- username_subquery,
- acl_subquery->data,
- racl_subquery->data,
- initacl_subquery->data,
- initracl_subquery->data);
-
- destroyPQExpBuffer(acl_subquery);
- destroyPQExpBuffer(racl_subquery);
- destroyPQExpBuffer(initacl_subquery);
- destroyPQExpBuffer(initracl_subquery);
+ "FROM pg_foreign_server",
+ username_subquery);
}
else
{
appendPQExpBuffer(query, "SELECT tableoid, oid, srvname, "
"(%s srvowner) AS rolname, "
"srvfdw, srvtype, srvversion, srvacl, "
- "NULL AS rsrvacl, "
- "NULL AS initsrvacl, NULL AS initrsrvacl, "
+ "NULL AS acldefault, "
"array_to_string(ARRAY("
"SELECT quote_ident(option_name) || ' ' || "
"quote_literal(option_value) "
i_srvtype = PQfnumber(res, "srvtype");
i_srvversion = PQfnumber(res, "srvversion");
i_srvacl = PQfnumber(res, "srvacl");
- i_rsrvacl = PQfnumber(res, "rsrvacl");
- i_initsrvacl = PQfnumber(res, "initsrvacl");
- i_initrsrvacl = PQfnumber(res, "initrsrvacl");
+ i_acldefault = PQfnumber(res, "acldefault");
i_srvoptions = PQfnumber(res, "srvoptions");
for (i = 0; i < ntups; i++)
AssignDumpId(&srvinfo[i].dobj);
srvinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_srvname));
srvinfo[i].dobj.namespace = NULL;
+ srvinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_srvacl));
+ srvinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
+ srvinfo[i].dacl.privtype = 0;
+ srvinfo[i].dacl.initprivs = NULL;
srvinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
srvinfo[i].srvfdw = atooid(PQgetvalue(res, i, i_srvfdw));
srvinfo[i].srvtype = pg_strdup(PQgetvalue(res, i, i_srvtype));
srvinfo[i].srvversion = pg_strdup(PQgetvalue(res, i, i_srvversion));
srvinfo[i].srvoptions = pg_strdup(PQgetvalue(res, i, i_srvoptions));
- srvinfo[i].srvacl = pg_strdup(PQgetvalue(res, i, i_srvacl));
- srvinfo[i].rsrvacl = pg_strdup(PQgetvalue(res, i, i_rsrvacl));
- srvinfo[i].initsrvacl = pg_strdup(PQgetvalue(res, i, i_initsrvacl));
- srvinfo[i].initrsrvacl = pg_strdup(PQgetvalue(res, i, i_initrsrvacl));
/* Decide whether we want to dump it */
selectDumpableObject(&(srvinfo[i].dobj), fout);
srvinfo[i].dobj.components |= DUMP_COMPONENT_USERMAP;
/* Mark whether server has an ACL */
- if (!(PQgetisnull(res, i, i_srvacl) &&
- PQgetisnull(res, i, i_rsrvacl) &&
- PQgetisnull(res, i, i_initsrvacl) &&
- PQgetisnull(res, i, i_initrsrvacl)))
+ if (!PQgetisnull(res, i, i_srvacl))
srvinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
}
int i_defaclnamespace;
int i_defaclobjtype;
int i_defaclacl;
- int i_rdefaclacl;
- int i_initdefaclacl;
- int i_initrdefaclacl;
+ int i_acldefault;
int i,
ntups;
query = createPQExpBuffer();
- if (fout->remoteVersion >= 90600)
- {
- PQExpBuffer acl_subquery = createPQExpBuffer();
- PQExpBuffer racl_subquery = createPQExpBuffer();
- PQExpBuffer initacl_subquery = createPQExpBuffer();
- PQExpBuffer initracl_subquery = createPQExpBuffer();
+ appendPQExpBuffer(query,
+ "SELECT oid, tableoid, "
+ "(%s defaclrole) AS defaclrole, "
+ "defaclnamespace, "
+ "defaclobjtype, "
+ "defaclacl, ",
+ username_subquery);
+ if (fout->remoteVersion >= 90200)
+ {
/*
* Global entries (with defaclnamespace=0) replace the hard-wired
* default ACL for their object type. We should dump them as deltas
* for interpreting the ALTER DEFAULT PRIVILEGES commands. On the
* other hand, non-global entries can only add privileges not revoke
* them. We must dump those as-is (i.e., as deltas from an empty
- * ACL). We implement that by passing NULL as the object type for
- * acldefault(), which works because acldefault() is STRICT.
+ * ACL).
*
* We can use defaclobjtype as the object type for acldefault(),
* except for the case of 'S' (DEFACLOBJ_SEQUENCE) which must be
* converted to 's'.
*/
- buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
- initracl_subquery, "defaclacl", "defaclrole",
- "pip.initprivs",
- "CASE WHEN defaclnamespace = 0 THEN"
- " CASE WHEN defaclobjtype = 'S' THEN 's'::\"char\""
- " ELSE defaclobjtype END "
- "ELSE NULL END",
- dopt->binary_upgrade);
-
- appendPQExpBuffer(query, "SELECT d.oid, d.tableoid, "
- "(%s d.defaclrole) AS defaclrole, "
- "d.defaclnamespace, "
- "d.defaclobjtype, "
- "%s AS defaclacl, "
- "%s AS rdefaclacl, "
- "%s AS initdefaclacl, "
- "%s AS initrdefaclacl "
- "FROM pg_default_acl d "
- "LEFT JOIN pg_init_privs pip ON "
- "(d.oid = pip.objoid "
- "AND pip.classoid = 'pg_default_acl'::regclass "
- "AND pip.objsubid = 0) ",
- username_subquery,
- acl_subquery->data,
- racl_subquery->data,
- initacl_subquery->data,
- initracl_subquery->data);
-
- destroyPQExpBuffer(acl_subquery);
- destroyPQExpBuffer(racl_subquery);
- destroyPQExpBuffer(initacl_subquery);
- destroyPQExpBuffer(initracl_subquery);
+ appendPQExpBufferStr(query,
+ "CASE WHEN defaclnamespace = 0 THEN "
+ "acldefault(CASE WHEN defaclobjtype = 'S' "
+ "THEN 's'::\"char\" ELSE defaclobjtype END, "
+ "defaclrole) ELSE '{}' END AS acldefault ");
}
else
- {
- appendPQExpBuffer(query, "SELECT oid, tableoid, "
- "(%s defaclrole) AS defaclrole, "
- "defaclnamespace, "
- "defaclobjtype, "
- "defaclacl, "
- "NULL AS rdefaclacl, "
- "NULL AS initdefaclacl, "
- "NULL AS initrdefaclacl "
- "FROM pg_default_acl",
- username_subquery);
- }
+ appendPQExpBufferStr(query,
+ "NULL AS acldefault ");
+
+ appendPQExpBufferStr(query,
+ "FROM pg_default_acl");
res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
i_defaclnamespace = PQfnumber(res, "defaclnamespace");
i_defaclobjtype = PQfnumber(res, "defaclobjtype");
i_defaclacl = PQfnumber(res, "defaclacl");
- i_rdefaclacl = PQfnumber(res, "rdefaclacl");
- i_initdefaclacl = PQfnumber(res, "initdefaclacl");
- i_initrdefaclacl = PQfnumber(res, "initrdefaclacl");
+ i_acldefault = PQfnumber(res, "acldefault");
for (i = 0; i < ntups; i++)
{
else
daclinfo[i].dobj.namespace = NULL;
+ daclinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_defaclacl));
+ daclinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
+ daclinfo[i].dacl.privtype = 0;
+ daclinfo[i].dacl.initprivs = NULL;
daclinfo[i].defaclrole = pg_strdup(PQgetvalue(res, i, i_defaclrole));
daclinfo[i].defaclobjtype = *(PQgetvalue(res, i, i_defaclobjtype));
- daclinfo[i].defaclacl = pg_strdup(PQgetvalue(res, i, i_defaclacl));
- daclinfo[i].rdefaclacl = pg_strdup(PQgetvalue(res, i, i_rdefaclacl));
- daclinfo[i].initdefaclacl = pg_strdup(PQgetvalue(res, i, i_initdefaclacl));
- daclinfo[i].initrdefaclacl = pg_strdup(PQgetvalue(res, i, i_initrdefaclacl));
/* Default ACLs are ACLs, of course */
daclinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
return daclinfo;
}
+/*
+ * getAdditionalACLs
+ *
+ * We have now created all the DumpableObjects, and collected the ACL data
+ * that appears in the directly-associated catalog entries. However, there's
+ * more ACL-related info to collect. If any of a table's columns have ACLs,
+ * we must set the TableInfo's DUMP_COMPONENT_ACL components flag, as well as
+ * its hascolumnACLs flag (we won't store the ACLs themselves here, though).
+ * Also, in versions having the pg_init_privs catalog, read that and load the
+ * information into the relevant DumpableObjects.
+ */
+static void
+getAdditionalACLs(Archive *fout)
+{
+ PQExpBuffer query = createPQExpBuffer();
+ PGresult *res;
+ int ntups,
+ i;
+
+ /* Check for per-column ACLs */
+ if (fout->remoteVersion >= 80400)
+ {
+ appendPQExpBufferStr(query,
+ "SELECT DISTINCT attrelid FROM pg_attribute "
+ "WHERE attacl IS NOT NULL");
+
+ res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+ for (i = 0; i < ntups; i++)
+ {
+ Oid relid = atooid(PQgetvalue(res, i, 0));
+ TableInfo *tblinfo;
+
+ tblinfo = findTableByOid(relid);
+ /* OK to ignore tables we haven't got a DumpableObject for */
+ if (tblinfo)
+ {
+ tblinfo->dobj.components |= DUMP_COMPONENT_ACL;
+ tblinfo->hascolumnACLs = true;
+ }
+ }
+ PQclear(res);
+ }
+
+ /* Fetch initial-privileges data */
+ if (fout->remoteVersion >= 90600)
+ {
+ printfPQExpBuffer(query,
+ "SELECT objoid, classoid, objsubid, privtype, initprivs "
+ "FROM pg_init_privs");
+
+ res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+ for (i = 0; i < ntups; i++)
+ {
+ Oid objoid = atooid(PQgetvalue(res, i, 0));
+ Oid classoid = atooid(PQgetvalue(res, i, 1));
+ int objsubid = atoi(PQgetvalue(res, i, 2));
+ char privtype = *(PQgetvalue(res, i, 3));
+ char *initprivs = PQgetvalue(res, i, 4);
+ CatalogId objId;
+ DumpableObject *dobj;
+
+ objId.tableoid = classoid;
+ objId.oid = objoid;
+ dobj = findObjectByCatalogId(objId);
+ /* OK to ignore entries we haven't got a DumpableObject for */
+ if (dobj)
+ {
+ /* Cope with sub-object initprivs */
+ if (objsubid != 0)
+ {
+ if (dobj->objType == DO_TABLE)
+ {
+ /* For a column initpriv, set the table's ACL flags */
+ dobj->components |= DUMP_COMPONENT_ACL;
+ ((TableInfo *) dobj)->hascolumnACLs = true;
+ }
+ else
+ pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
+ classoid, objoid, objsubid);
+ continue;
+ }
+
+ /*
+ * We ignore any pg_init_privs.initprivs entry for the public
+ * schema, as explained in getNamespaces().
+ */
+ if (dobj->objType == DO_NAMESPACE &&
+ strcmp(dobj->name, "public") == 0)
+ continue;
+
+ /* Else it had better be of a type we think has ACLs */
+ if (dobj->objType == DO_NAMESPACE ||
+ dobj->objType == DO_TYPE ||
+ dobj->objType == DO_FUNC ||
+ dobj->objType == DO_AGG ||
+ dobj->objType == DO_TABLE ||
+ dobj->objType == DO_PROCLANG ||
+ dobj->objType == DO_FDW ||
+ dobj->objType == DO_FOREIGN_SERVER)
+ {
+ DumpableObjectWithAcl *daobj = (DumpableObjectWithAcl *) dobj;
+
+ daobj->dacl.privtype = privtype;
+ daobj->dacl.initprivs = pstrdup(initprivs);
+ }
+ else
+ pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
+ classoid, objoid, objsubid);
+ }
+ }
+ PQclear(res);
+ }
+
+ destroyPQExpBuffer(query);
+}
+
/*
* dumpCommentExtended --
*
if (nspinfo->dobj.dump & DUMP_COMPONENT_ACL)
dumpACL(fout, nspinfo->dobj.dumpId, InvalidDumpId, "SCHEMA",
qnspname, NULL, NULL,
- nspinfo->rolname, nspinfo->nspacl, nspinfo->rnspacl,
- nspinfo->initnspacl, nspinfo->initrnspacl);
+ nspinfo->rolname, &nspinfo->dacl);
free(qnspname);
dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
qtypname, NULL,
tyinfo->dobj.namespace->dobj.name,
- tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl,
- tyinfo->inittypacl, tyinfo->initrtypacl);
+ tyinfo->rolname, &tyinfo->dacl);
PQclear(res);
destroyPQExpBuffer(q);
dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
qtypname, NULL,
tyinfo->dobj.namespace->dobj.name,
- tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl,
- tyinfo->inittypacl, tyinfo->initrtypacl);
+ tyinfo->rolname, &tyinfo->dacl);
PQclear(res);
destroyPQExpBuffer(q);
dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
qtypname, NULL,
tyinfo->dobj.namespace->dobj.name,
- tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl,
- tyinfo->inittypacl, tyinfo->initrtypacl);
+ tyinfo->rolname, &tyinfo->dacl);
destroyPQExpBuffer(q);
destroyPQExpBuffer(delq);
dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
qtypname, NULL,
tyinfo->dobj.namespace->dobj.name,
- tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl,
- tyinfo->inittypacl, tyinfo->initrtypacl);
+ tyinfo->rolname, &tyinfo->dacl);
PQclear(res);
destroyPQExpBuffer(q);
dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
qtypname, NULL,
tyinfo->dobj.namespace->dobj.name,
- tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl,
- tyinfo->inittypacl, tyinfo->initrtypacl);
+ tyinfo->rolname, &tyinfo->dacl);
/* Dump any per-constraint comments */
for (i = 0; i < tyinfo->nDomChecks; i++)
dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
qtypname, NULL,
tyinfo->dobj.namespace->dobj.name,
- tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl,
- tyinfo->inittypacl, tyinfo->initrtypacl);
+ tyinfo->rolname, &tyinfo->dacl);
PQclear(res);
destroyPQExpBuffer(q);
if (plang->lanpltrusted && plang->dobj.dump & DUMP_COMPONENT_ACL)
dumpACL(fout, plang->dobj.dumpId, InvalidDumpId, "LANGUAGE",
qlanname, NULL, NULL,
- plang->lanowner, plang->lanacl, plang->rlanacl,
- plang->initlanacl, plang->initrlanacl);
+ plang->lanowner, &plang->dacl);
free(qlanname);
dumpACL(fout, finfo->dobj.dumpId, InvalidDumpId, keyword,
funcsig, NULL,
finfo->dobj.namespace->dobj.name,
- finfo->rolname, finfo->proacl, finfo->rproacl,
- finfo->initproacl, finfo->initrproacl);
+ finfo->rolname, &finfo->dacl);
PQclear(res);
dumpACL(fout, agginfo->aggfn.dobj.dumpId, InvalidDumpId,
"FUNCTION", aggsig, NULL,
agginfo->aggfn.dobj.namespace->dobj.name,
- agginfo->aggfn.rolname, agginfo->aggfn.proacl,
- agginfo->aggfn.rproacl,
- agginfo->aggfn.initproacl, agginfo->aggfn.initrproacl);
+ agginfo->aggfn.rolname, &agginfo->aggfn.dacl);
free(aggsig);
if (aggfullsig)
if (fdwinfo->dobj.dump & DUMP_COMPONENT_ACL)
dumpACL(fout, fdwinfo->dobj.dumpId, InvalidDumpId,
"FOREIGN DATA WRAPPER", qfdwname, NULL,
- NULL, fdwinfo->rolname,
- fdwinfo->fdwacl, fdwinfo->rfdwacl,
- fdwinfo->initfdwacl, fdwinfo->initrfdwacl);
+ NULL, fdwinfo->rolname, &fdwinfo->dacl);
free(qfdwname);
if (srvinfo->dobj.dump & DUMP_COMPONENT_ACL)
dumpACL(fout, srvinfo->dobj.dumpId, InvalidDumpId,
"FOREIGN SERVER", qsrvname, NULL,
- NULL, srvinfo->rolname,
- srvinfo->srvacl, srvinfo->rsrvacl,
- srvinfo->initsrvacl, srvinfo->initrsrvacl);
+ NULL, srvinfo->rolname, &srvinfo->dacl);
/* Dump user mappings */
if (srvinfo->dobj.dump & DUMP_COMPONENT_USERMAP)
if (!buildDefaultACLCommands(type,
daclinfo->dobj.namespace != NULL ?
daclinfo->dobj.namespace->dobj.name : NULL,
- daclinfo->defaclacl,
- daclinfo->rdefaclacl,
- daclinfo->initdefaclacl,
- daclinfo->initrdefaclacl,
+ daclinfo->dacl.acl,
+ daclinfo->dacl.acldefault,
daclinfo->defaclrole,
fout->remoteVersion,
q))
fatal("could not parse default ACL list (%s)",
- daclinfo->defaclacl);
+ daclinfo->dacl.acl);
if (daclinfo->dobj.dump & DUMP_COMPONENT_ACL)
ArchiveEntry(fout, daclinfo->dobj.catId, daclinfo->dobj.dumpId,
* (Currently we assume that subname is only provided for table columns.)
* 'nspname' is the namespace the object is in (NULL if none).
* 'owner' is the owner, NULL if there is no owner (for languages).
- * 'acls' contains the ACL string of the object from the appropriate system
- * catalog field; it will be passed to buildACLCommands for building the
- * appropriate GRANT commands.
- * 'racls' contains the ACL string of any initial-but-now-revoked ACLs of the
- * object; it will be passed to buildACLCommands for building the
- * appropriate REVOKE commands.
- * 'initacls' In binary-upgrade mode, ACL string of the object's initial
- * privileges, to be recorded into pg_init_privs
- * 'initracls' In binary-upgrade mode, ACL string of the object's
- * revoked-from-default privileges, to be recorded into pg_init_privs
- *
- * NB: initacls/initracls are needed because extensions can set privileges on
- * an object during the extension's script file and we record those into
- * pg_init_privs as that object's initial privileges.
+ * 'dacl' is the DumpableAcl struct fpr the object.
*
* Returns the dump ID assigned to the ACL TocEntry, or InvalidDumpId if
* no ACL entry was created.
dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
const char *type, const char *name, const char *subname,
const char *nspname, const char *owner,
- const char *acls, const char *racls,
- const char *initacls, const char *initracls)
+ const DumpableAcl *dacl)
{
DumpId aclDumpId = InvalidDumpId;
DumpOptions *dopt = fout->dopt;
+ const char *acls = dacl->acl;
+ const char *acldefault = dacl->acldefault;
+ char privtype = dacl->privtype;
+ const char *initprivs = dacl->initprivs;
+ const char *baseacls;
PQExpBuffer sql;
/* Do nothing if ACL dump is not enabled */
sql = createPQExpBuffer();
/*
- * Check to see if this object has had any initial ACLs included for it.
- * If so, we are in binary upgrade mode and these are the ACLs to turn
- * into GRANT and REVOKE statements to set and record the initial
- * privileges for an extension object. Let the backend know that these
- * are to be recorded by calling binary_upgrade_set_record_init_privs()
- * before and after.
+ * In binary upgrade mode, we don't run an extension's script but instead
+ * dump out the objects independently and then recreate them. To preserve
+ * any initial privileges which were set on extension objects, we need to
+ * compute the set of GRANT and REVOKE commands necessary to get from the
+ * default privileges of an object to its initial privileges as recorded
+ * in pg_init_privs.
+ *
+ * At restore time, we apply these commands after having called
+ * binary_upgrade_set_record_init_privs(true). That tells the backend to
+ * copy the results into pg_init_privs. This is how we preserve the
+ * contents of that catalog across binary upgrades.
*/
- if (strlen(initacls) != 0 || strlen(initracls) != 0)
+ if (dopt->binary_upgrade && privtype == 'e' &&
+ initprivs && *initprivs != '\0')
{
appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\n");
if (!buildACLCommands(name, subname, nspname, type,
- initacls, initracls, owner,
+ initprivs, acldefault, owner,
"", fout->remoteVersion, sql))
- fatal("could not parse initial GRANT ACL list (%s) or initial REVOKE ACL list (%s) for object \"%s\" (%s)",
- initacls, initracls, name, type);
+ fatal("could not parse initial ACL list (%s) or default (%s) for object \"%s\" (%s)",
+ initprivs, acldefault, name, type);
appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\n");
}
+ /*
+ * Now figure the GRANT and REVOKE commands needed to get to the object's
+ * actual current ACL, starting from the initprivs if given, else from the
+ * object-type-specific default. Also, while buildACLCommands will assume
+ * that a NULL/empty acls string means it needn't do anything, what that
+ * actually represents is the object-type-specific default; so we need to
+ * substitute the acldefault string to get the right results in that case.
+ */
+ if (initprivs && *initprivs != '\0')
+ {
+ baseacls = initprivs;
+ if (acls == NULL || *acls == '\0')
+ acls = acldefault;
+ }
+ else
+ baseacls = acldefault;
+
if (!buildACLCommands(name, subname, nspname, type,
- acls, racls, owner,
+ acls, baseacls, owner,
"", fout->remoteVersion, sql))
- fatal("could not parse GRANT ACL list (%s) or REVOKE ACL list (%s) for object \"%s\" (%s)",
- acls, racls, name, type);
+ fatal("could not parse ACL list (%s) or default (%s) for object \"%s\" (%s)",
+ acls, baseacls, name, type);
if (sql->len > 0)
{
dumpACL(fout, tbinfo->dobj.dumpId, InvalidDumpId,
objtype, namecopy, NULL,
tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
- tbinfo->relacl, tbinfo->rrelacl,
- tbinfo->initrelacl, tbinfo->initrrelacl);
+ &tbinfo->dacl);
}
/*
* miss ACLs on system columns. Doing it this way also allows us to dump
* ACLs for catalogs that we didn't mark "interesting" back in getTables.
*/
- if (fout->remoteVersion >= 80400 && tbinfo->dobj.dump & DUMP_COMPONENT_ACL)
+ if ((tbinfo->dobj.dump & DUMP_COMPONENT_ACL) && tbinfo->hascolumnACLs)
{
PQExpBuffer query = createPQExpBuffer();
PGresult *res;
if (fout->remoteVersion >= 90600)
{
- PQExpBuffer acl_subquery = createPQExpBuffer();
- PQExpBuffer racl_subquery = createPQExpBuffer();
- PQExpBuffer initacl_subquery = createPQExpBuffer();
- PQExpBuffer initracl_subquery = createPQExpBuffer();
-
- buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
- initracl_subquery, "at.attacl", "c.relowner",
- "pip.initprivs", "'c'", dopt->binary_upgrade);
-
+ /*
+ * In principle we should call acldefault('c', relowner) to get
+ * the default ACL for a column. However, we don't currently
+ * store the numeric OID of the relowner in TableInfo. We could
+ * convert the owner name using regrole, but that creates a risk
+ * of failure due to concurrent role renames. Given that the
+ * default ACL for columns is empty and is likely to stay that
+ * way, it's not worth extra cycles and risk to avoid hard-wiring
+ * that knowledge here.
+ */
appendPQExpBuffer(query,
"SELECT at.attname, "
- "%s AS attacl, "
- "%s AS rattacl, "
- "%s AS initattacl, "
- "%s AS initrattacl "
+ "at.attacl, "
+ "'{}' AS acldefault, "
+ "pip.privtype, pip.initprivs "
"FROM pg_catalog.pg_attribute at "
- "JOIN pg_catalog.pg_class c ON (at.attrelid = c.oid) "
"LEFT JOIN pg_catalog.pg_init_privs pip ON "
"(at.attrelid = pip.objoid "
"AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
"AND at.attnum = pip.objsubid) "
"WHERE at.attrelid = '%u'::pg_catalog.oid AND "
"NOT at.attisdropped "
- "AND ("
- "%s IS NOT NULL OR "
- "%s IS NOT NULL OR "
- "%s IS NOT NULL OR "
- "%s IS NOT NULL)"
+ "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
"ORDER BY at.attnum",
- acl_subquery->data,
- racl_subquery->data,
- initacl_subquery->data,
- initracl_subquery->data,
- tbinfo->dobj.catId.oid,
- acl_subquery->data,
- racl_subquery->data,
- initacl_subquery->data,
- initracl_subquery->data);
-
- destroyPQExpBuffer(acl_subquery);
- destroyPQExpBuffer(racl_subquery);
- destroyPQExpBuffer(initacl_subquery);
- destroyPQExpBuffer(initracl_subquery);
+ tbinfo->dobj.catId.oid);
}
else
{
appendPQExpBuffer(query,
- "SELECT attname, attacl, NULL as rattacl, "
- "NULL AS initattacl, NULL AS initrattacl "
+ "SELECT attname, attacl, '{}' AS acldefault, "
+ "NULL AS privtype, NULL AS initprivs "
"FROM pg_catalog.pg_attribute "
"WHERE attrelid = '%u'::pg_catalog.oid AND NOT attisdropped "
"AND attacl IS NOT NULL "
{
char *attname = PQgetvalue(res, i, 0);
char *attacl = PQgetvalue(res, i, 1);
- char *rattacl = PQgetvalue(res, i, 2);
- char *initattacl = PQgetvalue(res, i, 3);
- char *initrattacl = PQgetvalue(res, i, 4);
+ char *acldefault = PQgetvalue(res, i, 2);
+ char privtype = *(PQgetvalue(res, i, 3));
+ char *initprivs = PQgetvalue(res, i, 4);
+ DumpableAcl coldacl;
char *attnamecopy;
+ coldacl.acl = attacl;
+ coldacl.acldefault = acldefault;
+ coldacl.privtype = privtype;
+ coldacl.initprivs = initprivs;
attnamecopy = pg_strdup(fmtId(attname));
/*
dumpACL(fout, tbinfo->dobj.dumpId, tableAclDumpId,
"TABLE", namecopy, attnamecopy,
tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
- attacl, rattacl, initattacl, initrattacl);
+ &coldacl);
free(attnamecopy);
}
PQclear(res);