In pg_dump, include pg_catalog and extension ACLs, if changed

Now that all of the infrastructure exists, add in the ability to
dump out the ACLs of the objects inside of pg_catalog or the ACLs
for objects which are members of extensions, but only if they have
been changed from their original values.

The original values are tracked in pg_init_privs.  When pg_dump'ing
9.6-and-above databases, we will dump out the ACLs for all objects
in pg_catalog and the ACLs for all extension members, where the ACL
has been changed from the original value which was set during either
initdb or CREATE EXTENSION.

This should not change dumps against pre-9.6 databases.

Reviews by Alexander Korotkov, Jose Luis Tallon
This commit is contained in:
Stephen Frost 2016-04-06 21:45:32 -04:00
parent d217b2c360
commit 23f34fa4ba
15 changed files with 1269 additions and 210 deletions

View File

@ -338,6 +338,27 @@
data; see below.)
</para>
<para>
The extension script may set privileges on objects which are part of the
extension via <command>GRANT</command> and <command>REVOKE</command>
statements. The final set of privileges for each object (if any are set)
will be stored in the
<link linkend="catalog-pg-init-privs"><structname>pg_init_privs</structname></link>
system catalog. When <application>pg_dump</> is used, the
<command>CREATE EXTENSION</> command will be included in the dump, followed
by the set of <command>GRANT</command> and <command>REVOKE</command>
statements necessary to set the privileges on the objects to what they were
at the time the dump was taken.
</para>
<para>
<productname>PostgreSQL</> does not currently support extension scripts
issuing <command>CREATE POLICY</command> or <command>SECURITY LABEL</command>
statements. These are expected to be set after the extension has been
created. All RLS policies and security labels on extension objects will be
included in dumps created by <application>pg_dump</>.
</para>
<para>
The extension mechanism also has provisions for packaging modification
scripts that adjust the definitions of the SQL objects contained in an

View File

@ -22,6 +22,7 @@
#include "access/htup_details.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "catalog/binary_upgrade.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
@ -85,6 +86,12 @@ typedef struct
DropBehavior behavior;
} InternalDefaultACL;
/*
* When performing a binary-upgrade, pg_dump will call a function to set
* this variable to let us know that we need to populate the pg_init_privs
* table for the GRANT/REVOKE commands while this variable is set to true.
*/
bool binary_upgrade_record_init_privs = false;
static void ExecGrantStmt_oids(InternalGrant *istmt);
static void ExecGrant_Relation(InternalGrant *grantStmt);
@ -5237,7 +5244,15 @@ recordExtensionInitPriv(Oid objoid, Oid classoid, int objsubid, Acl *new_acl)
HeapTuple tuple;
HeapTuple oldtuple;
if (!creating_extension)
/*
* Generally, we only record the initial privileges when an extension is
* being created, but because we don't actually use CREATE EXTENSION
* during binary upgrades with pg_upgrade, there is a variable to let us
* know that the GRANT and REVOKE statements being issued, while this
* variable is true, are for the initial privileges of the extension
* object and therefore we need to record them.
*/
if (!creating_extension && !binary_upgrade_record_init_privs)
return;
relation = heap_open(InitPrivsRelationId, RowExclusiveLock);

View File

@ -29,6 +29,7 @@ Datum binary_upgrade_set_next_toast_pg_class_oid(PG_FUNCTION_ARGS);
Datum binary_upgrade_set_next_pg_enum_oid(PG_FUNCTION_ARGS);
Datum binary_upgrade_set_next_pg_authid_oid(PG_FUNCTION_ARGS);
Datum binary_upgrade_create_empty_extension(PG_FUNCTION_ARGS);
Datum binary_upgrade_set_record_init_privs(PG_FUNCTION_ARGS);
#define CHECK_IS_BINARY_UPGRADE \
@ -193,3 +194,14 @@ binary_upgrade_create_empty_extension(PG_FUNCTION_ARGS)
PG_RETURN_VOID();
}
Datum
binary_upgrade_set_record_init_privs(PG_FUNCTION_ARGS)
{
bool record_init_privs = PG_GETARG_BOOL(0);
CHECK_IS_BINARY_UPGRADE;
binary_upgrade_record_init_privs = record_init_privs;
PG_RETURN_VOID();
}

View File

@ -2002,7 +2002,11 @@ setup_privileges(FILE *cmdfd)
char **priv_lines;
static char *privileges_setup[] = {
"UPDATE pg_class "
" SET relacl = E'{\"=r/\\\\\"$POSTGRES_SUPERUSERNAME\\\\\"\"}' "
" SET relacl = (SELECT array_agg(a.acl) FROM "
" (SELECT E'=r/\"$POSTGRES_SUPERUSERNAME\"' as acl "
" UNION SELECT unnest(pg_catalog.acldefault("
" CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid))"
" ) as a) "
" WHERE relkind IN ('r', 'v', 'm', 'S') AND relacl IS NULL;\n\n",
"GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n",
"GRANT CREATE, USAGE ON SCHEMA public TO PUBLIC;\n\n",

View File

@ -38,6 +38,7 @@ static void AddAcl(PQExpBuffer aclbuf, const char *keyword,
* TABLE, SEQUENCE, FUNCTION, 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
* 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
@ -54,13 +55,15 @@ static void AddAcl(PQExpBuffer aclbuf, const char *keyword,
*/
bool
buildACLCommands(const char *name, const char *subname,
const char *type, const char *acls, const char *owner,
const char *prefix, int remoteVersion,
const char *type, const char *acls, const char *racls,
const char *owner, const char *prefix, int remoteVersion,
PQExpBuffer sql)
{
bool ok = true;
char **aclitems;
int naclitems;
char **aclitems = NULL;
char **raclitems = NULL;
int naclitems = 0;
int nraclitems = 0;
int i;
PQExpBuffer grantee,
grantor,
@ -70,18 +73,31 @@ buildACLCommands(const char *name, const char *subname,
secondsql;
bool found_owner_privs = false;
if (strlen(acls) == 0)
if (strlen(acls) == 0 && strlen(racls) == 0)
return true; /* object has default permissions */
/* treat empty-string owner same as NULL */
if (owner && *owner == '\0')
owner = NULL;
if (!parsePGArray(acls, &aclitems, &naclitems))
if (strlen(acls) != 0)
{
if (aclitems)
free(aclitems);
return false;
if (!parsePGArray(acls, &aclitems, &naclitems))
{
if (aclitems)
free(aclitems);
return false;
}
}
if (strlen(racls) != 0)
{
if (!parsePGArray(racls, &raclitems, &nraclitems))
{
if (raclitems)
free(raclitems);
return false;
}
}
grantee = createPQExpBuffer();
@ -90,24 +106,101 @@ buildACLCommands(const char *name, const char *subname,
privswgo = createPQExpBuffer();
/*
* At the end, these two will be pasted together to form the result. But
* the owner privileges need to go before the other ones to keep the
* dependencies valid. In recent versions this is normally the case, but
* in old versions they come after the PUBLIC privileges and that results
* in problems if we need to run REVOKE on the owner privileges.
* 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();
/*
* Always start with REVOKE ALL FROM PUBLIC, so that we don't have to
* wire-in knowledge about the default public privileges for different
* kinds of objects.
* 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'.
*
* 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.
*/
appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
if (subname)
appendPQExpBuffer(firstsql, "(%s)", subname);
appendPQExpBuffer(firstsql, " ON %s %s FROM PUBLIC;\n", type, name);
if (remoteVersion < 90600)
{
Assert(nraclitems == 0);
appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
if (subname)
appendPQExpBuffer(firstsql, "(%s)", subname);
appendPQExpBuffer(firstsql, " ON %s %s FROM PUBLIC;\n", type, name);
}
else
{
/* Scan individual REVOKE ACL items */
for (i = 0; i < nraclitems; i++)
{
if (!parseAclItem(raclitems[i], type, name, subname, remoteVersion,
grantee, grantor, privs, privswgo))
{
ok = false;
break;
}
if (privs->len > 0 || privswgo->len > 0)
{
if (privs->len > 0)
{
appendPQExpBuffer(firstsql, "%sREVOKE %s ON %s %s FROM ",
prefix, privs->data, type, name);
if (grantee->len == 0)
appendPQExpBufferStr(firstsql, "PUBLIC;\n");
else if (strncmp(grantee->data, "group ",
strlen("group ")) == 0)
appendPQExpBuffer(firstsql, "GROUP %s;\n",
fmtId(grantee->data + strlen("group ")));
else
appendPQExpBuffer(firstsql, "%s;\n",
fmtId(grantee->data));
}
if (privswgo->len > 0)
{
appendPQExpBuffer(firstsql,
"%sREVOKE GRANT OPTION FOR %s ON %s %s FROM ",
prefix, privswgo->data, type, name);
if (grantee->len == 0)
appendPQExpBufferStr(firstsql, "PUBLIC");
else if (strncmp(grantee->data, "group ",
strlen("group ")) == 0)
appendPQExpBuffer(firstsql, "GROUP %s",
fmtId(grantee->data + strlen("group ")));
else
appendPQExpBufferStr(firstsql, fmtId(grantee->data));
}
}
}
}
/*
* We still need some hacking though to cover the case where new default
@ -138,7 +231,14 @@ buildACLCommands(const char *name, const char *subname,
if (privs->len > 0 || privswgo->len > 0)
{
if (owner
/*
* 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", respectivly. See above.
*/
if (remoteVersion < 90600 && owner
&& strcmp(grantee->data, owner) == 0
&& strcmp(grantor->data, owner) == 0)
{
@ -172,7 +272,14 @@ buildACLCommands(const char *name, const char *subname,
else
{
/*
* Otherwise can assume we are starting from no privs.
* 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))
@ -215,9 +322,12 @@ buildACLCommands(const char *name, const char *subname,
}
/*
* If we didn't find any owner privs, the owner must have revoked 'em all
* 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 (!found_owner_privs && owner)
if (remoteVersion < 90600 && !found_owner_privs && owner)
{
appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
if (subname)
@ -235,7 +345,11 @@ buildACLCommands(const char *name, const char *subname,
destroyPQExpBuffer(firstsql);
destroyPQExpBuffer(secondsql);
free(aclitems);
if (aclitems)
free(aclitems);
if (raclitems)
free(raclitems);
return ok;
}
@ -275,7 +389,7 @@ buildDefaultACLCommands(const char *type, const char *nspname,
appendPQExpBuffer(prefix, "IN SCHEMA %s ", fmtId(nspname));
result = buildACLCommands("", NULL,
type, acls, owner,
type, acls, "", owner,
prefix->data, remoteVersion,
sql);
@ -555,3 +669,109 @@ emitShSecLabels(PGconn *conn, PGresult *res, PQExpBuffer buffer,
appendPQExpBufferStr(buffer, ";\n");
}
}
/*
* 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 *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).
*/
printfPQExpBuffer(acl_subquery, "(SELECT array_agg(acl) FROM "
"(SELECT unnest(coalesce(%s,acldefault(%s,%s))) AS acl "
"EXCEPT "
"SELECT unnest(coalesce(pip.initprivs,acldefault(%s,%s)))) as foo)",
acl_column,
obj_kind,
acl_owner,
obj_kind,
acl_owner);
printfPQExpBuffer(racl_subquery, "(SELECT array_agg(acl) FROM "
"(SELECT unnest(coalesce(pip.initprivs,acldefault(%s,%s))) AS acl "
"EXCEPT "
"SELECT unnest(coalesce(%s,acldefault(%s,%s)))) as foo)",
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 array_agg(acl) FROM "
"(SELECT unnest(pip.initprivs) AS acl "
"EXCEPT "
"SELECT unnest(acldefault(%s,%s))) as foo) END",
obj_kind,
acl_owner);
printfPQExpBuffer(init_racl_subquery,
"CASE WHEN privtype = 'e' THEN "
"(SELECT array_agg(acl) FROM "
"(SELECT unnest(acldefault(%s,%s)) AS acl "
"EXCEPT "
"SELECT unnest(pip.initprivs)) as foo) END",
obj_kind,
acl_owner);
}
else
{
printfPQExpBuffer(init_acl_subquery, "NULL");
printfPQExpBuffer(init_racl_subquery, "NULL");
}
}

View File

@ -37,8 +37,8 @@
extern bool buildACLCommands(const char *name, const char *subname,
const char *type, const char *acls, const char *owner,
const char *prefix, int remoteVersion,
const char *type, const char *acls, const char *racls,
const char *owner, const char *prefix, int remoteVersion,
PQExpBuffer sql);
extern bool buildDefaultACLCommands(const char *type, const char *nspname,
const char *acls, const char *owner,
@ -49,4 +49,9 @@ extern void buildShSecLabelQuery(PGconn *conn, const char *catalog_name,
extern void emitShSecLabels(PGconn *conn, PGresult *res,
PQExpBuffer buffer, const char *target, const char *objname);
extern void buildACLQueries(PQExpBuffer acl_subquery, PQExpBuffer racl_subquery,
PQExpBuffer init_acl_subquery, PQExpBuffer init_racl_subquery,
const char *acl_column, const char *acl_owner,
const char *obj_kind, bool binary_upgrade);
#endif /* DUMPUTILS_H */

File diff suppressed because it is too large Load Diff

View File

@ -113,6 +113,9 @@ typedef struct _namespaceInfo
DumpableObject dobj;
char *rolname; /* name of owner, or empty string */
char *nspacl;
char *rnspacl;
char *initnspacl;
char *initrnspacl;
} NamespaceInfo;
typedef struct _extensionInfo
@ -135,6 +138,9 @@ typedef struct _typeInfo
*/
char *rolname; /* name of owner, or empty string */
char *typacl;
char *rtypacl;
char *inittypacl;
char *initrtypacl;
Oid typelem;
Oid typrelid;
char typrelkind; /* 'r', 'v', 'c', etc */
@ -164,6 +170,9 @@ typedef struct _funcInfo
Oid *argtypes;
Oid prorettype;
char *proacl;
char *rproacl;
char *initproacl;
char *initrproacl;
} FuncInfo;
/* AggInfo is a superset of FuncInfo */
@ -220,6 +229,9 @@ typedef struct _tableInfo
DumpableObject dobj;
char *rolname; /* name of owner, or empty string */
char *relacl;
char *rrelacl;
char *initrelacl;
char *initrrelacl;
char relkind;
char relpersistence; /* relation persistence */
bool relispopulated; /* relation is populated */
@ -388,6 +400,9 @@ typedef struct _procLangInfo
Oid laninline;
Oid lanvalidator;
char *lanacl;
char *rlanacl;
char *initlanacl;
char *initrlanacl;
char *lanowner; /* name of owner, or empty string */
} ProcLangInfo;
@ -457,6 +472,9 @@ typedef struct _fdwInfo
char *fdwvalidator;
char *fdwoptions;
char *fdwacl;
char *rfdwacl;
char *initfdwacl;
char *initrfdwacl;
} FdwInfo;
typedef struct _foreignServerInfo
@ -467,6 +485,9 @@ typedef struct _foreignServerInfo
char *srvtype;
char *srvversion;
char *srvacl;
char *rsrvacl;
char *initsrvacl;
char *initrsrvacl;
char *srvoptions;
} ForeignServerInfo;
@ -483,6 +504,9 @@ typedef struct _blobInfo
DumpableObject dobj;
char *rolname;
char *blobacl;
char *rblobacl;
char *initblobacl;
char *initrblobacl;
} BlobInfo;
/*

View File

@ -1113,8 +1113,8 @@ dumpTablespaces(PGconn *conn)
fspcname, spcoptions);
if (!skip_acls &&
!buildACLCommands(fspcname, NULL, "TABLESPACE", spcacl, spcowner,
"", server_version, buf))
!buildACLCommands(fspcname, NULL, "TABLESPACE", spcacl, "",
spcowner, "", server_version, buf))
{
fprintf(stderr, _("%s: could not parse ACL list (%s) for tablespace \"%s\"\n"),
progname, spcacl, fspcname);
@ -1444,7 +1444,7 @@ dumpCreateDB(PGconn *conn)
}
if (!skip_acls &&
!buildACLCommands(fdbname, NULL, "DATABASE", dbacl, dbowner,
!buildACLCommands(fdbname, NULL, "DATABASE", dbacl, "", dbowner,
"", server_version, buf))
{
fprintf(stderr, _("%s: could not parse ACL list (%s) for database \"%s\"\n"),

View File

@ -30,4 +30,6 @@ extern PGDLLIMPORT Oid binary_upgrade_next_toast_pg_class_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_pg_enum_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_pg_authid_oid;
extern PGDLLIMPORT bool binary_upgrade_record_init_privs;
#endif /* BINARY_UPGRADE_H */

View File

@ -5245,6 +5245,8 @@ DATA(insert OID = 3590 ( binary_upgrade_set_next_pg_authid_oid PGNSP PGUID 12 1
DESCR("for use by pg_upgrade");
DATA(insert OID = 3591 ( binary_upgrade_create_empty_extension PGNSP PGUID 12 1 0 0 0 f f f f f f v r 7 0 2278 "25 25 16 25 1028 1009 1009" _null_ _null_ _null_ _null_ _null_ binary_upgrade_create_empty_extension _null_ _null_ _null_ ));
DESCR("for use by pg_upgrade");
DATA(insert OID = 4083 ( binary_upgrade_set_record_init_privs PGNSP PGUID 12 1 0 0 0 f f f f t f v r 1 0 2278 "16" _null_ _null_ _null_ _null_ _null_ binary_upgrade_set_record_init_privs _null_ _null_ _null_ ));
DESCR("for use by pg_upgrade");
/* replication/origin.h */
DATA(insert OID = 6003 ( pg_replication_origin_create PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 26 "25" _null_ _null_ _null_ _null_ _null_ pg_replication_origin_create _null_ _null_ _null_ ));

View File

@ -0,0 +1,13 @@
-- Test iniital privileges
-- There should always be some initial privileges, set up by initdb
SELECT count(*) > 0 FROM pg_init_privs;
?column?
----------
t
(1 row)
CREATE ROLE init_privs_test_role1;
CREATE ROLE init_privs_test_role2;
-- Intentionally include some non-initial privs for pg_dump to dump out
GRANT SELECT ON pg_proc TO init_privs_test_role1;
GRANT SELECT (prosrc) ON pg_proc TO init_privs_test_role2;

View File

@ -84,7 +84,7 @@ test: select_into select_distinct select_distinct_on select_implicit select_havi
# ----------
# Another group of parallel tests
# ----------
test: brin gin gist spgist privileges security_label collate matview lock replica_identity rowsecurity object_address tablesample groupingsets drop_operator
test: brin gin gist spgist privileges init_privs security_label collate matview lock replica_identity rowsecurity object_address tablesample groupingsets drop_operator
# ----------
# Another group of parallel tests

View File

@ -105,6 +105,7 @@ test: gin
test: gist
test: spgist
test: privileges
test: init_privs
test: security_label
test: collate
test: matview

View File

@ -0,0 +1,11 @@
-- Test iniital privileges
-- There should always be some initial privileges, set up by initdb
SELECT count(*) > 0 FROM pg_init_privs;
CREATE ROLE init_privs_test_role1;
CREATE ROLE init_privs_test_role2;
-- Intentionally include some non-initial privs for pg_dump to dump out
GRANT SELECT ON pg_proc TO init_privs_test_role1;
GRANT SELECT (prosrc) ON pg_proc TO init_privs_test_role2;