aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/catalog/Makefile4
-rw-r--r--src/backend/catalog/aclchk.c357
-rw-r--r--src/backend/catalog/catalog.c36
-rw-r--r--src/backend/catalog/dependency.c6
-rw-r--r--src/backend/catalog/objectaddress.c71
-rw-r--r--src/backend/catalog/pg_parameter_acl.c118
-rw-r--r--src/backend/commands/alter.c1
-rw-r--r--src/backend/commands/event_trigger.c5
-rw-r--r--src/backend/commands/seclabel.c1
-rw-r--r--src/backend/commands/tablecmds.c1
-rw-r--r--src/backend/parser/gram.y46
-rw-r--r--src/backend/utils/adt/acl.c114
-rw-r--r--src/backend/utils/cache/syscache.c23
-rw-r--r--src/backend/utils/misc/guc.c146
-rw-r--r--src/bin/pg_dump/dumputils.c7
-rw-r--r--src/bin/pg_dump/pg_dumpall.c65
-rw-r--r--src/bin/psql/tab-complete.c78
-rw-r--r--src/include/catalog/catversion.h2
-rw-r--r--src/include/catalog/dependency.h1
-rw-r--r--src/include/catalog/objectaccess.h2
-rw-r--r--src/include/catalog/pg_parameter_acl.h62
-rw-r--r--src/include/catalog/pg_proc.dat16
-rw-r--r--src/include/nodes/parsenodes.h5
-rw-r--r--src/include/parser/kwlist.h1
-rw-r--r--src/include/utils/acl.h11
-rw-r--r--src/include/utils/guc.h2
-rw-r--r--src/include/utils/syscache.h2
-rw-r--r--src/test/modules/test_oat_hooks/expected/test_oat_hooks.out215
-rw-r--r--src/test/modules/test_oat_hooks/sql/test_oat_hooks.sql49
-rw-r--r--src/test/modules/test_oat_hooks/test_oat_hooks.c80
-rw-r--r--src/test/modules/test_pg_dump/t/001_base.pl32
-rw-r--r--src/test/modules/unsafe_tests/Makefile2
-rw-r--r--src/test/modules/unsafe_tests/expected/guc_privs.out526
-rw-r--r--src/test/modules/unsafe_tests/sql/guc_privs.sql237
34 files changed, 2196 insertions, 128 deletions
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 87d7386e013..89a0221ec9b 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -38,6 +38,7 @@ OBJS = \
pg_largeobject.o \
pg_namespace.o \
pg_operator.o \
+ pg_parameter_acl.o \
pg_proc.o \
pg_publication.o \
pg_range.o \
@@ -68,7 +69,8 @@ CATALOG_HEADERS := \
pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
pg_foreign_table.h pg_policy.h pg_replication_origin.h \
pg_default_acl.h pg_init_privs.h pg_seclabel.h pg_shseclabel.h \
- pg_collation.h pg_partitioned_table.h pg_range.h pg_transform.h \
+ pg_collation.h pg_parameter_acl.h pg_partitioned_table.h \
+ pg_range.h pg_transform.h \
pg_sequence.h pg_publication.h pg_publication_namespace.h \
pg_publication_rel.h pg_subscription.h pg_subscription_rel.h
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 1dd03a8e516..5f1726c0957 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -48,6 +48,7 @@
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_opfamily.h"
+#include "catalog/pg_parameter_acl.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_subscription.h"
@@ -112,11 +113,13 @@ static void ExecGrant_Largeobject(InternalGrant *grantStmt);
static void ExecGrant_Namespace(InternalGrant *grantStmt);
static void ExecGrant_Tablespace(InternalGrant *grantStmt);
static void ExecGrant_Type(InternalGrant *grantStmt);
+static void ExecGrant_Parameter(InternalGrant *grantStmt);
static void SetDefaultACLsInSchemas(InternalDefaultACL *iacls, List *nspnames);
static void SetDefaultACL(InternalDefaultACL *iacls);
-static List *objectNamesToOids(ObjectType objtype, List *objnames);
+static List *objectNamesToOids(ObjectType objtype, List *objnames,
+ bool is_grant);
static List *objectsInSchemaToOids(ObjectType objtype, List *nspnames);
static List *getRelationsInNamespace(Oid namespaceId, char relkind);
static void expand_col_privileges(List *colnames, Oid table_oid,
@@ -259,6 +262,9 @@ restrict_and_check_grant(bool is_grant, AclMode avail_goptions, bool all_privs,
case OBJECT_TYPE:
whole_mask = ACL_ALL_RIGHTS_TYPE;
break;
+ case OBJECT_PARAMETER_ACL:
+ whole_mask = ACL_ALL_RIGHTS_PARAMETER_ACL;
+ break;
default:
elog(ERROR, "unrecognized object type: %d", objtype);
/* not reached, but keep compiler quiet */
@@ -390,7 +396,8 @@ ExecuteGrantStmt(GrantStmt *stmt)
switch (stmt->targtype)
{
case ACL_TARGET_OBJECT:
- istmt.objects = objectNamesToOids(stmt->objtype, stmt->objects);
+ istmt.objects = objectNamesToOids(stmt->objtype, stmt->objects,
+ stmt->is_grant);
break;
case ACL_TARGET_ALL_IN_SCHEMA:
istmt.objects = objectsInSchemaToOids(stmt->objtype, stmt->objects);
@@ -498,6 +505,10 @@ ExecuteGrantStmt(GrantStmt *stmt)
all_privileges = ACL_ALL_RIGHTS_FOREIGN_SERVER;
errormsg = gettext_noop("invalid privilege type %s for foreign server");
break;
+ case OBJECT_PARAMETER_ACL:
+ all_privileges = ACL_ALL_RIGHTS_PARAMETER_ACL;
+ errormsg = gettext_noop("invalid privilege type %s for parameter");
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) stmt->objtype);
@@ -600,6 +611,9 @@ ExecGrantStmt_oids(InternalGrant *istmt)
case OBJECT_TABLESPACE:
ExecGrant_Tablespace(istmt);
break;
+ case OBJECT_PARAMETER_ACL:
+ ExecGrant_Parameter(istmt);
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) istmt->objtype);
@@ -626,7 +640,7 @@ ExecGrantStmt_oids(InternalGrant *istmt)
* to fail.
*/
static List *
-objectNamesToOids(ObjectType objtype, List *objnames)
+objectNamesToOids(ObjectType objtype, List *objnames, bool is_grant)
{
List *objects = NIL;
ListCell *cell;
@@ -759,6 +773,37 @@ objectNamesToOids(ObjectType objtype, List *objnames)
objects = lappend_oid(objects, srvid);
}
break;
+ case OBJECT_PARAMETER_ACL:
+ foreach(cell, objnames)
+ {
+ /*
+ * In this code we represent a GUC by the OID of its entry in
+ * pg_parameter_acl, which we have to manufacture here if it
+ * doesn't exist yet. (That's a hack for sure, but it avoids
+ * messing with all the GRANT/REVOKE infrastructure that
+ * expects to use OIDs for object identities.) However, if
+ * this is a REVOKE, we can instead just ignore any GUCs that
+ * don't have such an entry, as they must not have any
+ * privileges needing removal.
+ */
+ char *parameter = strVal(lfirst(cell));
+ Oid parameterId = ParameterAclLookup(parameter, true);
+
+ if (!OidIsValid(parameterId) && is_grant)
+ {
+ parameterId = ParameterAclCreate(parameter);
+
+ /*
+ * Prevent error when processing duplicate objects, and
+ * make this new entry visible so that ExecGrant_Parameter
+ * can update it.
+ */
+ CommandCounterIncrement();
+ }
+ if (OidIsValid(parameterId))
+ objects = lappend_oid(objects, parameterId);
+ }
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) objtype);
@@ -1494,6 +1539,9 @@ RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid)
case ForeignDataWrapperRelationId:
istmt.objtype = OBJECT_FDW;
break;
+ case ParameterAclRelationId:
+ istmt.objtype = OBJECT_PARAMETER_ACL;
+ break;
default:
elog(ERROR, "unexpected object class %u", classid);
break;
@@ -3225,6 +3273,154 @@ ExecGrant_Type(InternalGrant *istmt)
table_close(relation, RowExclusiveLock);
}
+static void
+ExecGrant_Parameter(InternalGrant *istmt)
+{
+ Relation relation;
+ ListCell *cell;
+
+ if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
+ istmt->privileges = ACL_ALL_RIGHTS_PARAMETER_ACL;
+
+ relation = table_open(ParameterAclRelationId, RowExclusiveLock);
+
+ foreach(cell, istmt->objects)
+ {
+ Oid parameterId = lfirst_oid(cell);
+ Datum nameDatum;
+ const char *parname;
+ Datum aclDatum;
+ bool isNull;
+ AclMode avail_goptions;
+ AclMode this_privileges;
+ Acl *old_acl;
+ Acl *new_acl;
+ Oid grantorId;
+ Oid ownerId;
+ HeapTuple tuple;
+ int noldmembers;
+ int nnewmembers;
+ Oid *oldmembers;
+ Oid *newmembers;
+
+ tuple = SearchSysCache1(PARAMETERACLOID, ObjectIdGetDatum(parameterId));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for parameter ACL %u",
+ parameterId);
+
+ /* We'll need the GUC's name */
+ nameDatum = SysCacheGetAttr(PARAMETERACLOID, tuple,
+ Anum_pg_parameter_acl_parname,
+ &isNull);
+ Assert(!isNull);
+ parname = TextDatumGetCString(nameDatum);
+
+ /* Treat all parameters as belonging to the bootstrap superuser. */
+ ownerId = BOOTSTRAP_SUPERUSERID;
+
+ /*
+ * Get working copy of existing ACL. If there's no ACL, substitute the
+ * proper default.
+ */
+ aclDatum = SysCacheGetAttr(PARAMETERACLOID, tuple,
+ Anum_pg_parameter_acl_paracl,
+ &isNull);
+
+ if (isNull)
+ {
+ old_acl = acldefault(istmt->objtype, ownerId);
+ /* There are no old member roles according to the catalogs */
+ noldmembers = 0;
+ oldmembers = NULL;
+ }
+ else
+ {
+ old_acl = DatumGetAclPCopy(aclDatum);
+ /* Get the roles mentioned in the existing ACL */
+ noldmembers = aclmembers(old_acl, &oldmembers);
+ }
+
+ /* Determine ID to do the grant as, and available grant options */
+ select_best_grantor(GetUserId(), istmt->privileges,
+ old_acl, ownerId,
+ &grantorId, &avail_goptions);
+
+ /*
+ * Restrict the privileges to what we can actually grant, and emit the
+ * standards-mandated warning and error messages.
+ */
+ this_privileges =
+ restrict_and_check_grant(istmt->is_grant, avail_goptions,
+ istmt->all_privs, istmt->privileges,
+ parameterId, grantorId,
+ OBJECT_PARAMETER_ACL,
+ parname,
+ 0, NULL);
+
+ /*
+ * Generate new ACL.
+ */
+ new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
+ istmt->grant_option, istmt->behavior,
+ istmt->grantees, this_privileges,
+ grantorId, ownerId);
+
+ /*
+ * We need the members of both old and new ACLs so we can correct the
+ * shared dependency information.
+ */
+ nnewmembers = aclmembers(new_acl, &newmembers);
+
+ /*
+ * If the new ACL is equal to the default, we don't need the catalog
+ * entry any longer. Delete it rather than updating it, to avoid
+ * leaving a degenerate entry.
+ */
+ if (aclequal(new_acl, acldefault(istmt->objtype, ownerId)))
+ {
+ CatalogTupleDelete(relation, &tuple->t_self);
+ }
+ else
+ {
+ /* finished building new ACL value, now insert it */
+ HeapTuple newtuple;
+ Datum values[Natts_pg_parameter_acl];
+ bool nulls[Natts_pg_parameter_acl];
+ bool replaces[Natts_pg_parameter_acl];
+
+ MemSet(values, 0, sizeof(values));
+ MemSet(nulls, false, sizeof(nulls));
+ MemSet(replaces, false, sizeof(replaces));
+
+ replaces[Anum_pg_parameter_acl_paracl - 1] = true;
+ values[Anum_pg_parameter_acl_paracl - 1] = PointerGetDatum(new_acl);
+
+ newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation),
+ values, nulls, replaces);
+
+ CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);
+ }
+
+ /* Update initial privileges for extensions */
+ recordExtensionInitPriv(parameterId, ParameterAclRelationId, 0,
+ new_acl);
+
+ /* Update the shared dependency ACL info */
+ updateAclDependencies(ParameterAclRelationId, parameterId, 0,
+ ownerId,
+ noldmembers, oldmembers,
+ nnewmembers, newmembers);
+
+ ReleaseSysCache(tuple);
+ pfree(new_acl);
+
+ /* prevent error when processing duplicate objects */
+ CommandCounterIncrement();
+ }
+
+ table_close(relation, RowExclusiveLock);
+}
+
static AclMode
string_to_privilege(const char *privname)
@@ -3255,6 +3451,10 @@ string_to_privilege(const char *privname)
return ACL_CREATE_TEMP;
if (strcmp(privname, "connect") == 0)
return ACL_CONNECT;
+ if (strcmp(privname, "set") == 0)
+ return ACL_SET;
+ if (strcmp(privname, "alter system") == 0)
+ return ACL_ALTER_SYSTEM;
if (strcmp(privname, "rule") == 0)
return 0; /* ignore old RULE privileges */
ereport(ERROR,
@@ -3292,6 +3492,10 @@ privilege_to_string(AclMode privilege)
return "TEMP";
case ACL_CONNECT:
return "CONNECT";
+ case ACL_SET:
+ return "SET";
+ case ACL_ALTER_SYSTEM:
+ return "ALTER SYSTEM";
default:
elog(ERROR, "unrecognized privilege: %d", (int) privilege);
}
@@ -3376,6 +3580,9 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_OPFAMILY:
msg = gettext_noop("permission denied for operator family %s");
break;
+ case OBJECT_PARAMETER_ACL:
+ msg = gettext_noop("permission denied for parameter %s");
+ break;
case OBJECT_POLICY:
msg = gettext_noop("permission denied for policy %s");
break;
@@ -3567,6 +3774,7 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_DEFAULT:
case OBJECT_DEFACL:
case OBJECT_DOMCONSTRAINT:
+ case OBJECT_PARAMETER_ACL:
case OBJECT_PUBLICATION_NAMESPACE:
case OBJECT_PUBLICATION_REL:
case OBJECT_ROLE:
@@ -3653,6 +3861,8 @@ pg_aclmask(ObjectType objtype, Oid table_oid, AttrNumber attnum, Oid roleid,
case OBJECT_LARGEOBJECT:
return pg_largeobject_aclmask_snapshot(table_oid, roleid,
mask, how, NULL);
+ case OBJECT_PARAMETER_ACL:
+ return pg_parameter_acl_aclmask(table_oid, roleid, mask, how);
case OBJECT_SCHEMA:
return pg_namespace_aclmask(table_oid, roleid, mask, how);
case OBJECT_STATISTIC_EXT:
@@ -4001,6 +4211,121 @@ pg_database_aclmask(Oid db_oid, Oid roleid,
}
/*
+ * Exported routine for examining a user's privileges for a configuration
+ * parameter (GUC), identified by GUC name.
+ */
+AclMode
+pg_parameter_aclmask(const char *name, Oid roleid, AclMode mask, AclMaskHow how)
+{
+ AclMode result;
+ char *parname;
+ text *partext;
+ HeapTuple tuple;
+
+ /* Superusers bypass all permission checking. */
+ if (superuser_arg(roleid))
+ return mask;
+
+ /* Convert name to the form it should have in pg_parameter_acl... */
+ parname = convert_GUC_name_for_parameter_acl(name);
+ partext = cstring_to_text(parname);
+
+ /* ... and look it up */
+ tuple = SearchSysCache1(PARAMETERACLNAME, PointerGetDatum(partext));
+
+ if (!HeapTupleIsValid(tuple))
+ {
+ /* If no entry, GUC has no permissions for non-superusers */
+ result = ACL_NO_RIGHTS;
+ }
+ else
+ {
+ Datum aclDatum;
+ bool isNull;
+ Acl *acl;
+
+ aclDatum = SysCacheGetAttr(PARAMETERACLNAME, tuple,
+ Anum_pg_parameter_acl_paracl,
+ &isNull);
+ if (isNull)
+ {
+ /* No ACL, so build default ACL */
+ acl = acldefault(OBJECT_PARAMETER_ACL, BOOTSTRAP_SUPERUSERID);
+ aclDatum = (Datum) 0;
+ }
+ else
+ {
+ /* detoast ACL if necessary */
+ acl = DatumGetAclP(aclDatum);
+ }
+
+ result = aclmask(acl, roleid, BOOTSTRAP_SUPERUSERID, mask, how);
+
+ /* if we have a detoasted copy, free it */
+ if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
+ pfree(acl);
+
+ ReleaseSysCache(tuple);
+ }
+
+ pfree(parname);
+ pfree(partext);
+
+ return result;
+}
+
+/*
+ * Exported routine for examining a user's privileges for a configuration
+ * parameter (GUC), identified by the OID of its pg_parameter_acl entry.
+ */
+AclMode
+pg_parameter_acl_aclmask(Oid acl_oid, Oid roleid, AclMode mask, AclMaskHow how)
+{
+ AclMode result;
+ HeapTuple tuple;
+ Datum aclDatum;
+ bool isNull;
+ Acl *acl;
+
+ /* Superusers bypass all permission checking. */
+ if (superuser_arg(roleid))
+ return mask;
+
+ /* Get the ACL from pg_parameter_acl */
+ tuple = SearchSysCache1(PARAMETERACLOID, ObjectIdGetDatum(acl_oid));
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("parameter ACL with OID %u does not exist",
+ acl_oid)));
+
+ aclDatum = SysCacheGetAttr(PARAMETERACLOID, tuple,
+ Anum_pg_parameter_acl_paracl,
+ &isNull);
+ if (isNull)
+ {
+ /* No ACL, so build default ACL */
+ acl = acldefault(OBJECT_PARAMETER_ACL, BOOTSTRAP_SUPERUSERID);
+ aclDatum = (Datum) 0;
+ }
+ else
+ {
+ /* detoast ACL if necessary */
+ acl = DatumGetAclP(aclDatum);
+ }
+
+ result = aclmask(acl, roleid, BOOTSTRAP_SUPERUSERID, mask, how);
+
+ /* if we have a detoasted copy, free it */
+ if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
+ pfree(acl);
+
+ ReleaseSysCache(tuple);
+
+ return result;
+}
+
+/*
* Exported routine for examining a user's privileges for a function
*/
AclMode
@@ -4714,6 +5039,32 @@ pg_database_aclcheck(Oid db_oid, Oid roleid, AclMode mode)
}
/*
+ * Exported routine for checking a user's access privileges to a configuration
+ * parameter (GUC), identified by GUC name.
+ */
+AclResult
+pg_parameter_aclcheck(const char *name, Oid roleid, AclMode mode)
+{
+ if (pg_parameter_aclmask(name, roleid, mode, ACLMASK_ANY) != 0)
+ return ACLCHECK_OK;
+ else
+ return ACLCHECK_NO_PRIV;
+}
+
+/*
+ * Exported routine for checking a user's access privileges to a configuration
+ * parameter (GUC), identified by the OID of its pg_parameter_acl entry.
+ */
+AclResult
+pg_parameter_acl_aclcheck(Oid acl_oid, Oid roleid, AclMode mode)
+{
+ if (pg_parameter_acl_aclmask(acl_oid, roleid, mode, ACLMASK_ANY) != 0)
+ return ACLCHECK_OK;
+ else
+ return ACLCHECK_NO_PRIV;
+}
+
+/*
* Exported routine for checking a user's access privileges to a function
*/
AclResult
diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c
index dfd5fb669ee..520f77971b3 100644
--- a/src/backend/catalog/catalog.c
+++ b/src/backend/catalog/catalog.c
@@ -33,6 +33,7 @@
#include "catalog/pg_db_role_setting.h"
#include "catalog/pg_largeobject.h"
#include "catalog/pg_namespace.h"
+#include "catalog/pg_parameter_acl.h"
#include "catalog/pg_replication_origin.h"
#include "catalog/pg_shdepend.h"
#include "catalog/pg_shdescription.h"
@@ -247,32 +248,35 @@ IsSharedRelation(Oid relationId)
if (relationId == AuthIdRelationId ||
relationId == AuthMemRelationId ||
relationId == DatabaseRelationId ||
- relationId == SharedDescriptionRelationId ||
- relationId == SharedDependRelationId ||
- relationId == SharedSecLabelRelationId ||
- relationId == TableSpaceRelationId ||
relationId == DbRoleSettingRelationId ||
+ relationId == ParameterAclRelationId ||
relationId == ReplicationOriginRelationId ||
- relationId == SubscriptionRelationId)
+ relationId == SharedDependRelationId ||
+ relationId == SharedDescriptionRelationId ||
+ relationId == SharedSecLabelRelationId ||
+ relationId == SubscriptionRelationId ||
+ relationId == TableSpaceRelationId)
return true;
/* These are their indexes */
- if (relationId == AuthIdRolnameIndexId ||
- relationId == AuthIdOidIndexId ||
- relationId == AuthMemRoleMemIndexId ||
+ if (relationId == AuthIdOidIndexId ||
+ relationId == AuthIdRolnameIndexId ||
relationId == AuthMemMemRoleIndexId ||
+ relationId == AuthMemRoleMemIndexId ||
relationId == DatabaseNameIndexId ||
relationId == DatabaseOidIndexId ||
- relationId == SharedDescriptionObjIndexId ||
- relationId == SharedDependDependerIndexId ||
- relationId == SharedDependReferenceIndexId ||
- relationId == SharedSecLabelObjectIndexId ||
- relationId == TablespaceOidIndexId ||
- relationId == TablespaceNameIndexId ||
relationId == DbRoleSettingDatidRolidIndexId ||
+ relationId == ParameterAclOidIndexId ||
+ relationId == ParameterAclParnameIndexId ||
relationId == ReplicationOriginIdentIndex ||
relationId == ReplicationOriginNameIndex ||
+ relationId == SharedDependDependerIndexId ||
+ relationId == SharedDependReferenceIndexId ||
+ relationId == SharedDescriptionObjIndexId ||
+ relationId == SharedSecLabelObjectIndexId ||
+ relationId == SubscriptionNameIndexId ||
relationId == SubscriptionObjectIndexId ||
- relationId == SubscriptionNameIndexId)
+ relationId == TablespaceNameIndexId ||
+ relationId == TablespaceOidIndexId)
return true;
/* These are their toast tables and toast indexes */
if (relationId == PgAuthidToastTable ||
@@ -281,6 +285,8 @@ IsSharedRelation(Oid relationId)
relationId == PgDatabaseToastIndex ||
relationId == PgDbRoleSettingToastTable ||
relationId == PgDbRoleSettingToastIndex ||
+ relationId == PgParameterAclToastTable ||
+ relationId == PgParameterAclToastIndex ||
relationId == PgReplicationOriginToastTable ||
relationId == PgReplicationOriginToastIndex ||
relationId == PgShdescriptionToastTable ||
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 25fe56d3101..de109233910 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -46,6 +46,7 @@
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_opfamily.h"
+#include "catalog/pg_parameter_acl.h"
#include "catalog/pg_policy.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_publication.h"
@@ -178,6 +179,7 @@ static const Oid object_classes[] = {
DefaultAclRelationId, /* OCLASS_DEFACL */
ExtensionRelationId, /* OCLASS_EXTENSION */
EventTriggerRelationId, /* OCLASS_EVENT_TRIGGER */
+ ParameterAclRelationId, /* OCLASS_PARAMETER_ACL */
PolicyRelationId, /* OCLASS_POLICY */
PublicationNamespaceRelationId, /* OCLASS_PUBLICATION_NAMESPACE */
PublicationRelationId, /* OCLASS_PUBLICATION */
@@ -1507,6 +1509,7 @@ doDeletion(const ObjectAddress *object, int flags)
case OCLASS_DATABASE:
case OCLASS_TBLSPACE:
case OCLASS_SUBSCRIPTION:
+ case OCLASS_PARAMETER_ACL:
elog(ERROR, "global objects cannot be deleted by doDeletion");
break;
@@ -2861,6 +2864,9 @@ getObjectClass(const ObjectAddress *object)
case EventTriggerRelationId:
return OCLASS_EVENT_TRIGGER;
+ case ParameterAclRelationId:
+ return OCLASS_PARAMETER_ACL;
+
case PolicyRelationId:
return OCLASS_POLICY;
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 3fd17ea64f0..31c80f7209f 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -45,6 +45,7 @@
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_opfamily.h"
+#include "catalog/pg_parameter_acl.h"
#include "catalog/pg_policy.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_publication.h"
@@ -818,6 +819,10 @@ static const struct object_type_map
{
"event trigger", OBJECT_EVENT_TRIGGER
},
+ /* OCLASS_PARAMETER_ACL */
+ {
+ "parameter ACL", OBJECT_PARAMETER_ACL
+ },
/* OCLASS_POLICY */
{
"policy", OBJECT_POLICY
@@ -1014,6 +1019,7 @@ get_object_address(ObjectType objtype, Node *object,
case OBJECT_FDW:
case OBJECT_FOREIGN_SERVER:
case OBJECT_EVENT_TRIGGER:
+ case OBJECT_PARAMETER_ACL:
case OBJECT_ACCESS_METHOD:
case OBJECT_PUBLICATION:
case OBJECT_SUBSCRIPTION:
@@ -1315,6 +1321,11 @@ get_object_address_unqualified(ObjectType objtype,
address.objectId = get_event_trigger_oid(name, missing_ok);
address.objectSubId = 0;
break;
+ case OBJECT_PARAMETER_ACL:
+ address.classId = ParameterAclRelationId;
+ address.objectId = ParameterAclLookup(name, missing_ok);
+ address.objectSubId = 0;
+ break;
case OBJECT_PUBLICATION:
address.classId = PublicationRelationId;
address.objectId = get_publication_oid(name, missing_ok);
@@ -2307,6 +2318,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
case OBJECT_FDW:
case OBJECT_FOREIGN_SERVER:
case OBJECT_LANGUAGE:
+ case OBJECT_PARAMETER_ACL:
case OBJECT_PUBLICATION:
case OBJECT_ROLE:
case OBJECT_SCHEMA:
@@ -2597,6 +2609,7 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
case OBJECT_TSPARSER:
case OBJECT_TSTEMPLATE:
case OBJECT_ACCESS_METHOD:
+ case OBJECT_PARAMETER_ACL:
/* We treat these object types as being owned by superusers */
if (!superuser_arg(roleid))
ereport(ERROR,
@@ -3880,6 +3893,32 @@ getObjectDescription(const ObjectAddress *object, bool missing_ok)
break;
}
+ case OCLASS_PARAMETER_ACL:
+ {
+ HeapTuple tup;
+ Datum nameDatum;
+ bool isNull;
+ char *parname;
+
+ tup = SearchSysCache1(PARAMETERACLOID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for parameter ACL %u",
+ object->objectId);
+ break;
+ }
+ nameDatum = SysCacheGetAttr(PARAMETERACLOID, tup,
+ Anum_pg_parameter_acl_parname,
+ &isNull);
+ Assert(!isNull);
+ parname = TextDatumGetCString(nameDatum);
+ appendStringInfo(&buffer, _("parameter %s"), parname);
+ ReleaseSysCache(tup);
+ break;
+ }
+
case OCLASS_POLICY:
{
Relation policy_rel;
@@ -4547,6 +4586,10 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "event trigger");
break;
+ case OCLASS_PARAMETER_ACL:
+ appendStringInfoString(&buffer, "parameter ACL");
+ break;
+
case OCLASS_POLICY:
appendStringInfoString(&buffer, "policy");
break;
@@ -5693,6 +5736,34 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case OCLASS_PARAMETER_ACL:
+ {
+ HeapTuple tup;
+ Datum nameDatum;
+ bool isNull;
+ char *parname;
+
+ tup = SearchSysCache1(PARAMETERACLOID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for parameter ACL %u",
+ object->objectId);
+ break;
+ }
+ nameDatum = SysCacheGetAttr(PARAMETERACLOID, tup,
+ Anum_pg_parameter_acl_parname,
+ &isNull);
+ Assert(!isNull);
+ parname = TextDatumGetCString(nameDatum);
+ appendStringInfoString(&buffer, parname);
+ if (objname)
+ *objname = list_make1(parname);
+ ReleaseSysCache(tup);
+ break;
+ }
+
case OCLASS_POLICY:
{
Relation polDesc;
diff --git a/src/backend/catalog/pg_parameter_acl.c b/src/backend/catalog/pg_parameter_acl.c
new file mode 100644
index 00000000000..2decee909b3
--- /dev/null
+++ b/src/backend/catalog/pg_parameter_acl.c
@@ -0,0 +1,118 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_parameter_acl.c
+ * routines to support manipulation of the pg_parameter_acl relation
+ *
+ * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/catalog/pg_parameter_acl.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/table.h"
+#include "catalog/catalog.h"
+#include "catalog/indexing.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_namespace.h"
+#include "catalog/pg_parameter_acl.h"
+#include "utils/builtins.h"
+#include "utils/pg_locale.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+
+/*
+ * ParameterAclLookup - Given a configuration parameter name,
+ * look up the associated configuration parameter ACL's OID.
+ *
+ * If missing_ok is false, throw an error if ACL entry not found. If
+ * true, just return InvalidOid.
+ */
+Oid
+ParameterAclLookup(const char *parameter, bool missing_ok)
+{
+ Oid oid;
+ char *parname;
+
+ /* Convert name to the form it should have in pg_parameter_acl... */
+ parname = convert_GUC_name_for_parameter_acl(parameter);
+
+ /* ... and look it up */
+ oid = GetSysCacheOid1(PARAMETERACLNAME, Anum_pg_parameter_acl_oid,
+ PointerGetDatum(cstring_to_text(parname)));
+
+ if (!OidIsValid(oid) && !missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("parameter ACL \"%s\" does not exist", parameter)));
+
+ pfree(parname);
+
+ return oid;
+}
+
+/*
+ * ParameterAclCreate
+ *
+ * Add a new tuple to pg_parameter_acl.
+ *
+ * parameter: the parameter name to create an entry for.
+ * Caller should have verified that there's no such entry already.
+ *
+ * Returns the new entry's OID.
+ */
+Oid
+ParameterAclCreate(const char *parameter)
+{
+ Oid parameterId;
+ char *parname;
+ Relation rel;
+ TupleDesc tupDesc;
+ HeapTuple tuple;
+ Datum values[Natts_pg_parameter_acl];
+ bool nulls[Natts_pg_parameter_acl];
+
+ /*
+ * To prevent cluttering pg_parameter_acl with useless entries, insist
+ * that the name be valid.
+ */
+ if (!check_GUC_name_for_parameter_acl(parameter))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_NAME),
+ errmsg("invalid parameter name \"%s\"",
+ parameter)));
+
+ /* Convert name to the form it should have in pg_parameter_acl. */
+ parname = convert_GUC_name_for_parameter_acl(parameter);
+
+ /*
+ * Create and insert a new record containing a null ACL.
+ *
+ * We don't take a strong enough lock to prevent concurrent insertions,
+ * relying instead on the unique index.
+ */
+ rel = table_open(ParameterAclRelationId, RowExclusiveLock);
+ tupDesc = RelationGetDescr(rel);
+ MemSet(values, 0, sizeof(values));
+ MemSet(nulls, false, sizeof(nulls));
+ parameterId = GetNewOidWithIndex(rel,
+ ParameterAclOidIndexId,
+ Anum_pg_parameter_acl_oid);
+ values[Anum_pg_parameter_acl_oid - 1] = ObjectIdGetDatum(parameterId);
+ values[Anum_pg_parameter_acl_parname - 1] =
+ PointerGetDatum(cstring_to_text(parname));
+ nulls[Anum_pg_parameter_acl_paracl - 1] = true;
+ tuple = heap_form_tuple(tupDesc, values, nulls);
+ CatalogTupleInsert(rel, tuple);
+
+ /* Close pg_parameter_acl, but keep lock till commit. */
+ heap_freetuple(tuple);
+ table_close(rel, NoLock);
+
+ return parameterId;
+}
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index 1f64c8aa517..5456b8222ba 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -658,6 +658,7 @@ AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid,
case OCLASS_DEFACL:
case OCLASS_EXTENSION:
case OCLASS_EVENT_TRIGGER:
+ case OCLASS_PARAMETER_ACL:
case OCLASS_POLICY:
case OCLASS_PUBLICATION:
case OCLASS_PUBLICATION_NAMESPACE:
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 3c3fc2515b7..46425278811 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -940,6 +940,7 @@ EventTriggerSupportsObjectType(ObjectType obtype)
case OBJECT_DATABASE:
case OBJECT_TABLESPACE:
case OBJECT_ROLE:
+ case OBJECT_PARAMETER_ACL:
/* no support for global objects */
return false;
case OBJECT_EVENT_TRIGGER:
@@ -1015,6 +1016,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
case OCLASS_DATABASE:
case OCLASS_TBLSPACE:
case OCLASS_ROLE:
+ case OCLASS_PARAMETER_ACL:
/* no support for global objects */
return false;
case OCLASS_EVENT_TRIGGER:
@@ -2042,6 +2044,8 @@ stringify_grant_objtype(ObjectType objtype)
return "LARGE OBJECT";
case OBJECT_SCHEMA:
return "SCHEMA";
+ case OBJECT_PARAMETER_ACL:
+ return "PARAMETER";
case OBJECT_PROCEDURE:
return "PROCEDURE";
case OBJECT_ROUTINE:
@@ -2153,6 +2157,7 @@ stringify_adefprivs_objtype(ObjectType objtype)
case OBJECT_OPCLASS:
case OBJECT_OPERATOR:
case OBJECT_OPFAMILY:
+ case OBJECT_PARAMETER_ACL:
case OBJECT_POLICY:
case OBJECT_PUBLICATION:
case OBJECT_PUBLICATION_NAMESPACE:
diff --git a/src/backend/commands/seclabel.c b/src/backend/commands/seclabel.c
index 7a62d547e2f..7ae19b98bb9 100644
--- a/src/backend/commands/seclabel.c
+++ b/src/backend/commands/seclabel.c
@@ -78,6 +78,7 @@ SecLabelSupportsObjectType(ObjectType objtype)
case OBJECT_OPCLASS:
case OBJECT_OPERATOR:
case OBJECT_OPFAMILY:
+ case OBJECT_PARAMETER_ACL:
case OBJECT_POLICY:
case OBJECT_PUBLICATION_NAMESPACE:
case OBJECT_PUBLICATION_REL:
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 7febb5018fc..a241b444975 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -12655,6 +12655,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
case OCLASS_DEFACL:
case OCLASS_EXTENSION:
case OCLASS_EVENT_TRIGGER:
+ case OCLASS_PARAMETER_ACL:
case OCLASS_PUBLICATION:
case OCLASS_PUBLICATION_NAMESPACE:
case OCLASS_PUBLICATION_REL:
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7e3f4a5d275..2cc92a89432 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -371,8 +371,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <str> foreign_server_version opt_foreign_server_version
%type <str> opt_in_database
-%type <str> OptSchemaName
-%type <list> OptSchemaEltList
+%type <str> OptSchemaName parameter_name
+%type <list> OptSchemaEltList parameter_name_list
%type <chr> am_type
@@ -827,7 +827,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
ORDER ORDINALITY OTHERS OUT_P OUTER_P
OVER OVERLAPS OVERLAY OVERRIDING OWNED OWNER
- PARALLEL PARSER PARTIAL PARTITION PASSING PASSWORD PATH PLACING PLAN PLANS POLICY
+ PARALLEL PARAMETER PARSER PARTIAL PARTITION PASSING PASSWORD PATH
+ PLACING PLAN PLANS POLICY
POSITION PRECEDING PRECISION PRESERVE PREPARE PREPARED PRIMARY
PRIOR PRIVILEGES PROCEDURAL PROCEDURE PROCEDURES PROGRAM PUBLICATION
@@ -7197,6 +7198,13 @@ privilege: SELECT opt_column_list
n->cols = $2;
$$ = n;
}
+ | ALTER SYSTEM_P
+ {
+ AccessPriv *n = makeNode(AccessPriv);
+ n->priv_name = pstrdup("alter system");
+ n->cols = NIL;
+ $$ = n;
+ }
| ColId opt_column_list
{
AccessPriv *n = makeNode(AccessPriv);
@@ -7206,6 +7214,28 @@ privilege: SELECT opt_column_list
}
;
+parameter_name_list:
+ parameter_name
+ {
+ $$ = list_make1(makeString($1));
+ }
+ | parameter_name_list ',' parameter_name
+ {
+ $$ = lappend($1, makeString($3));
+ }
+ ;
+
+parameter_name:
+ ColId
+ {
+ $$ = $1;
+ }
+ | parameter_name '.' ColId
+ {
+ $$ = psprintf("%s.%s", $1, $3);
+ }
+ ;
+
/* Don't bother trying to fold the first two rules into one using
* opt_table. You're going to get conflicts.
@@ -7307,6 +7337,14 @@ privilege_target:
n->objs = $3;
$$ = n;
}
+ | PARAMETER parameter_name_list
+ {
+ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
+ n->targtype = ACL_TARGET_OBJECT;
+ n->objtype = OBJECT_PARAMETER_ACL;
+ n->objs = $2;
+ $$ = n;
+ }
| SCHEMA name_list
{
PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
@@ -17065,6 +17103,7 @@ unreserved_keyword:
| OWNED
| OWNER
| PARALLEL
+ | PARAMETER
| PARSER
| PARTIAL
| PARTITION
@@ -17682,6 +17721,7 @@ bare_label_keyword:
| OWNED
| OWNER
| PARALLEL
+ | PARAMETER
| PARSER
| PARTIAL
| PARTITION
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index 83cf7ac9ff9..8bdba1c42a5 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -23,6 +23,7 @@
#include "catalog/pg_authid.h"
#include "catalog/pg_class.h"
#include "catalog/pg_database.h"
+#include "catalog/pg_parameter_acl.h"
#include "catalog/pg_type.h"
#include "commands/dbcommands.h"
#include "commands/proclang.h"
@@ -36,6 +37,7 @@
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/catcache.h"
+#include "utils/guc.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
@@ -109,6 +111,7 @@ static Oid convert_tablespace_name(text *tablespacename);
static AclMode convert_tablespace_priv_string(text *priv_type_text);
static Oid convert_type_name(text *typename);
static AclMode convert_type_priv_string(text *priv_type_text);
+static AclMode convert_parameter_priv_string(text *priv_text);
static AclMode convert_role_priv_string(text *priv_type_text);
static AclResult pg_role_aclcheck(Oid role_oid, Oid roleid, AclMode mode);
@@ -306,6 +309,12 @@ aclparse(const char *s, AclItem *aip)
case ACL_CONNECT_CHR:
read = ACL_CONNECT;
break;
+ case ACL_SET_CHR:
+ read = ACL_SET;
+ break;
+ case ACL_ALTER_SYSTEM_CHR:
+ read = ACL_ALTER_SYSTEM;
+ break;
case 'R': /* ignore old RULE privileges */
read = 0;
break;
@@ -794,6 +803,10 @@ acldefault(ObjectType objtype, Oid ownerId)
world_default = ACL_USAGE;
owner_default = ACL_ALL_RIGHTS_TYPE;
break;
+ case OBJECT_PARAMETER_ACL:
+ world_default = ACL_NO_RIGHTS;
+ owner_default = ACL_ALL_RIGHTS_PARAMETER_ACL;
+ break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
world_default = ACL_NO_RIGHTS; /* keep compiler quiet */
@@ -876,6 +889,9 @@ acldefault_sql(PG_FUNCTION_ARGS)
case 'n':
objtype = OBJECT_SCHEMA;
break;
+ case 'p':
+ objtype = OBJECT_PARAMETER_ACL;
+ break;
case 't':
objtype = OBJECT_TABLESPACE;
break;
@@ -1602,6 +1618,10 @@ convert_priv_string(text *priv_type_text)
return ACL_CREATE_TEMP;
if (pg_strcasecmp(priv_type, "CONNECT") == 0)
return ACL_CONNECT;
+ if (pg_strcasecmp(priv_type, "SET") == 0)
+ return ACL_SET;
+ if (pg_strcasecmp(priv_type, "ALTER SYSTEM") == 0)
+ return ACL_ALTER_SYSTEM;
if (pg_strcasecmp(priv_type, "RULE") == 0)
return 0; /* ignore old RULE privileges */
@@ -1698,6 +1718,10 @@ convert_aclright_to_string(int aclright)
return "TEMPORARY";
case ACL_CONNECT:
return "CONNECT";
+ case ACL_SET:
+ return "SET";
+ case ACL_ALTER_SYSTEM:
+ return "ALTER SYSTEM";
default:
elog(ERROR, "unrecognized aclright: %d", aclright);
return NULL;
@@ -4429,6 +4453,96 @@ convert_type_priv_string(text *priv_type_text)
return convert_any_priv_string(priv_type_text, type_priv_map);
}
+/*
+ * has_parameter_privilege variants
+ * These are all named "has_parameter_privilege" at the SQL level.
+ * They take various combinations of parameter name with
+ * user name, user OID, or implicit user = current_user.
+ *
+ * The result is a boolean value: true if user has been granted
+ * the indicated privilege or false if not.
+ */
+
+/*
+ * has_param_priv_byname
+ *
+ * Helper function to check user privileges on a parameter given the
+ * role by Oid, parameter by text name, and privileges as AclMode.
+ */
+static bool
+has_param_priv_byname(Oid roleid, const text *parameter, AclMode priv)
+{
+ char *paramstr = text_to_cstring(parameter);
+
+ return pg_parameter_aclcheck(paramstr, roleid, priv) == ACLCHECK_OK;
+}
+
+/*
+ * has_parameter_privilege_name_name
+ * Check user privileges on a parameter given name username, text
+ * parameter, and text priv name.
+ */
+Datum
+has_parameter_privilege_name_name(PG_FUNCTION_ARGS)
+{
+ Name username = PG_GETARG_NAME(0);
+ text *parameter = PG_GETARG_TEXT_PP(1);
+ AclMode priv = convert_parameter_priv_string(PG_GETARG_TEXT_PP(2));
+ Oid roleid = get_role_oid_or_public(NameStr(*username));
+
+ PG_RETURN_BOOL(has_param_priv_byname(roleid, parameter, priv));
+}
+
+/*
+ * has_parameter_privilege_name
+ * Check user privileges on a parameter given text parameter and text priv
+ * name. current_user is assumed
+ */
+Datum
+has_parameter_privilege_name(PG_FUNCTION_ARGS)
+{
+ text *parameter = PG_GETARG_TEXT_PP(0);
+ AclMode priv = convert_parameter_priv_string(PG_GETARG_TEXT_PP(1));
+
+ PG_RETURN_BOOL(has_param_priv_byname(GetUserId(), parameter, priv));
+}
+
+/*
+ * has_parameter_privilege_id_name
+ * Check user privileges on a parameter given roleid, text parameter, and
+ * text priv name.
+ */
+Datum
+has_parameter_privilege_id_name(PG_FUNCTION_ARGS)
+{
+ Oid roleid = PG_GETARG_OID(0);
+ text *parameter = PG_GETARG_TEXT_PP(1);
+ AclMode priv = convert_parameter_priv_string(PG_GETARG_TEXT_PP(2));
+
+ PG_RETURN_BOOL(has_param_priv_byname(roleid, parameter, priv));
+}
+
+/*
+ * Support routines for has_parameter_privilege family.
+ */
+
+/*
+ * convert_parameter_priv_string
+ * Convert text string to AclMode value.
+ */
+static AclMode
+convert_parameter_priv_string(text *priv_text)
+{
+ static const priv_map parameter_priv_map[] = {
+ {"SET", ACL_SET},
+ {"SET WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_SET)},
+ {"ALTER SYSTEM", ACL_ALTER_SYSTEM},
+ {"ALTER SYSTEM WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_ALTER_SYSTEM)},
+ {NULL, 0}
+ };
+
+ return convert_any_priv_string(priv_text, parameter_priv_map);
+}
/*
* pg_has_role variants
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index a675877d195..8d265f2d23c 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -47,6 +47,7 @@
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_opfamily.h"
+#include "catalog/pg_parameter_acl.h"
#include "catalog/pg_partitioned_table.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_publication.h"
@@ -574,6 +575,28 @@ static const struct cachedesc cacheinfo[] = {
},
8
},
+ {ParameterAclRelationId, /* PARAMETERACLNAME */
+ ParameterAclParnameIndexId,
+ 1,
+ {
+ Anum_pg_parameter_acl_parname,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
+ {ParameterAclRelationId, /* PARAMETERACLOID */
+ ParameterAclOidIndexId,
+ 1,
+ {
+ Anum_pg_parameter_acl_oid,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
{PartitionedRelationId, /* PARTRELID */
PartitionedRelidIndexId,
1,
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 9e8ab1420d9..5538465d7d6 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -45,6 +45,7 @@
#include "catalog/namespace.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_authid.h"
+#include "catalog/pg_parameter_acl.h"
#include "catalog/storage.h"
#include "commands/async.h"
#include "commands/prepare.h"
@@ -5714,6 +5715,65 @@ guc_name_compare(const char *namea, const char *nameb)
/*
+ * Convert a GUC name to the form that should be used in pg_parameter_acl.
+ *
+ * We need to canonicalize entries since, for example, case should not be
+ * significant. In addition, we apply the map_old_guc_names[] mapping so that
+ * any obsolete names will be converted when stored in a new PG version.
+ * Note however that this function does not verify legality of the name.
+ *
+ * The result is a palloc'd string.
+ */
+char *
+convert_GUC_name_for_parameter_acl(const char *name)
+{
+ char *result;
+
+ /* Apply old-GUC-name mapping. */
+ for (int i = 0; map_old_guc_names[i] != NULL; i += 2)
+ {
+ if (guc_name_compare(name, map_old_guc_names[i]) == 0)
+ {
+ name = map_old_guc_names[i + 1];
+ break;
+ }
+ }
+
+ /* Apply case-folding that matches guc_name_compare(). */
+ result = pstrdup(name);
+ for (char *ptr = result; *ptr != '\0'; ptr++)
+ {
+ char ch = *ptr;
+
+ if (ch >= 'A' && ch <= 'Z')
+ {
+ ch += 'a' - 'A';
+ *ptr = ch;
+ }
+ }
+
+ return result;
+}
+
+/*
+ * Check whether we should allow creation of a pg_parameter_acl entry
+ * for the given name. (This can be applied either before or after
+ * canonicalizing it.)
+ */
+bool
+check_GUC_name_for_parameter_acl(const char *name)
+{
+ /* OK if the GUC exists. */
+ if (find_option(name, false, true, DEBUG1) != NULL)
+ return true;
+ /* Otherwise, it'd better be a valid custom GUC name. */
+ if (valid_custom_variable_name(name))
+ return true;
+ return false;
+}
+
+
+/*
* Initialize GUC options during program startup.
*
* Note that we cannot read the config file yet, since we have not yet
@@ -7518,14 +7578,24 @@ set_config_option(const char *name, const char *value,
*/
break;
case PGC_SU_BACKEND:
- /* Reject if we're connecting but user is not superuser */
if (context == PGC_BACKEND)
{
- ereport(elevel,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("permission denied to set parameter \"%s\"",
- name)));
- return 0;
+ /*
+ * Check whether the current user has been granted privilege
+ * to set this GUC.
+ */
+ AclResult aclresult;
+
+ aclresult = pg_parameter_aclcheck(name, GetUserId(), ACL_SET);
+ if (aclresult != ACLCHECK_OK)
+ {
+ /* No granted privilege */
+ ereport(elevel,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to set parameter \"%s\"",
+ name)));
+ return 0;
+ }
}
/* fall through to process the same as PGC_BACKEND */
/* FALLTHROUGH */
@@ -7568,11 +7638,22 @@ set_config_option(const char *name, const char *value,
case PGC_SUSET:
if (context == PGC_USERSET || context == PGC_BACKEND)
{
- ereport(elevel,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("permission denied to set parameter \"%s\"",
- name)));
- return 0;
+ /*
+ * Check whether the current user has been granted privilege
+ * to set this GUC.
+ */
+ AclResult aclresult;
+
+ aclresult = pg_parameter_aclcheck(name, GetUserId(), ACL_SET);
+ if (aclresult != ACLCHECK_OK)
+ {
+ /* No granted privilege */
+ ereport(elevel,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to set parameter \"%s\"",
+ name)));
+ return 0;
+ }
}
break;
case PGC_USERSET:
@@ -8617,11 +8698,6 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
char AutoConfFileName[MAXPGPATH];
char AutoConfTmpFileName[MAXPGPATH];
- if (!superuser())
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("must be superuser to execute ALTER SYSTEM command")));
-
/*
* Extract statement arguments
*/
@@ -8650,6 +8726,29 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
}
/*
+ * Check permission to run ALTER SYSTEM on the target variable
+ */
+ if (!superuser())
+ {
+ if (resetall)
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to perform ALTER SYSTEM RESET ALL")));
+ else
+ {
+ AclResult aclresult;
+
+ aclresult = pg_parameter_aclcheck(name, GetUserId(),
+ ACL_ALTER_SYSTEM);
+ if (aclresult != ACLCHECK_OK)
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to set parameter \"%s\"",
+ name)));
+ }
+ }
+
+ /*
* Unless it's RESET_ALL, validate the target variable and value
*/
if (!resetall)
@@ -8760,13 +8859,18 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
}
/*
- * Invoke the post-alter hook for altering this GUC variable.
+ * Invoke the post-alter hook for setting this GUC variable. GUCs
+ * typically do not have corresponding entries in pg_parameter_acl, so we
+ * call the hook using the name rather than a potentially-non-existent
+ * OID. Nonetheless, we pass ParameterAclRelationId so that this call
+ * context can be distinguished from others. (Note that "name" will be
+ * NULL in the RESET ALL case.)
*
* We do this here rather than at the end, because ALTER SYSTEM is not
* transactional. If the hook aborts our transaction, it will be cleaner
* to do so before we touch any files.
*/
- InvokeObjectPostAlterHookArgStr(InvalidOid, name,
+ InvokeObjectPostAlterHookArgStr(ParameterAclRelationId, name,
ACL_ALTER_SYSTEM,
altersysstmt->setstmt->kind,
false);
@@ -8943,9 +9047,9 @@ ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
break;
}
- /* Invoke the post-alter hook for setting this GUC variable. */
- InvokeObjectPostAlterHookArgStr(InvalidOid, stmt->name,
- ACL_SET_VALUE, stmt->kind, false);
+ /* Invoke the post-alter hook for setting this GUC variable, by name. */
+ InvokeObjectPostAlterHookArgStr(ParameterAclRelationId, stmt->name,
+ ACL_SET, stmt->kind, false);
}
/*
diff --git a/src/bin/pg_dump/dumputils.c b/src/bin/pg_dump/dumputils.c
index 6086d57cf3a..3e68dfc78f9 100644
--- a/src/bin/pg_dump/dumputils.c
+++ b/src/bin/pg_dump/dumputils.c
@@ -37,7 +37,7 @@ static void AddAcl(PQExpBuffer aclbuf, const char *keyword,
* nspname: the namespace the object is in (NULL if none); not pre-quoted
* type: the object type (as seen in GRANT command: must be one of
* TABLE, SEQUENCE, FUNCTION, PROCEDURE, LANGUAGE, SCHEMA, DATABASE, TABLESPACE,
- * FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT)
+ * FOREIGN DATA WRAPPER, SERVER, PARAMETER or LARGE OBJECT)
* acls: the ACL string fetched from the database
* baseacls: the initial ACL string for this object
* owner: username of object owner (will be passed through fmtId); can be
@@ -501,6 +501,11 @@ do { \
CONVERT_PRIV('U', "USAGE");
else if (strcmp(type, "FOREIGN TABLE") == 0)
CONVERT_PRIV('r', "SELECT");
+ else if (strcmp(type, "PARAMETER") == 0)
+ {
+ CONVERT_PRIV('s', "SET");
+ CONVERT_PRIV('A', "ALTER SYSTEM");
+ }
else if (strcmp(type, "LARGE OBJECT") == 0)
{
CONVERT_PRIV('r', "SELECT");
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 9c9f7c6d63c..2dc33627630 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -18,6 +18,7 @@
#include <time.h>
#include <unistd.h>
+#include "catalog/pg_authid_d.h"
#include "common/connect.h"
#include "common/file_utils.h"
#include "common/logging.h"
@@ -36,6 +37,7 @@ static void help(void);
static void dropRoles(PGconn *conn);
static void dumpRoles(PGconn *conn);
static void dumpRoleMembership(PGconn *conn);
+static void dumpRoleGUCPrivs(PGconn *conn);
static void dropTablespaces(PGconn *conn);
static void dumpTablespaces(PGconn *conn);
static void dropDBs(PGconn *conn);
@@ -585,6 +587,10 @@ main(int argc, char *argv[])
/* Dump role memberships */
dumpRoleMembership(conn);
+
+ /* Dump role GUC privileges */
+ if (server_version >= 150000 && !skip_acls)
+ dumpRoleGUCPrivs(conn);
}
/* Dump tablespaces */
@@ -990,6 +996,65 @@ dumpRoleMembership(PGconn *conn)
/*
+ * Dump role configuration parameter privileges. This code is used for 15.0
+ * and later servers.
+ *
+ * Note: we expect dumpRoles already created all the roles, but there are
+ * no per-role configuration parameter privileges yet.
+ */
+static void
+dumpRoleGUCPrivs(PGconn *conn)
+{
+ PGresult *res;
+ int i;
+
+ /*
+ * Get all parameters that have non-default acls defined.
+ */
+ res = executeQuery(conn, "SELECT parname, "
+ "pg_catalog.pg_get_userbyid(" CppAsString2(BOOTSTRAP_SUPERUSERID) ") AS parowner, "
+ "paracl, "
+ "pg_catalog.acldefault('p', " CppAsString2(BOOTSTRAP_SUPERUSERID) ") AS acldefault "
+ "FROM pg_catalog.pg_parameter_acl "
+ "ORDER BY 1");
+
+ if (PQntuples(res) > 0)
+ fprintf(OPF, "--\n-- Role privileges on configuration parameters\n--\n\n");
+
+ for (i = 0; i < PQntuples(res); i++)
+ {
+ PQExpBuffer buf = createPQExpBuffer();
+ char *parname = PQgetvalue(res, i, 0);
+ char *parowner = PQgetvalue(res, i, 1);
+ char *paracl = PQgetvalue(res, i, 2);
+ char *acldefault = PQgetvalue(res, i, 3);
+ char *fparname;
+
+ /* needed for buildACLCommands() */
+ fparname = pg_strdup(fmtId(parname));
+
+ if (!buildACLCommands(fparname, NULL, NULL, "PARAMETER",
+ paracl, acldefault,
+ parowner, "", server_version, buf))
+ {
+ pg_log_error("could not parse ACL list (%s) for parameter \"%s\"",
+ paracl, parname);
+ PQfinish(conn);
+ exit_nicely(1);
+ }
+
+ fprintf(OPF, "%s", buf->data);
+
+ free(fparname);
+ destroyPQExpBuffer(buf);
+ }
+
+ PQclear(res);
+ fprintf(OPF, "\n\n");
+}
+
+
+/*
* Drop tablespaces.
*/
static void
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 645d46ab75a..32d0b4855f5 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -3737,7 +3737,8 @@ psql_completion(const char *text, int start, int end)
* ALTER DEFAULT PRIVILEGES, so use TailMatches
*/
/* Complete GRANT/REVOKE with a list of roles and privileges */
- else if (TailMatches("GRANT|REVOKE"))
+ else if (TailMatches("GRANT|REVOKE") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR"))
{
/*
* With ALTER DEFAULT PRIVILEGES, restrict completion to grantable
@@ -3749,6 +3750,7 @@ psql_completion(const char *text, int start, int end)
"EXECUTE", "USAGE", "ALL");
else
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
+ "GRANT",
"SELECT",
"INSERT",
"UPDATE",
@@ -3761,14 +3763,48 @@ psql_completion(const char *text, int start, int end)
"TEMPORARY",
"EXECUTE",
"USAGE",
+ "SET",
+ "ALTER SYSTEM",
"ALL");
}
+ else if (TailMatches("REVOKE", "GRANT"))
+ COMPLETE_WITH("OPTION FOR");
+ else if (TailMatches("REVOKE", "GRANT", "OPTION"))
+ COMPLETE_WITH("FOR");
+
+ else if (TailMatches("GRANT|REVOKE", "ALTER") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "ALTER"))
+ COMPLETE_WITH("SYSTEM");
+
+ else if (TailMatches("GRANT|REVOKE", "SET") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "SET") ||
+ TailMatches("GRANT|REVOKE", "ALTER", "SYSTEM") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "ALTER", "SYSTEM"))
+ COMPLETE_WITH("ON PARAMETER");
+
+ else if (TailMatches("GRANT|REVOKE", MatchAny, "ON", "PARAMETER") ||
+ TailMatches("GRANT|REVOKE", MatchAny, MatchAny, "ON", "PARAMETER") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON", "PARAMETER") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, MatchAny, "ON", "PARAMETER"))
+ COMPLETE_WITH_QUERY(Query_for_list_of_alter_system_set_vars);
+
+ else if (TailMatches("GRANT", MatchAny, "ON", "PARAMETER", MatchAny) ||
+ TailMatches("GRANT", MatchAny, MatchAny, "ON", "PARAMETER", MatchAny))
+ COMPLETE_WITH("TO");
+
+ else if (TailMatches("REVOKE", MatchAny, "ON", "PARAMETER", MatchAny) ||
+ TailMatches("REVOKE", MatchAny, MatchAny, "ON", "PARAMETER", MatchAny) ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON", "PARAMETER", MatchAny) ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, MatchAny, "ON", "PARAMETER", MatchAny))
+ COMPLETE_WITH("FROM");
+
/*
* Complete GRANT/REVOKE <privilege> with "ON", GRANT/REVOKE <role> with
* TO/FROM
*/
- else if (TailMatches("GRANT|REVOKE", MatchAny))
+ else if (TailMatches("GRANT|REVOKE", MatchAny) ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny))
{
if (TailMatches("SELECT|INSERT|UPDATE|DELETE|TRUNCATE|REFERENCES|TRIGGER|CREATE|CONNECT|TEMPORARY|TEMP|EXECUTE|USAGE|ALL"))
COMPLETE_WITH("ON");
@@ -3785,7 +3821,8 @@ psql_completion(const char *text, int start, int end)
* here will only work if the privilege list contains exactly one
* privilege.
*/
- else if (TailMatches("GRANT|REVOKE", MatchAny, "ON"))
+ else if (TailMatches("GRANT|REVOKE", MatchAny, "ON") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON"))
{
/*
* With ALTER DEFAULT PRIVILEGES, restrict completion to the kinds of
@@ -3807,6 +3844,7 @@ psql_completion(const char *text, int start, int end)
"FUNCTION",
"LANGUAGE",
"LARGE OBJECT",
+ "PARAMETER",
"PROCEDURE",
"ROUTINE",
"SCHEMA",
@@ -3815,13 +3853,15 @@ psql_completion(const char *text, int start, int end)
"TABLESPACE",
"TYPE");
}
- else if (TailMatches("GRANT|REVOKE", MatchAny, "ON", "ALL"))
+ else if (TailMatches("GRANT|REVOKE", MatchAny, "ON", "ALL") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON", "ALL"))
COMPLETE_WITH("FUNCTIONS IN SCHEMA",
"PROCEDURES IN SCHEMA",
"ROUTINES IN SCHEMA",
"SEQUENCES IN SCHEMA",
"TABLES IN SCHEMA");
- else if (TailMatches("GRANT|REVOKE", MatchAny, "ON", "FOREIGN"))
+ else if (TailMatches("GRANT|REVOKE", MatchAny, "ON", "FOREIGN") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON", "FOREIGN"))
COMPLETE_WITH("DATA WRAPPER", "SERVER");
/*
@@ -3830,7 +3870,8 @@ psql_completion(const char *text, int start, int end)
*
* Complete "GRANT/REVOKE * ON *" with "TO/FROM".
*/
- else if (TailMatches("GRANT|REVOKE", MatchAny, "ON", MatchAny))
+ else if (TailMatches("GRANT|REVOKE", MatchAny, "ON", MatchAny) ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON", MatchAny))
{
if (TailMatches("DATABASE"))
COMPLETE_WITH_QUERY(Query_for_list_of_databases);
@@ -3868,6 +3909,22 @@ psql_completion(const char *text, int start, int end)
(HeadMatches("REVOKE") && TailMatches("FROM")))
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
Keywords_for_list_of_grant_roles);
+
+ /*
+ * Offer grant options after that.
+ */
+ else if (HeadMatches("GRANT") && TailMatches("TO", MatchAny))
+ COMPLETE_WITH("WITH ADMIN OPTION",
+ "WITH GRANT OPTION",
+ "GRANTED BY");
+ else if (HeadMatches("GRANT") && TailMatches("TO", MatchAny, "WITH"))
+ COMPLETE_WITH("ADMIN OPTION",
+ "GRANT OPTION");
+ else if (HeadMatches("GRANT") && TailMatches("TO", MatchAny, "WITH", MatchAny, "OPTION"))
+ COMPLETE_WITH("GRANTED BY");
+ else if (HeadMatches("GRANT") && TailMatches("TO", MatchAny, "WITH", MatchAny, "OPTION", "GRANTED", "BY"))
+ COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
+ Keywords_for_list_of_grant_roles);
/* Complete "ALTER DEFAULT PRIVILEGES ... GRANT/REVOKE ... TO/FROM */
else if (HeadMatches("ALTER", "DEFAULT", "PRIVILEGES") && TailMatches("TO|FROM"))
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
@@ -3879,7 +3936,8 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH("FROM");
/* Complete "GRANT/REVOKE * ON ALL * IN SCHEMA *" with TO/FROM */
- else if (TailMatches("GRANT|REVOKE", MatchAny, "ON", "ALL", MatchAny, "IN", "SCHEMA", MatchAny))
+ else if (TailMatches("GRANT|REVOKE", MatchAny, "ON", "ALL", MatchAny, "IN", "SCHEMA", MatchAny) ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON", "ALL", MatchAny, "IN", "SCHEMA", MatchAny))
{
if (TailMatches("GRANT", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny, MatchAny, MatchAny))
COMPLETE_WITH("TO");
@@ -3888,7 +3946,8 @@ psql_completion(const char *text, int start, int end)
}
/* Complete "GRANT/REVOKE * ON FOREIGN DATA WRAPPER *" with TO/FROM */
- else if (TailMatches("GRANT|REVOKE", MatchAny, "ON", "FOREIGN", "DATA", "WRAPPER", MatchAny))
+ else if (TailMatches("GRANT|REVOKE", MatchAny, "ON", "FOREIGN", "DATA", "WRAPPER", MatchAny) ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON", "FOREIGN", "DATA", "WRAPPER", MatchAny))
{
if (TailMatches("GRANT", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny, MatchAny))
COMPLETE_WITH("TO");
@@ -3897,7 +3956,8 @@ psql_completion(const char *text, int start, int end)
}
/* Complete "GRANT/REVOKE * ON FOREIGN SERVER *" with TO/FROM */
- else if (TailMatches("GRANT|REVOKE", MatchAny, "ON", "FOREIGN", "SERVER", MatchAny))
+ else if (TailMatches("GRANT|REVOKE", MatchAny, "ON", "FOREIGN", "SERVER", MatchAny) ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON", "FOREIGN", "SERVER", MatchAny))
{
if (TailMatches("GRANT", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny))
COMPLETE_WITH("TO");
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index cb26c967adc..ab28a56b3a0 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 202203301
+#define CATALOG_VERSION_NO 202204061
#endif
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 344482ec877..d027075a4c3 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -120,6 +120,7 @@ typedef enum ObjectClass
OCLASS_DEFACL, /* pg_default_acl */
OCLASS_EXTENSION, /* pg_extension */
OCLASS_EVENT_TRIGGER, /* pg_event_trigger */
+ OCLASS_PARAMETER_ACL, /* pg_parameter_acl */
OCLASS_POLICY, /* pg_policy */
OCLASS_PUBLICATION, /* pg_publication */
OCLASS_PUBLICATION_NAMESPACE, /* pg_publication_namespace */
diff --git a/src/include/catalog/objectaccess.h b/src/include/catalog/objectaccess.h
index 4d54ae2a7d4..ac6adcb7300 100644
--- a/src/include/catalog/objectaccess.h
+++ b/src/include/catalog/objectaccess.h
@@ -239,7 +239,7 @@ extern void RunFunctionExecuteHookStr(const char *objectStr);
RunObjectTruncateHookStr(objectName); \
} while(0)
-#define InvokeObjectPostAlterHookStr(className,objectName,subId) \
+#define InvokeObjectPostAlterHookStr(classId,objectName,subId) \
InvokeObjectPostAlterHookArgStr((classId),(objectName),(subId), \
InvalidOid,false)
#define InvokeObjectPostAlterHookArgStr(classId,objectName,subId, \
diff --git a/src/include/catalog/pg_parameter_acl.h b/src/include/catalog/pg_parameter_acl.h
new file mode 100644
index 00000000000..8316391e51c
--- /dev/null
+++ b/src/include/catalog/pg_parameter_acl.h
@@ -0,0 +1,62 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_parameter_acl.h
+ * definition of the "configuration parameter ACL" system catalog
+ * (pg_parameter_acl).
+ *
+ *
+ * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_parameter_acl.h
+ *
+ * NOTES
+ * The Catalog.pm module reads this file and derives schema
+ * information.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_PARAMETER_ACL_H
+#define PG_PARAMETER_ACL_H
+
+#include "catalog/genbki.h"
+#include "catalog/pg_parameter_acl_d.h"
+
+/* ----------------
+ * pg_parameter_acl definition. cpp turns this into
+ * typedef struct FormData_pg_parameter_acl
+ * ----------------
+ */
+CATALOG(pg_parameter_acl,8924,ParameterAclRelationId) BKI_SHARED_RELATION
+{
+ Oid oid; /* oid */
+
+#ifdef CATALOG_VARLEN /* variable-length fields start here */
+ /* name of parameter */
+ text parname BKI_FORCE_NOT_NULL;
+
+ /* access permissions */
+ aclitem paracl[1] BKI_DEFAULT(_null_);
+#endif
+} FormData_pg_parameter_acl;
+
+
+/* ----------------
+ * Form_pg_parameter_acl corresponds to a pointer to a tuple with
+ * the format of pg_parameter_acl relation.
+ * ----------------
+ */
+typedef FormData_pg_parameter_acl *Form_pg_parameter_acl;
+
+DECLARE_TOAST(pg_parameter_acl, 8925, 8926);
+#define PgParameterAclToastTable 8925
+#define PgParameterAclToastIndex 8926
+
+DECLARE_UNIQUE_INDEX(pg_parameter_acl_parname_index, 8927, ParameterAclParnameIndexId, on pg_parameter_acl using btree(parname text_ops));
+DECLARE_UNIQUE_INDEX_PKEY(pg_parameter_acl_oid_index, 8928, ParameterAclOidIndexId, on pg_parameter_acl using btree(oid oid_ops));
+
+
+extern Oid ParameterAclLookup(const char *parameter, bool missing_ok);
+extern Oid ParameterAclCreate(const char *parameter);
+
+#endif /* PG_PARAMETER_ACL_H */
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 25304430f44..4d285ece8bf 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -7213,6 +7213,22 @@
proname => 'has_type_privilege', provolatile => 's', prorettype => 'bool',
proargtypes => 'oid text', prosrc => 'has_type_privilege_id' },
+{ oid => '8050',
+ descr => 'user privilege on parameter by username, parameter name',
+ proname => 'has_parameter_privilege', provolatile => 's',
+ prorettype => 'bool', proargtypes => 'name text text',
+ prosrc => 'has_parameter_privilege_name_name' },
+{ oid => '8051',
+ descr => 'user privilege on parameter by user oid, parameter name',
+ proname => 'has_parameter_privilege', provolatile => 's',
+ prorettype => 'bool', proargtypes => 'oid text text',
+ prosrc => 'has_parameter_privilege_id_name' },
+{ oid => '8052',
+ descr => 'current user privilege on parameter by parameter name',
+ proname => 'has_parameter_privilege', provolatile => 's',
+ prorettype => 'bool', proargtypes => 'text text',
+ prosrc => 'has_parameter_privilege_name' },
+
{ oid => '2705', descr => 'user privilege on role by username, role name',
proname => 'pg_has_role', provolatile => 's', prorettype => 'bool',
proargtypes => 'name name text', prosrc => 'pg_has_role_name_name' },
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 4a2ca81f3c0..8998d345605 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -92,8 +92,8 @@ typedef uint32 AclMode; /* a bitmask of privilege bits */
#define ACL_CREATE (1<<9) /* for namespaces and databases */
#define ACL_CREATE_TEMP (1<<10) /* for databases */
#define ACL_CONNECT (1<<11) /* for databases */
-#define ACL_SET_VALUE (1<<12) /* for configuration parameters */
-#define ACL_ALTER_SYSTEM (1<<13) /* for configuration parameters */
+#define ACL_SET (1<<12) /* for configuration parameters */
+#define ACL_ALTER_SYSTEM (1<<13) /* for configuration parameters */
#define N_ACL_RIGHTS 14 /* 1 plus the last 1<<x */
#define ACL_NO_RIGHTS 0
/* Currently, SELECT ... FOR [KEY] UPDATE/SHARE requires UPDATE privileges */
@@ -2162,6 +2162,7 @@ typedef enum ObjectType
OBJECT_OPCLASS,
OBJECT_OPERATOR,
OBJECT_OPFAMILY,
+ OBJECT_PARAMETER_ACL,
OBJECT_POLICY,
OBJECT_PROCEDURE,
OBJECT_PUBLICATION,
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 0caa7310f2d..8a2ab405a28 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -329,6 +329,7 @@ PG_KEYWORD("overriding", OVERRIDING, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("owned", OWNED, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("owner", OWNER, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("parallel", PARALLEL, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("parameter", PARAMETER, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("parser", PARSER, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("partial", PARTIAL, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("partition", PARTITION, UNRESERVED_KEYWORD, BARE_LABEL)
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index 91ce3d8e9c3..48f7d72add5 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -146,7 +146,7 @@ typedef struct ArrayType Acl;
#define ACL_CREATE_CHR 'C'
#define ACL_CREATE_TEMP_CHR 'T'
#define ACL_CONNECT_CHR 'c'
-#define ACL_SET_VALUE_CHR 's'
+#define ACL_SET_CHR 's'
#define ACL_ALTER_SYSTEM_CHR 'A'
/* string holding all privilege code chars, in order by bitmask position */
@@ -164,6 +164,7 @@ typedef struct ArrayType Acl;
#define ACL_ALL_RIGHTS_FUNCTION (ACL_EXECUTE)
#define ACL_ALL_RIGHTS_LANGUAGE (ACL_USAGE)
#define ACL_ALL_RIGHTS_LARGEOBJECT (ACL_SELECT|ACL_UPDATE)
+#define ACL_ALL_RIGHTS_PARAMETER_ACL (ACL_SET|ACL_ALTER_SYSTEM)
#define ACL_ALL_RIGHTS_SCHEMA (ACL_USAGE|ACL_CREATE)
#define ACL_ALL_RIGHTS_TABLESPACE (ACL_CREATE)
#define ACL_ALL_RIGHTS_TYPE (ACL_USAGE)
@@ -245,6 +246,10 @@ extern AclMode pg_class_aclmask_ext(Oid table_oid, Oid roleid,
bool *is_missing);
extern AclMode pg_database_aclmask(Oid db_oid, Oid roleid,
AclMode mask, AclMaskHow how);
+extern AclMode pg_parameter_aclmask(const char *name, Oid roleid,
+ AclMode mask, AclMaskHow how);
+extern AclMode pg_parameter_acl_aclmask(Oid acl_oid, Oid roleid,
+ AclMode mask, AclMaskHow how);
extern AclMode pg_proc_aclmask(Oid proc_oid, Oid roleid,
AclMode mask, AclMaskHow how);
extern AclMode pg_language_aclmask(Oid lang_oid, Oid roleid,
@@ -273,6 +278,10 @@ extern AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode);
extern AclResult pg_class_aclcheck_ext(Oid table_oid, Oid roleid,
AclMode mode, bool *is_missing);
extern AclResult pg_database_aclcheck(Oid db_oid, Oid roleid, AclMode mode);
+extern AclResult pg_parameter_aclcheck(const char *name, Oid roleid,
+ AclMode mode);
+extern AclResult pg_parameter_acl_aclcheck(Oid acl_oid, Oid roleid,
+ AclMode mode);
extern AclResult pg_proc_aclcheck(Oid proc_oid, Oid roleid, AclMode mode);
extern AclResult pg_language_aclcheck(Oid lang_oid, Oid roleid, AclMode mode);
extern AclResult pg_largeobject_aclcheck_snapshot(Oid lang_oid, Oid roleid,
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index ea774968f07..3446334e906 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -364,6 +364,8 @@ extern const char *GetConfigOption(const char *name, bool missing_ok,
extern const char *GetConfigOptionResetString(const char *name);
extern int GetConfigOptionFlags(const char *name, bool missing_ok);
extern void ProcessConfigFile(GucContext context);
+extern char *convert_GUC_name_for_parameter_acl(const char *name);
+extern bool check_GUC_name_for_parameter_acl(const char *name);
extern void InitializeGUCOptions(void);
extern bool SelectConfigFiles(const char *userDoption, const char *progname);
extern void ResetAllOptions(void);
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 9c1a76e8bb6..4463ea66bea 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -72,6 +72,8 @@ enum SysCacheIdentifier
OPEROID,
OPFAMILYAMNAMENSP,
OPFAMILYOID,
+ PARAMETERACLNAME,
+ PARAMETERACLOID,
PARTRELID,
PROCNAMEARGSNSP,
PROCOID,
diff --git a/src/test/modules/test_oat_hooks/expected/test_oat_hooks.out b/src/test/modules/test_oat_hooks/expected/test_oat_hooks.out
index 45ff276f7e9..39b274b8fa0 100644
--- a/src/test/modules/test_oat_hooks/expected/test_oat_hooks.out
+++ b/src/test/modules/test_oat_hooks/expected/test_oat_hooks.out
@@ -1,36 +1,84 @@
+-- Creating privileges on a placeholder GUC should create entries in the
+-- pg_parameter_acl catalog which conservatively grant no privileges to public.
+CREATE ROLE regress_role_joe;
+GRANT SET ON PARAMETER test_oat_hooks.user_var1 TO regress_role_joe;
+GRANT SET ON PARAMETER test_oat_hooks.super_var1 TO regress_role_joe;
-- SET commands fire both the ProcessUtility_hook and the
-- object_access_hook_str. Since the auditing GUC starts out false, we miss the
-- initial "attempting" audit message from the ProcessUtility_hook, but we
--- should thereafter see the audit messages
+-- should thereafter see the audit messages.
LOAD 'test_oat_hooks';
SET test_oat_hooks.audit = true;
-NOTICE: in object_access_hook_str: superuser attempting alter (set) [test_oat_hooks.audit]
-NOTICE: in object_access_hook_str: superuser finished alter (set) [test_oat_hooks.audit]
+NOTICE: in object_access_hook_str: superuser attempting alter (subId=0x1000, set) [test_oat_hooks.audit]
+NOTICE: in object_access_hook_str: superuser finished alter (subId=0x1000, set) [test_oat_hooks.audit]
NOTICE: in process utility: superuser finished set
+-- Creating privileges on an existent custom GUC should create precisely the
+-- right privileges, not overly conservative ones.
+GRANT SET ON PARAMETER test_oat_hooks.user_var2 TO regress_role_joe;
+NOTICE: in process utility: superuser attempting GrantStmt
+NOTICE: in process utility: superuser finished GrantStmt
+GRANT SET ON PARAMETER test_oat_hooks.super_var2 TO regress_role_joe;
+NOTICE: in process utility: superuser attempting GrantStmt
+NOTICE: in process utility: superuser finished GrantStmt
+-- Granting multiple privileges on a parameter should be reported correctly to
+-- the OAT hook, but beware that WITH GRANT OPTION is not represented.
+GRANT SET, ALTER SYSTEM ON PARAMETER none.such TO regress_role_joe;
+NOTICE: in process utility: superuser attempting GrantStmt
+NOTICE: in process utility: superuser finished GrantStmt
+GRANT SET, ALTER SYSTEM ON PARAMETER another.bogus TO regress_role_joe WITH GRANT OPTION;
+NOTICE: in process utility: superuser attempting GrantStmt
+NOTICE: in process utility: superuser finished GrantStmt
+-- Check when the hooks fire relative to dependency based abort of a drop
+DROP ROLE regress_role_joe;
+NOTICE: in process utility: superuser attempting DropRoleStmt
+NOTICE: in object access: superuser attempting drop (subId=0x0) []
+NOTICE: in object access: superuser finished drop (subId=0x0) []
+ERROR: role "regress_role_joe" cannot be dropped because some objects depend on it
+DETAIL: privileges for parameter test_oat_hooks.user_var1
+privileges for parameter test_oat_hooks.super_var1
+privileges for parameter test_oat_hooks.user_var2
+privileges for parameter test_oat_hooks.super_var2
+privileges for parameter none.such
+privileges for parameter another.bogus
+-- Check the behavior of the hooks relative to do-nothing grants and revokes
+GRANT SET ON PARAMETER work_mem TO PUBLIC;
+NOTICE: in process utility: superuser attempting GrantStmt
+NOTICE: in process utility: superuser finished GrantStmt
+REVOKE ALTER SYSTEM ON PARAMETER work_mem FROM PUBLIC;
+NOTICE: in process utility: superuser attempting GrantStmt
+NOTICE: in process utility: superuser finished GrantStmt
+-- Check the behavior of the hooks relative to unrecognized parameters
+GRANT ALL ON PARAMETER "none.such" TO PUBLIC;
+NOTICE: in process utility: superuser attempting GrantStmt
+NOTICE: in process utility: superuser finished GrantStmt
+-- Check relative to an operation that causes the catalog entry to be deleted
+REVOKE ALL ON PARAMETER "none.such" FROM PUBLIC;
+NOTICE: in process utility: superuser attempting GrantStmt
+NOTICE: in process utility: superuser finished GrantStmt
-- Create objects for use in the test
CREATE USER regress_test_user;
NOTICE: in process utility: superuser attempting CreateRoleStmt
-NOTICE: in object access: superuser attempting create (subId=0) [explicit]
-NOTICE: in object access: superuser finished create (subId=0) [explicit]
+NOTICE: in object access: superuser attempting create (subId=0x0) [explicit]
+NOTICE: in object access: superuser finished create (subId=0x0) [explicit]
NOTICE: in process utility: superuser finished CreateRoleStmt
CREATE TABLE regress_test_table (t text);
NOTICE: in process utility: superuser attempting CreateStmt
-NOTICE: in object access: superuser attempting namespace search (subId=0) [no report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [no report on violation, allowed]
LINE 1: CREATE TABLE regress_test_table (t text);
^
-NOTICE: in object access: superuser finished namespace search (subId=0) [no report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [no report on violation, allowed]
LINE 1: CREATE TABLE regress_test_table (t text);
^
-NOTICE: in object access: superuser attempting create (subId=0) [explicit]
-NOTICE: in object access: superuser finished create (subId=0) [explicit]
-NOTICE: in object access: superuser attempting create (subId=0) [explicit]
-NOTICE: in object access: superuser finished create (subId=0) [explicit]
-NOTICE: in object access: superuser attempting create (subId=0) [explicit]
-NOTICE: in object access: superuser finished create (subId=0) [explicit]
-NOTICE: in object access: superuser attempting create (subId=0) [internal]
-NOTICE: in object access: superuser finished create (subId=0) [internal]
-NOTICE: in object access: superuser attempting create (subId=0) [internal]
-NOTICE: in object access: superuser finished create (subId=0) [internal]
+NOTICE: in object access: superuser attempting create (subId=0x0) [explicit]
+NOTICE: in object access: superuser finished create (subId=0x0) [explicit]
+NOTICE: in object access: superuser attempting create (subId=0x0) [explicit]
+NOTICE: in object access: superuser finished create (subId=0x0) [explicit]
+NOTICE: in object access: superuser attempting create (subId=0x0) [explicit]
+NOTICE: in object access: superuser finished create (subId=0x0) [explicit]
+NOTICE: in object access: superuser attempting create (subId=0x0) [internal]
+NOTICE: in object access: superuser finished create (subId=0x0) [internal]
+NOTICE: in object access: superuser attempting create (subId=0x0) [internal]
+NOTICE: in object access: superuser finished create (subId=0x0) [internal]
NOTICE: in process utility: superuser finished CreateStmt
GRANT SELECT ON Table regress_test_table TO public;
NOTICE: in process utility: superuser attempting GrantStmt
@@ -39,8 +87,8 @@ CREATE FUNCTION regress_test_func (t text) RETURNS text AS $$
SELECT $1;
$$ LANGUAGE sql;
NOTICE: in process utility: superuser attempting CreateFunctionStmt
-NOTICE: in object access: superuser attempting create (subId=0) [explicit]
-NOTICE: in object access: superuser finished create (subId=0) [explicit]
+NOTICE: in object access: superuser attempting create (subId=0x0) [explicit]
+NOTICE: in object access: superuser finished create (subId=0x0) [explicit]
NOTICE: in process utility: superuser finished CreateFunctionStmt
GRANT EXECUTE ON FUNCTION regress_test_func (text) TO public;
NOTICE: in process utility: superuser attempting GrantStmt
@@ -63,35 +111,35 @@ NOTICE: in executor check perms: superuser finished execute
SET work_mem = 8192;
NOTICE: in process utility: superuser attempting set
-NOTICE: in object_access_hook_str: superuser attempting alter (set) [work_mem]
-NOTICE: in object_access_hook_str: superuser finished alter (set) [work_mem]
+NOTICE: in object_access_hook_str: superuser attempting alter (subId=0x1000, set) [work_mem]
+NOTICE: in object_access_hook_str: superuser finished alter (subId=0x1000, set) [work_mem]
NOTICE: in process utility: superuser finished set
RESET work_mem;
NOTICE: in process utility: superuser attempting set
-NOTICE: in object_access_hook_str: superuser attempting alter (set) [work_mem]
-NOTICE: in object_access_hook_str: superuser finished alter (set) [work_mem]
+NOTICE: in object_access_hook_str: superuser attempting alter (subId=0x1000, set) [work_mem]
+NOTICE: in object_access_hook_str: superuser finished alter (subId=0x1000, set) [work_mem]
NOTICE: in process utility: superuser finished set
ALTER SYSTEM SET work_mem = 8192;
NOTICE: in process utility: superuser attempting alter system
-NOTICE: in object_access_hook_str: superuser attempting alter (alter system set) [work_mem]
-NOTICE: in object_access_hook_str: superuser finished alter (alter system set) [work_mem]
+NOTICE: in object_access_hook_str: superuser attempting alter (subId=0x2000, alter system) [work_mem]
+NOTICE: in object_access_hook_str: superuser finished alter (subId=0x2000, alter system) [work_mem]
NOTICE: in process utility: superuser finished alter system
ALTER SYSTEM RESET work_mem;
NOTICE: in process utility: superuser attempting alter system
-NOTICE: in object_access_hook_str: superuser attempting alter (alter system set) [work_mem]
-NOTICE: in object_access_hook_str: superuser finished alter (alter system set) [work_mem]
+NOTICE: in object_access_hook_str: superuser attempting alter (subId=0x2000, alter system) [work_mem]
+NOTICE: in object_access_hook_str: superuser finished alter (subId=0x2000, alter system) [work_mem]
NOTICE: in process utility: superuser finished alter system
-- Do those same things as non-superuser
SET SESSION AUTHORIZATION regress_test_user;
NOTICE: in process utility: superuser attempting set
-NOTICE: in object_access_hook_str: non-superuser attempting alter (set) [session_authorization]
-NOTICE: in object_access_hook_str: non-superuser finished alter (set) [session_authorization]
+NOTICE: in object_access_hook_str: non-superuser attempting alter (subId=0x1000, set) [session_authorization]
+NOTICE: in object_access_hook_str: non-superuser finished alter (subId=0x1000, set) [session_authorization]
NOTICE: in process utility: non-superuser finished set
SELECT * FROM regress_test_table;
-NOTICE: in object access: non-superuser attempting namespace search (subId=0) [no report on violation, allowed]
+NOTICE: in object access: non-superuser attempting namespace search (subId=0x0) [no report on violation, allowed]
LINE 1: SELECT * FROM regress_test_table;
^
-NOTICE: in object access: non-superuser finished namespace search (subId=0) [no report on violation, allowed]
+NOTICE: in object access: non-superuser finished namespace search (subId=0x0) [no report on violation, allowed]
LINE 1: SELECT * FROM regress_test_table;
^
NOTICE: in executor check perms: non-superuser attempting execute
@@ -110,61 +158,89 @@ NOTICE: in executor check perms: non-superuser finished execute
SET work_mem = 8192;
NOTICE: in process utility: non-superuser attempting set
-NOTICE: in object_access_hook_str: non-superuser attempting alter (set) [work_mem]
-NOTICE: in object_access_hook_str: non-superuser finished alter (set) [work_mem]
+NOTICE: in object_access_hook_str: non-superuser attempting alter (subId=0x1000, set) [work_mem]
+NOTICE: in object_access_hook_str: non-superuser finished alter (subId=0x1000, set) [work_mem]
NOTICE: in process utility: non-superuser finished set
RESET work_mem;
NOTICE: in process utility: non-superuser attempting set
-NOTICE: in object_access_hook_str: non-superuser attempting alter (set) [work_mem]
-NOTICE: in object_access_hook_str: non-superuser finished alter (set) [work_mem]
+NOTICE: in object_access_hook_str: non-superuser attempting alter (subId=0x1000, set) [work_mem]
+NOTICE: in object_access_hook_str: non-superuser finished alter (subId=0x1000, set) [work_mem]
NOTICE: in process utility: non-superuser finished set
ALTER SYSTEM SET work_mem = 8192;
NOTICE: in process utility: non-superuser attempting alter system
-ERROR: must be superuser to execute ALTER SYSTEM command
+ERROR: permission denied to set parameter "work_mem"
ALTER SYSTEM RESET work_mem;
NOTICE: in process utility: non-superuser attempting alter system
-ERROR: must be superuser to execute ALTER SYSTEM command
+ERROR: permission denied to set parameter "work_mem"
+SET test_oat_hooks.user_var1 = true;
+NOTICE: in process utility: non-superuser attempting set
+NOTICE: in object_access_hook_str: non-superuser attempting alter (subId=0x1000, set) [test_oat_hooks.user_var1]
+NOTICE: in object_access_hook_str: non-superuser finished alter (subId=0x1000, set) [test_oat_hooks.user_var1]
+NOTICE: in process utility: non-superuser finished set
+SET test_oat_hooks.super_var1 = true;
+NOTICE: in process utility: non-superuser attempting set
+ERROR: permission denied to set parameter "test_oat_hooks.super_var1"
+ALTER SYSTEM SET test_oat_hooks.user_var1 = true;
+NOTICE: in process utility: non-superuser attempting alter system
+ERROR: permission denied to set parameter "test_oat_hooks.user_var1"
+ALTER SYSTEM SET test_oat_hooks.super_var1 = true;
+NOTICE: in process utility: non-superuser attempting alter system
+ERROR: permission denied to set parameter "test_oat_hooks.super_var1"
+SET test_oat_hooks.user_var2 = true;
+NOTICE: in process utility: non-superuser attempting set
+NOTICE: in object_access_hook_str: non-superuser attempting alter (subId=0x1000, set) [test_oat_hooks.user_var2]
+NOTICE: in object_access_hook_str: non-superuser finished alter (subId=0x1000, set) [test_oat_hooks.user_var2]
+NOTICE: in process utility: non-superuser finished set
+SET test_oat_hooks.super_var2 = true;
+NOTICE: in process utility: non-superuser attempting set
+ERROR: permission denied to set parameter "test_oat_hooks.super_var2"
+ALTER SYSTEM SET test_oat_hooks.user_var2 = true;
+NOTICE: in process utility: non-superuser attempting alter system
+ERROR: permission denied to set parameter "test_oat_hooks.user_var2"
+ALTER SYSTEM SET test_oat_hooks.super_var2 = true;
+NOTICE: in process utility: non-superuser attempting alter system
+ERROR: permission denied to set parameter "test_oat_hooks.super_var2"
RESET SESSION AUTHORIZATION;
NOTICE: in process utility: non-superuser attempting set
-NOTICE: in object_access_hook_str: superuser attempting alter (set) [session_authorization]
-NOTICE: in object_access_hook_str: superuser finished alter (set) [session_authorization]
+NOTICE: in object_access_hook_str: superuser attempting alter (subId=0x1000, set) [session_authorization]
+NOTICE: in object_access_hook_str: superuser finished alter (subId=0x1000, set) [session_authorization]
NOTICE: in process utility: superuser finished set
-- Turn off non-superuser permissions
SET test_oat_hooks.deny_set_variable = true;
NOTICE: in process utility: superuser attempting set
-NOTICE: in object_access_hook_str: superuser attempting alter (set) [test_oat_hooks.deny_set_variable]
-NOTICE: in object_access_hook_str: superuser finished alter (set) [test_oat_hooks.deny_set_variable]
+NOTICE: in object_access_hook_str: superuser attempting alter (subId=0x1000, set) [test_oat_hooks.deny_set_variable]
+NOTICE: in object_access_hook_str: superuser finished alter (subId=0x1000, set) [test_oat_hooks.deny_set_variable]
NOTICE: in process utility: superuser finished set
SET test_oat_hooks.deny_alter_system = true;
NOTICE: in process utility: superuser attempting set
-NOTICE: in object_access_hook_str: superuser attempting alter (set) [test_oat_hooks.deny_alter_system]
-NOTICE: in object_access_hook_str: superuser finished alter (set) [test_oat_hooks.deny_alter_system]
+NOTICE: in object_access_hook_str: superuser attempting alter (subId=0x1000, set) [test_oat_hooks.deny_alter_system]
+NOTICE: in object_access_hook_str: superuser finished alter (subId=0x1000, set) [test_oat_hooks.deny_alter_system]
NOTICE: in process utility: superuser finished set
SET test_oat_hooks.deny_object_access = true;
NOTICE: in process utility: superuser attempting set
-NOTICE: in object_access_hook_str: superuser attempting alter (set) [test_oat_hooks.deny_object_access]
-NOTICE: in object_access_hook_str: superuser finished alter (set) [test_oat_hooks.deny_object_access]
+NOTICE: in object_access_hook_str: superuser attempting alter (subId=0x1000, set) [test_oat_hooks.deny_object_access]
+NOTICE: in object_access_hook_str: superuser finished alter (subId=0x1000, set) [test_oat_hooks.deny_object_access]
NOTICE: in process utility: superuser finished set
SET test_oat_hooks.deny_exec_perms = true;
NOTICE: in process utility: superuser attempting set
-NOTICE: in object_access_hook_str: superuser attempting alter (set) [test_oat_hooks.deny_exec_perms]
-NOTICE: in object_access_hook_str: superuser finished alter (set) [test_oat_hooks.deny_exec_perms]
+NOTICE: in object_access_hook_str: superuser attempting alter (subId=0x1000, set) [test_oat_hooks.deny_exec_perms]
+NOTICE: in object_access_hook_str: superuser finished alter (subId=0x1000, set) [test_oat_hooks.deny_exec_perms]
NOTICE: in process utility: superuser finished set
SET test_oat_hooks.deny_utility_commands = true;
NOTICE: in process utility: superuser attempting set
-NOTICE: in object_access_hook_str: superuser attempting alter (set) [test_oat_hooks.deny_utility_commands]
-NOTICE: in object_access_hook_str: superuser finished alter (set) [test_oat_hooks.deny_utility_commands]
+NOTICE: in object_access_hook_str: superuser attempting alter (subId=0x1000, set) [test_oat_hooks.deny_utility_commands]
+NOTICE: in object_access_hook_str: superuser finished alter (subId=0x1000, set) [test_oat_hooks.deny_utility_commands]
NOTICE: in process utility: superuser finished set
-- Try again as non-superuser with permissions denied
SET SESSION AUTHORIZATION regress_test_user;
NOTICE: in process utility: superuser attempting set
-NOTICE: in object_access_hook_str: non-superuser attempting alter (set) [session_authorization]
+NOTICE: in object_access_hook_str: non-superuser attempting alter (subId=0x1000, set) [session_authorization]
ERROR: permission denied: set session_authorization
SELECT * FROM regress_test_table;
-NOTICE: in object access: superuser attempting namespace search (subId=0) [no report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [no report on violation, allowed]
LINE 1: SELECT * FROM regress_test_table;
^
-NOTICE: in object access: superuser finished namespace search (subId=0) [no report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [no report on violation, allowed]
LINE 1: SELECT * FROM regress_test_table;
^
NOTICE: in executor check perms: superuser attempting execute
@@ -183,28 +259,43 @@ NOTICE: in executor check perms: superuser finished execute
SET work_mem = 8192;
NOTICE: in process utility: superuser attempting set
-NOTICE: in object_access_hook_str: superuser attempting alter (set) [work_mem]
-NOTICE: in object_access_hook_str: superuser finished alter (set) [work_mem]
+NOTICE: in object_access_hook_str: superuser attempting alter (subId=0x1000, set) [work_mem]
+NOTICE: in object_access_hook_str: superuser finished alter (subId=0x1000, set) [work_mem]
NOTICE: in process utility: superuser finished set
RESET work_mem;
NOTICE: in process utility: superuser attempting set
-NOTICE: in object_access_hook_str: superuser attempting alter (set) [work_mem]
-NOTICE: in object_access_hook_str: superuser finished alter (set) [work_mem]
+NOTICE: in object_access_hook_str: superuser attempting alter (subId=0x1000, set) [work_mem]
+NOTICE: in object_access_hook_str: superuser finished alter (subId=0x1000, set) [work_mem]
NOTICE: in process utility: superuser finished set
ALTER SYSTEM SET work_mem = 8192;
NOTICE: in process utility: superuser attempting alter system
-NOTICE: in object_access_hook_str: superuser attempting alter (alter system set) [work_mem]
-NOTICE: in object_access_hook_str: superuser finished alter (alter system set) [work_mem]
+NOTICE: in object_access_hook_str: superuser attempting alter (subId=0x2000, alter system) [work_mem]
+NOTICE: in object_access_hook_str: superuser finished alter (subId=0x2000, alter system) [work_mem]
NOTICE: in process utility: superuser finished alter system
ALTER SYSTEM RESET work_mem;
NOTICE: in process utility: superuser attempting alter system
-NOTICE: in object_access_hook_str: superuser attempting alter (alter system set) [work_mem]
-NOTICE: in object_access_hook_str: superuser finished alter (alter system set) [work_mem]
+NOTICE: in object_access_hook_str: superuser attempting alter (subId=0x2000, alter system) [work_mem]
+NOTICE: in object_access_hook_str: superuser finished alter (subId=0x2000, alter system) [work_mem]
NOTICE: in process utility: superuser finished alter system
+-- Clean up
RESET SESSION AUTHORIZATION;
NOTICE: in process utility: superuser attempting set
-NOTICE: in object_access_hook_str: superuser attempting alter (set) [session_authorization]
-NOTICE: in object_access_hook_str: superuser finished alter (set) [session_authorization]
+NOTICE: in object_access_hook_str: superuser attempting alter (subId=0x1000, set) [session_authorization]
+NOTICE: in object_access_hook_str: superuser finished alter (subId=0x1000, set) [session_authorization]
NOTICE: in process utility: superuser finished set
SET test_oat_hooks.audit = false;
NOTICE: in process utility: superuser attempting set
+DROP ROLE regress_role_joe; -- fails
+ERROR: role "regress_role_joe" cannot be dropped because some objects depend on it
+DETAIL: privileges for parameter test_oat_hooks.user_var1
+privileges for parameter test_oat_hooks.super_var1
+privileges for parameter test_oat_hooks.user_var2
+privileges for parameter test_oat_hooks.super_var2
+privileges for parameter none.such
+privileges for parameter another.bogus
+REVOKE ALL PRIVILEGES ON PARAMETER
+ none.such, another.bogus,
+ test_oat_hooks.user_var1, test_oat_hooks.super_var1,
+ test_oat_hooks.user_var2, test_oat_hooks.super_var2
+ FROM regress_role_joe;
+DROP ROLE regress_role_joe;
diff --git a/src/test/modules/test_oat_hooks/sql/test_oat_hooks.sql b/src/test/modules/test_oat_hooks/sql/test_oat_hooks.sql
index 09e61864ee2..8b6d5373aa5 100644
--- a/src/test/modules/test_oat_hooks/sql/test_oat_hooks.sql
+++ b/src/test/modules/test_oat_hooks/sql/test_oat_hooks.sql
@@ -1,10 +1,39 @@
+-- Creating privileges on a placeholder GUC should create entries in the
+-- pg_parameter_acl catalog which conservatively grant no privileges to public.
+CREATE ROLE regress_role_joe;
+GRANT SET ON PARAMETER test_oat_hooks.user_var1 TO regress_role_joe;
+GRANT SET ON PARAMETER test_oat_hooks.super_var1 TO regress_role_joe;
+
-- SET commands fire both the ProcessUtility_hook and the
-- object_access_hook_str. Since the auditing GUC starts out false, we miss the
-- initial "attempting" audit message from the ProcessUtility_hook, but we
--- should thereafter see the audit messages
+-- should thereafter see the audit messages.
LOAD 'test_oat_hooks';
SET test_oat_hooks.audit = true;
+-- Creating privileges on an existent custom GUC should create precisely the
+-- right privileges, not overly conservative ones.
+GRANT SET ON PARAMETER test_oat_hooks.user_var2 TO regress_role_joe;
+GRANT SET ON PARAMETER test_oat_hooks.super_var2 TO regress_role_joe;
+
+-- Granting multiple privileges on a parameter should be reported correctly to
+-- the OAT hook, but beware that WITH GRANT OPTION is not represented.
+GRANT SET, ALTER SYSTEM ON PARAMETER none.such TO regress_role_joe;
+GRANT SET, ALTER SYSTEM ON PARAMETER another.bogus TO regress_role_joe WITH GRANT OPTION;
+
+-- Check when the hooks fire relative to dependency based abort of a drop
+DROP ROLE regress_role_joe;
+
+-- Check the behavior of the hooks relative to do-nothing grants and revokes
+GRANT SET ON PARAMETER work_mem TO PUBLIC;
+REVOKE ALTER SYSTEM ON PARAMETER work_mem FROM PUBLIC;
+
+-- Check the behavior of the hooks relative to unrecognized parameters
+GRANT ALL ON PARAMETER "none.such" TO PUBLIC;
+
+-- Check relative to an operation that causes the catalog entry to be deleted
+REVOKE ALL ON PARAMETER "none.such" FROM PUBLIC;
+
-- Create objects for use in the test
CREATE USER regress_test_user;
CREATE TABLE regress_test_table (t text);
@@ -30,6 +59,16 @@ SET work_mem = 8192;
RESET work_mem;
ALTER SYSTEM SET work_mem = 8192;
ALTER SYSTEM RESET work_mem;
+
+SET test_oat_hooks.user_var1 = true;
+SET test_oat_hooks.super_var1 = true;
+ALTER SYSTEM SET test_oat_hooks.user_var1 = true;
+ALTER SYSTEM SET test_oat_hooks.super_var1 = true;
+SET test_oat_hooks.user_var2 = true;
+SET test_oat_hooks.super_var2 = true;
+ALTER SYSTEM SET test_oat_hooks.user_var2 = true;
+ALTER SYSTEM SET test_oat_hooks.super_var2 = true;
+
RESET SESSION AUTHORIZATION;
-- Turn off non-superuser permissions
@@ -48,6 +87,14 @@ RESET work_mem;
ALTER SYSTEM SET work_mem = 8192;
ALTER SYSTEM RESET work_mem;
+-- Clean up
RESET SESSION AUTHORIZATION;
SET test_oat_hooks.audit = false;
+DROP ROLE regress_role_joe; -- fails
+REVOKE ALL PRIVILEGES ON PARAMETER
+ none.such, another.bogus,
+ test_oat_hooks.user_var1, test_oat_hooks.super_var1,
+ test_oat_hooks.user_var2, test_oat_hooks.super_var2
+ FROM regress_role_joe;
+DROP ROLE regress_role_joe;
diff --git a/src/test/modules/test_oat_hooks/test_oat_hooks.c b/src/test/modules/test_oat_hooks/test_oat_hooks.c
index eb7564ce22e..551da5d4986 100644
--- a/src/test/modules/test_oat_hooks/test_oat_hooks.c
+++ b/src/test/modules/test_oat_hooks/test_oat_hooks.c
@@ -34,6 +34,15 @@ static bool REGRESS_deny_exec_perms = false;
static bool REGRESS_deny_utility_commands = false;
static bool REGRESS_audit = false;
+/*
+ * GUCs for testing privileges on USERSET and SUSET variables,
+ * with and without privileges granted prior to module load.
+ */
+static bool REGRESS_userset_variable1 = false;
+static bool REGRESS_userset_variable2 = false;
+static bool REGRESS_suset_variable1 = false;
+static bool REGRESS_suset_variable2 = false;
+
/* Saved hook values in case of unload */
static object_access_hook_type next_object_access_hook = NULL;
static object_access_hook_type_str next_object_access_hook_str = NULL;
@@ -153,6 +162,56 @@ _PG_init(void)
NULL,
NULL);
+ /*
+ * test_oat_hooks.user_var{1,2} = (on|off)
+ */
+ DefineCustomBoolVariable("test_oat_hooks.user_var1",
+ "Dummy parameter settable by public",
+ NULL,
+ &REGRESS_userset_variable1,
+ false,
+ PGC_USERSET,
+ GUC_NOT_IN_SAMPLE,
+ NULL,
+ NULL,
+ NULL);
+
+ DefineCustomBoolVariable("test_oat_hooks.user_var2",
+ "Dummy parameter settable by public",
+ NULL,
+ &REGRESS_userset_variable2,
+ false,
+ PGC_USERSET,
+ GUC_NOT_IN_SAMPLE,
+ NULL,
+ NULL,
+ NULL);
+
+ /*
+ * test_oat_hooks.super_var{1,2} = (on|off)
+ */
+ DefineCustomBoolVariable("test_oat_hooks.super_var1",
+ "Dummy parameter settable by superuser",
+ NULL,
+ &REGRESS_suset_variable1,
+ false,
+ PGC_SUSET,
+ GUC_NOT_IN_SAMPLE,
+ NULL,
+ NULL,
+ NULL);
+
+ DefineCustomBoolVariable("test_oat_hooks.super_var2",
+ "Dummy parameter settable by superuser",
+ NULL,
+ &REGRESS_suset_variable2,
+ false,
+ PGC_SUSET,
+ GUC_NOT_IN_SAMPLE,
+ NULL,
+ NULL,
+ NULL);
+
MarkGUCPrefixReserved("test_oat_hooks");
/* Object access hook */
@@ -250,7 +309,14 @@ REGRESS_object_access_hook_str(ObjectAccessType access, Oid classId, const char
switch (access)
{
case OAT_POST_ALTER:
- if (subId & ACL_SET_VALUE)
+ if ((subId & ACL_SET) && (subId & ACL_ALTER_SYSTEM))
+ {
+ if (REGRESS_deny_set_variable && !superuser_arg(GetUserId()))
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied: all privileges %s", objName)));
+ }
+ else if (subId & ACL_SET)
{
if (REGRESS_deny_set_variable && !superuser_arg(GetUserId()))
ereport(ERROR,
@@ -265,7 +331,7 @@ REGRESS_object_access_hook_str(ObjectAccessType access, Oid classId, const char
errmsg("permission denied: alter system set %s", objName)));
}
else
- elog(ERROR, "Unknown SettingAclRelationId subId: %d", subId);
+ elog(ERROR, "Unknown ParameterAclRelationId subId: %d", subId);
break;
default:
break;
@@ -860,12 +926,14 @@ accesstype_to_string(ObjectAccessType access, int subId)
type = "UNRECOGNIZED ObjectAccessType";
}
- if (subId & ACL_SET_VALUE)
- return psprintf("%s (set)", type);
+ if ((subId & ACL_SET) && (subId & ACL_ALTER_SYSTEM))
+ return psprintf("%s (subId=0x%x, all privileges)", type, subId);
+ if (subId & ACL_SET)
+ return psprintf("%s (subId=0x%x, set)", type, subId);
if (subId & ACL_ALTER_SYSTEM)
- return psprintf("%s (alter system set)", type);
+ return psprintf("%s (subId=0x%x, alter system)", type, subId);
- return psprintf("%s (subId=%d)", type, subId);
+ return psprintf("%s (subId=0x%x)", type, subId);
}
static char *
diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl
index 84a35590b75..d842f934a3a 100644
--- a/src/test/modules/test_pg_dump/t/001_base.pl
+++ b/src/test/modules/test_pg_dump/t/001_base.pl
@@ -316,6 +316,38 @@ my %tests = (
like => { pg_dumpall_globals => 1, },
},
+ 'GRANT ALTER SYSTEM ON PARAMETER full_page_writes TO regress_dump_test_role'
+ => {
+ create_order => 2,
+ create_sql =>
+ 'GRANT ALTER SYSTEM ON PARAMETER full_page_writes TO regress_dump_test_role;',
+ regexp =>
+
+ qr/^GRANT ALTER SYSTEM ON PARAMETER full_page_writes TO regress_dump_test_role;/m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
+ 'GRANT ALL ON PARAMETER Custom.Knob TO regress_dump_test_role WITH GRANT OPTION'
+ => {
+ create_order => 2,
+ create_sql =>
+ 'GRANT SET, ALTER SYSTEM ON PARAMETER Custom.Knob TO regress_dump_test_role WITH GRANT OPTION;',
+ regexp =>
+ # "set" plus "alter system" is "all" privileges on parameters
+ qr/^GRANT ALL ON PARAMETER "custom.knob" TO regress_dump_test_role WITH GRANT OPTION;/m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
+ 'GRANT ALL ON PARAMETER DateStyle TO regress_dump_test_role' => {
+ create_order => 2,
+ create_sql =>
+ 'GRANT ALL ON PARAMETER "DateStyle" TO regress_dump_test_role WITH GRANT OPTION; REVOKE GRANT OPTION FOR ALL ON PARAMETER DateStyle FROM regress_dump_test_role;',
+ regexp =>
+ # The revoke simplifies the ultimate grant so as to not include "with grant option"
+ qr/^GRANT ALL ON PARAMETER datestyle TO regress_dump_test_role;/m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
'CREATE SCHEMA public' => {
regexp => qr/^CREATE SCHEMA public;/m,
like => {
diff --git a/src/test/modules/unsafe_tests/Makefile b/src/test/modules/unsafe_tests/Makefile
index 3ecf5fcfc5b..df582736884 100644
--- a/src/test/modules/unsafe_tests/Makefile
+++ b/src/test/modules/unsafe_tests/Makefile
@@ -1,6 +1,6 @@
# src/test/modules/unsafe_tests/Makefile
-REGRESS = rolenames alter_system_table
+REGRESS = rolenames alter_system_table guc_privs
ifdef USE_PGXS
PG_CONFIG = pg_config
diff --git a/src/test/modules/unsafe_tests/expected/guc_privs.out b/src/test/modules/unsafe_tests/expected/guc_privs.out
new file mode 100644
index 00000000000..58dc87f958e
--- /dev/null
+++ b/src/test/modules/unsafe_tests/expected/guc_privs.out
@@ -0,0 +1,526 @@
+--
+-- Tests for privileges on GUCs.
+-- This is unsafe because changes will affect other databases in the cluster.
+--
+-- Test with a superuser role.
+CREATE ROLE regress_admin SUPERUSER;
+-- Perform operations as user 'regress_admin'.
+SET SESSION AUTHORIZATION regress_admin;
+-- PGC_BACKEND
+SET ignore_system_indexes = OFF; -- fail, cannot be set after connection start
+ERROR: parameter "ignore_system_indexes" cannot be set after connection start
+RESET ignore_system_indexes; -- fail, cannot be set after connection start
+ERROR: parameter "ignore_system_indexes" cannot be set after connection start
+ALTER SYSTEM SET ignore_system_indexes = OFF; -- ok
+ALTER SYSTEM RESET ignore_system_indexes; -- ok
+-- PGC_INTERNAL
+SET block_size = 50; -- fail, cannot be changed
+ERROR: parameter "block_size" cannot be changed
+RESET block_size; -- fail, cannot be changed
+ERROR: parameter "block_size" cannot be changed
+ALTER SYSTEM SET block_size = 50; -- fail, cannot be changed
+ERROR: parameter "block_size" cannot be changed
+ALTER SYSTEM RESET block_size; -- fail, cannot be changed
+ERROR: parameter "block_size" cannot be changed
+-- PGC_POSTMASTER
+SET autovacuum_freeze_max_age = 1000050000; -- fail, requires restart
+ERROR: parameter "autovacuum_freeze_max_age" cannot be changed without restarting the server
+RESET autovacuum_freeze_max_age; -- fail, requires restart
+ERROR: parameter "autovacuum_freeze_max_age" cannot be changed without restarting the server
+ALTER SYSTEM SET autovacuum_freeze_max_age = 1000050000; -- ok
+ALTER SYSTEM RESET autovacuum_freeze_max_age; -- ok
+ALTER SYSTEM SET config_file = '/usr/local/data/postgresql.conf'; -- fail, cannot be changed
+ERROR: parameter "config_file" cannot be changed
+ALTER SYSTEM RESET config_file; -- fail, cannot be changed
+ERROR: parameter "config_file" cannot be changed
+-- PGC_SIGHUP
+SET autovacuum = OFF; -- fail, requires reload
+ERROR: parameter "autovacuum" cannot be changed now
+RESET autovacuum; -- fail, requires reload
+ERROR: parameter "autovacuum" cannot be changed now
+ALTER SYSTEM SET autovacuum = OFF; -- ok
+ALTER SYSTEM RESET autovacuum; -- ok
+-- PGC_SUSET
+SET lc_messages = 'C'; -- ok
+RESET lc_messages; -- ok
+ALTER SYSTEM SET lc_messages = 'C'; -- ok
+ALTER SYSTEM RESET lc_messages; -- ok
+-- PGC_SU_BACKEND
+SET jit_debugging_support = OFF; -- fail, cannot be set after connection start
+ERROR: parameter "jit_debugging_support" cannot be set after connection start
+RESET jit_debugging_support; -- fail, cannot be set after connection start
+ERROR: parameter "jit_debugging_support" cannot be set after connection start
+ALTER SYSTEM SET jit_debugging_support = OFF; -- ok
+ALTER SYSTEM RESET jit_debugging_support; -- ok
+-- PGC_USERSET
+SET DateStyle = 'ISO, MDY'; -- ok
+RESET DateStyle; -- ok
+ALTER SYSTEM SET DateStyle = 'ISO, MDY'; -- ok
+ALTER SYSTEM RESET DateStyle; -- ok
+ALTER SYSTEM SET ssl_renegotiation_limit = 0; -- fail, cannot be changed
+ERROR: parameter "ssl_renegotiation_limit" cannot be changed
+ALTER SYSTEM RESET ssl_renegotiation_limit; -- fail, cannot be changed
+ERROR: parameter "ssl_renegotiation_limit" cannot be changed
+-- Finished testing superuser
+-- Create non-superuser with privileges to configure host resource usage
+CREATE ROLE regress_host_resource_admin NOSUPERUSER;
+-- Revoke privileges not yet granted
+REVOKE SET, ALTER SYSTEM ON PARAMETER work_mem FROM regress_host_resource_admin;
+REVOKE SET, ALTER SYSTEM ON PARAMETER zero_damaged_pages FROM regress_host_resource_admin;
+-- Check the new role does not yet have privileges on parameters
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SET, ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SET');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+-- Check inappropriate and nonsense privilege types
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SELECT, UPDATE, CREATE');
+ERROR: unrecognized privilege type: "SELECT"
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'USAGE');
+ERROR: unrecognized privilege type: "USAGE"
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER');
+ERROR: unrecognized privilege type: "WHATEVER"
+-- Revoke, grant, and revoke again a SUSET parameter not yet granted
+SELECT has_parameter_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'SET');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+REVOKE SET ON PARAMETER zero_damaged_pages FROM regress_host_resource_admin;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'SET');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+GRANT SET ON PARAMETER zero_damaged_pages TO regress_host_resource_admin;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'SET');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+REVOKE SET ON PARAMETER zero_damaged_pages FROM regress_host_resource_admin;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'SET');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+-- Revoke, grant, and revoke again a USERSET parameter not yet granted
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SET');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+REVOKE SET ON PARAMETER work_mem FROM regress_host_resource_admin;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SET');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+GRANT SET ON PARAMETER work_mem TO regress_host_resource_admin;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SET');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+REVOKE SET ON PARAMETER work_mem FROM regress_host_resource_admin;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SET');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+-- Revoke privileges from a non-existent custom GUC. This should not create
+-- entries in the catalog.
+REVOKE ALL ON PARAMETER "none.such" FROM regress_host_resource_admin;
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'none.such';
+ ?column?
+----------
+(0 rows)
+
+-- Grant and then revoke privileges on the non-existent custom GUC. Check that
+-- a do-nothing entry is not left in the catalogs after the revoke.
+GRANT ALL ON PARAMETER none.such TO regress_host_resource_admin;
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'none.such';
+ ?column?
+----------
+ 1
+(1 row)
+
+REVOKE ALL ON PARAMETER "None.Such" FROM regress_host_resource_admin;
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'none.such';
+ ?column?
+----------
+(0 rows)
+
+-- Can't grant on a non-existent core GUC.
+GRANT ALL ON PARAMETER no_such_guc TO regress_host_resource_admin; -- fail
+ERROR: invalid parameter name "no_such_guc"
+-- Initially there are no privileges and no catalog entry for this GUC.
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET, ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'enable_material';
+ ?column?
+----------
+(0 rows)
+
+-- GRANT SET creates an entry:
+GRANT SET ON PARAMETER enable_material TO PUBLIC;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET, ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'enable_material';
+ ?column?
+----------
+ 1
+(1 row)
+
+-- Now grant ALTER SYSTEM:
+GRANT ALL ON PARAMETER enable_material TO PUBLIC;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET, ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'enable_material';
+ ?column?
+----------
+ 1
+(1 row)
+
+-- REVOKE ALTER SYSTEM brings us back to just the SET privilege:
+REVOKE ALTER SYSTEM ON PARAMETER enable_material FROM PUBLIC;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET, ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'enable_material';
+ ?column?
+----------
+ 1
+(1 row)
+
+-- And this should remove the entry altogether:
+REVOKE SET ON PARAMETER enable_material FROM PUBLIC;
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'enable_material';
+ ?column?
+----------
+(0 rows)
+
+-- Grant privileges on parameters to the new non-superuser role
+GRANT SET, ALTER SYSTEM ON PARAMETER
+ autovacuum_work_mem, hash_mem_multiplier, max_stack_depth,
+ shared_buffers, temp_file_limit, work_mem
+TO regress_host_resource_admin;
+-- Check the new role now has privilges on parameters
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SET, ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SET');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SET WITH GRANT OPTION, ALTER SYSTEM WITH GRANT OPTION');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+-- Check again the inappropriate and nonsense privilege types. The prior
+-- similar check was performed before any entry for work_mem existed.
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SELECT, UPDATE, CREATE');
+ERROR: unrecognized privilege type: "SELECT"
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'USAGE');
+ERROR: unrecognized privilege type: "USAGE"
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER');
+ERROR: unrecognized privilege type: "WHATEVER"
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER WITH GRANT OPTION');
+ERROR: unrecognized privilege type: "WHATEVER WITH GRANT OPTION"
+-- Check other function signatures
+SELECT has_parameter_privilege((SELECT oid FROM pg_catalog.pg_authid WHERE rolname = 'regress_host_resource_admin'),
+ 'max_stack_depth',
+ 'SET');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT has_parameter_privilege('hash_mem_multiplier', 'set');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+-- Check object identity functions
+SELECT pg_describe_object(tableoid, oid, 0)
+FROM pg_parameter_acl WHERE parname = 'work_mem';
+ pg_describe_object
+--------------------
+ parameter work_mem
+(1 row)
+
+SELECT pg_identify_object(tableoid, oid, 0)
+FROM pg_parameter_acl WHERE parname = 'work_mem';
+ pg_identify_object
+------------------------------
+ ("parameter ACL",,,work_mem)
+(1 row)
+
+SELECT pg_identify_object_as_address(tableoid, oid, 0)
+FROM pg_parameter_acl WHERE parname = 'work_mem';
+ pg_identify_object_as_address
+---------------------------------
+ ("parameter ACL",{work_mem},{})
+(1 row)
+
+SELECT classid::regclass,
+ (SELECT parname FROM pg_parameter_acl WHERE oid = goa.objid) AS parname,
+ objsubid
+FROM pg_get_object_address('parameter ACL', '{work_mem}', '{}') goa;
+ classid | parname | objsubid
+------------------+----------+----------
+ pg_parameter_acl | work_mem | 0
+(1 row)
+
+-- Perform some operations as user 'regress_host_resource_admin'
+SET SESSION AUTHORIZATION regress_host_resource_admin;
+ALTER SYSTEM SET autovacuum_work_mem = 32; -- ok, privileges have been granted
+ALTER SYSTEM SET ignore_system_indexes = OFF; -- fail, insufficient privileges
+ERROR: permission denied to set parameter "ignore_system_indexes"
+ALTER SYSTEM RESET autovacuum_multixact_freeze_max_age; -- fail, insufficient privileges
+ERROR: permission denied to set parameter "autovacuum_multixact_freeze_max_age"
+SET jit_provider = 'llvmjit'; -- fail, insufficient privileges
+ERROR: parameter "jit_provider" cannot be changed without restarting the server
+SELECT set_config ('jit_provider', 'llvmjit', true); -- fail, insufficient privileges
+ERROR: parameter "jit_provider" cannot be changed without restarting the server
+ALTER SYSTEM SET shared_buffers = 50; -- ok
+ALTER SYSTEM RESET shared_buffers; -- ok
+SET autovacuum_work_mem = 50; -- cannot be changed now
+ERROR: parameter "autovacuum_work_mem" cannot be changed now
+ALTER SYSTEM RESET temp_file_limit; -- ok
+SET TimeZone = 'Europe/Helsinki'; -- ok
+RESET TimeZone; -- ok
+SET max_stack_depth = 2048; -- ok, privileges have been granted
+RESET max_stack_depth; -- ok, privileges have been granted
+ALTER SYSTEM SET max_stack_depth = 2048; -- ok, privileges have been granted
+ALTER SYSTEM RESET max_stack_depth; -- ok, privileges have been granted
+SET lc_messages = 'C'; -- fail, insufficient privileges
+ERROR: permission denied to set parameter "lc_messages"
+RESET lc_messages; -- fail, insufficient privileges
+ERROR: permission denied to set parameter "lc_messages"
+ALTER SYSTEM SET lc_messages = 'C'; -- fail, insufficient privileges
+ERROR: permission denied to set parameter "lc_messages"
+ALTER SYSTEM RESET lc_messages; -- fail, insufficient privileges
+ERROR: permission denied to set parameter "lc_messages"
+SELECT set_config ('temp_buffers', '8192', false); -- ok
+ set_config
+------------
+ 64MB
+(1 row)
+
+ALTER SYSTEM RESET autovacuum_work_mem; -- ok, privileges have been granted
+ALTER SYSTEM RESET ALL; -- fail, insufficient privileges
+ERROR: permission denied to perform ALTER SYSTEM RESET ALL
+-- Check dropping/revoking behavior
+SET SESSION AUTHORIZATION regress_admin;
+DROP ROLE regress_host_resource_admin; -- fail, privileges remain
+ERROR: role "regress_host_resource_admin" cannot be dropped because some objects depend on it
+DETAIL: privileges for parameter autovacuum_work_mem
+privileges for parameter hash_mem_multiplier
+privileges for parameter max_stack_depth
+privileges for parameter shared_buffers
+privileges for parameter temp_file_limit
+privileges for parameter work_mem
+-- Use "revoke" to remove the privileges and allow the role to be dropped
+REVOKE SET, ALTER SYSTEM ON PARAMETER
+ autovacuum_work_mem, hash_mem_multiplier, max_stack_depth,
+ shared_buffers, temp_file_limit, work_mem
+FROM regress_host_resource_admin;
+DROP ROLE regress_host_resource_admin; -- ok
+-- Try that again, but use "drop owned by" instead of "revoke"
+CREATE ROLE regress_host_resource_admin NOSUPERUSER;
+SET SESSION AUTHORIZATION regress_host_resource_admin;
+ALTER SYSTEM SET autovacuum_work_mem = 32; -- fail, privileges not yet granted
+ERROR: permission denied to set parameter "autovacuum_work_mem"
+SET SESSION AUTHORIZATION regress_admin;
+GRANT SET, ALTER SYSTEM ON PARAMETER
+ autovacuum_work_mem, hash_mem_multiplier, max_stack_depth,
+ shared_buffers, temp_file_limit, work_mem
+TO regress_host_resource_admin;
+DROP ROLE regress_host_resource_admin; -- fail, privileges remain
+ERROR: role "regress_host_resource_admin" cannot be dropped because some objects depend on it
+DETAIL: privileges for parameter autovacuum_work_mem
+privileges for parameter hash_mem_multiplier
+privileges for parameter max_stack_depth
+privileges for parameter shared_buffers
+privileges for parameter temp_file_limit
+privileges for parameter work_mem
+DROP OWNED BY regress_host_resource_admin RESTRICT; -- cascade should not be needed
+SET SESSION AUTHORIZATION regress_host_resource_admin;
+ALTER SYSTEM SET autovacuum_work_mem = 32; -- fail, "drop owned" has dropped privileges
+ERROR: permission denied to set parameter "autovacuum_work_mem"
+SET SESSION AUTHORIZATION regress_admin;
+DROP ROLE regress_host_resource_admin; -- ok
+-- Check that "reassign owned" doesn't affect privileges
+CREATE ROLE regress_host_resource_admin NOSUPERUSER;
+CREATE ROLE regress_host_resource_newadmin NOSUPERUSER;
+GRANT SET, ALTER SYSTEM ON PARAMETER
+ autovacuum_work_mem, hash_mem_multiplier, max_stack_depth,
+ shared_buffers, temp_file_limit, work_mem
+TO regress_host_resource_admin;
+REASSIGN OWNED BY regress_host_resource_admin TO regress_host_resource_newadmin;
+SET SESSION AUTHORIZATION regress_host_resource_admin;
+ALTER SYSTEM SET autovacuum_work_mem = 32; -- ok, "reassign owned" did not change privileges
+ALTER SYSTEM RESET autovacuum_work_mem; -- ok
+SET SESSION AUTHORIZATION regress_admin;
+DROP ROLE regress_host_resource_admin; -- fail, privileges remain
+ERROR: role "regress_host_resource_admin" cannot be dropped because some objects depend on it
+DETAIL: privileges for parameter autovacuum_work_mem
+privileges for parameter hash_mem_multiplier
+privileges for parameter max_stack_depth
+privileges for parameter shared_buffers
+privileges for parameter temp_file_limit
+privileges for parameter work_mem
+DROP ROLE regress_host_resource_newadmin; -- ok, nothing was transferred
+-- Use "drop owned by" so we can drop the role
+DROP OWNED BY regress_host_resource_admin; -- ok
+DROP ROLE regress_host_resource_admin; -- ok
+-- Clean up
+RESET SESSION AUTHORIZATION;
+DROP ROLE regress_admin; -- ok
diff --git a/src/test/modules/unsafe_tests/sql/guc_privs.sql b/src/test/modules/unsafe_tests/sql/guc_privs.sql
new file mode 100644
index 00000000000..12b22548f06
--- /dev/null
+++ b/src/test/modules/unsafe_tests/sql/guc_privs.sql
@@ -0,0 +1,237 @@
+--
+-- Tests for privileges on GUCs.
+-- This is unsafe because changes will affect other databases in the cluster.
+--
+
+-- Test with a superuser role.
+CREATE ROLE regress_admin SUPERUSER;
+
+-- Perform operations as user 'regress_admin'.
+SET SESSION AUTHORIZATION regress_admin;
+
+-- PGC_BACKEND
+SET ignore_system_indexes = OFF; -- fail, cannot be set after connection start
+RESET ignore_system_indexes; -- fail, cannot be set after connection start
+ALTER SYSTEM SET ignore_system_indexes = OFF; -- ok
+ALTER SYSTEM RESET ignore_system_indexes; -- ok
+-- PGC_INTERNAL
+SET block_size = 50; -- fail, cannot be changed
+RESET block_size; -- fail, cannot be changed
+ALTER SYSTEM SET block_size = 50; -- fail, cannot be changed
+ALTER SYSTEM RESET block_size; -- fail, cannot be changed
+-- PGC_POSTMASTER
+SET autovacuum_freeze_max_age = 1000050000; -- fail, requires restart
+RESET autovacuum_freeze_max_age; -- fail, requires restart
+ALTER SYSTEM SET autovacuum_freeze_max_age = 1000050000; -- ok
+ALTER SYSTEM RESET autovacuum_freeze_max_age; -- ok
+ALTER SYSTEM SET config_file = '/usr/local/data/postgresql.conf'; -- fail, cannot be changed
+ALTER SYSTEM RESET config_file; -- fail, cannot be changed
+-- PGC_SIGHUP
+SET autovacuum = OFF; -- fail, requires reload
+RESET autovacuum; -- fail, requires reload
+ALTER SYSTEM SET autovacuum = OFF; -- ok
+ALTER SYSTEM RESET autovacuum; -- ok
+-- PGC_SUSET
+SET lc_messages = 'C'; -- ok
+RESET lc_messages; -- ok
+ALTER SYSTEM SET lc_messages = 'C'; -- ok
+ALTER SYSTEM RESET lc_messages; -- ok
+-- PGC_SU_BACKEND
+SET jit_debugging_support = OFF; -- fail, cannot be set after connection start
+RESET jit_debugging_support; -- fail, cannot be set after connection start
+ALTER SYSTEM SET jit_debugging_support = OFF; -- ok
+ALTER SYSTEM RESET jit_debugging_support; -- ok
+-- PGC_USERSET
+SET DateStyle = 'ISO, MDY'; -- ok
+RESET DateStyle; -- ok
+ALTER SYSTEM SET DateStyle = 'ISO, MDY'; -- ok
+ALTER SYSTEM RESET DateStyle; -- ok
+ALTER SYSTEM SET ssl_renegotiation_limit = 0; -- fail, cannot be changed
+ALTER SYSTEM RESET ssl_renegotiation_limit; -- fail, cannot be changed
+-- Finished testing superuser
+
+-- Create non-superuser with privileges to configure host resource usage
+CREATE ROLE regress_host_resource_admin NOSUPERUSER;
+-- Revoke privileges not yet granted
+REVOKE SET, ALTER SYSTEM ON PARAMETER work_mem FROM regress_host_resource_admin;
+REVOKE SET, ALTER SYSTEM ON PARAMETER zero_damaged_pages FROM regress_host_resource_admin;
+-- Check the new role does not yet have privileges on parameters
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SET, ALTER SYSTEM');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SET');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+-- Check inappropriate and nonsense privilege types
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SELECT, UPDATE, CREATE');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'USAGE');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER');
+-- Revoke, grant, and revoke again a SUSET parameter not yet granted
+SELECT has_parameter_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'SET');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'ALTER SYSTEM');
+REVOKE SET ON PARAMETER zero_damaged_pages FROM regress_host_resource_admin;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'SET');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'ALTER SYSTEM');
+GRANT SET ON PARAMETER zero_damaged_pages TO regress_host_resource_admin;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'SET');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'ALTER SYSTEM');
+REVOKE SET ON PARAMETER zero_damaged_pages FROM regress_host_resource_admin;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'SET');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'ALTER SYSTEM');
+-- Revoke, grant, and revoke again a USERSET parameter not yet granted
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SET');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+REVOKE SET ON PARAMETER work_mem FROM regress_host_resource_admin;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SET');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+GRANT SET ON PARAMETER work_mem TO regress_host_resource_admin;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SET');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+REVOKE SET ON PARAMETER work_mem FROM regress_host_resource_admin;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SET');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+
+-- Revoke privileges from a non-existent custom GUC. This should not create
+-- entries in the catalog.
+REVOKE ALL ON PARAMETER "none.such" FROM regress_host_resource_admin;
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'none.such';
+-- Grant and then revoke privileges on the non-existent custom GUC. Check that
+-- a do-nothing entry is not left in the catalogs after the revoke.
+GRANT ALL ON PARAMETER none.such TO regress_host_resource_admin;
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'none.such';
+REVOKE ALL ON PARAMETER "None.Such" FROM regress_host_resource_admin;
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'none.such';
+-- Can't grant on a non-existent core GUC.
+GRANT ALL ON PARAMETER no_such_guc TO regress_host_resource_admin; -- fail
+
+-- Initially there are no privileges and no catalog entry for this GUC.
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'ALTER SYSTEM');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET, ALTER SYSTEM');
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'enable_material';
+-- GRANT SET creates an entry:
+GRANT SET ON PARAMETER enable_material TO PUBLIC;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'ALTER SYSTEM');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET, ALTER SYSTEM');
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'enable_material';
+-- Now grant ALTER SYSTEM:
+GRANT ALL ON PARAMETER enable_material TO PUBLIC;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'ALTER SYSTEM');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET, ALTER SYSTEM');
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'enable_material';
+-- REVOKE ALTER SYSTEM brings us back to just the SET privilege:
+REVOKE ALTER SYSTEM ON PARAMETER enable_material FROM PUBLIC;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'ALTER SYSTEM');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET, ALTER SYSTEM');
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'enable_material';
+-- And this should remove the entry altogether:
+REVOKE SET ON PARAMETER enable_material FROM PUBLIC;
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'enable_material';
+
+-- Grant privileges on parameters to the new non-superuser role
+GRANT SET, ALTER SYSTEM ON PARAMETER
+ autovacuum_work_mem, hash_mem_multiplier, max_stack_depth,
+ shared_buffers, temp_file_limit, work_mem
+TO regress_host_resource_admin;
+-- Check the new role now has privilges on parameters
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SET, ALTER SYSTEM');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SET');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SET WITH GRANT OPTION, ALTER SYSTEM WITH GRANT OPTION');
+-- Check again the inappropriate and nonsense privilege types. The prior
+-- similar check was performed before any entry for work_mem existed.
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SELECT, UPDATE, CREATE');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'USAGE');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER WITH GRANT OPTION');
+
+-- Check other function signatures
+SELECT has_parameter_privilege((SELECT oid FROM pg_catalog.pg_authid WHERE rolname = 'regress_host_resource_admin'),
+ 'max_stack_depth',
+ 'SET');
+SELECT has_parameter_privilege('hash_mem_multiplier', 'set');
+
+-- Check object identity functions
+SELECT pg_describe_object(tableoid, oid, 0)
+FROM pg_parameter_acl WHERE parname = 'work_mem';
+SELECT pg_identify_object(tableoid, oid, 0)
+FROM pg_parameter_acl WHERE parname = 'work_mem';
+SELECT pg_identify_object_as_address(tableoid, oid, 0)
+FROM pg_parameter_acl WHERE parname = 'work_mem';
+SELECT classid::regclass,
+ (SELECT parname FROM pg_parameter_acl WHERE oid = goa.objid) AS parname,
+ objsubid
+FROM pg_get_object_address('parameter ACL', '{work_mem}', '{}') goa;
+
+-- Perform some operations as user 'regress_host_resource_admin'
+SET SESSION AUTHORIZATION regress_host_resource_admin;
+ALTER SYSTEM SET autovacuum_work_mem = 32; -- ok, privileges have been granted
+ALTER SYSTEM SET ignore_system_indexes = OFF; -- fail, insufficient privileges
+ALTER SYSTEM RESET autovacuum_multixact_freeze_max_age; -- fail, insufficient privileges
+SET jit_provider = 'llvmjit'; -- fail, insufficient privileges
+SELECT set_config ('jit_provider', 'llvmjit', true); -- fail, insufficient privileges
+ALTER SYSTEM SET shared_buffers = 50; -- ok
+ALTER SYSTEM RESET shared_buffers; -- ok
+SET autovacuum_work_mem = 50; -- cannot be changed now
+ALTER SYSTEM RESET temp_file_limit; -- ok
+SET TimeZone = 'Europe/Helsinki'; -- ok
+RESET TimeZone; -- ok
+SET max_stack_depth = 2048; -- ok, privileges have been granted
+RESET max_stack_depth; -- ok, privileges have been granted
+ALTER SYSTEM SET max_stack_depth = 2048; -- ok, privileges have been granted
+ALTER SYSTEM RESET max_stack_depth; -- ok, privileges have been granted
+SET lc_messages = 'C'; -- fail, insufficient privileges
+RESET lc_messages; -- fail, insufficient privileges
+ALTER SYSTEM SET lc_messages = 'C'; -- fail, insufficient privileges
+ALTER SYSTEM RESET lc_messages; -- fail, insufficient privileges
+SELECT set_config ('temp_buffers', '8192', false); -- ok
+ALTER SYSTEM RESET autovacuum_work_mem; -- ok, privileges have been granted
+ALTER SYSTEM RESET ALL; -- fail, insufficient privileges
+
+-- Check dropping/revoking behavior
+SET SESSION AUTHORIZATION regress_admin;
+DROP ROLE regress_host_resource_admin; -- fail, privileges remain
+-- Use "revoke" to remove the privileges and allow the role to be dropped
+REVOKE SET, ALTER SYSTEM ON PARAMETER
+ autovacuum_work_mem, hash_mem_multiplier, max_stack_depth,
+ shared_buffers, temp_file_limit, work_mem
+FROM regress_host_resource_admin;
+DROP ROLE regress_host_resource_admin; -- ok
+
+-- Try that again, but use "drop owned by" instead of "revoke"
+CREATE ROLE regress_host_resource_admin NOSUPERUSER;
+SET SESSION AUTHORIZATION regress_host_resource_admin;
+ALTER SYSTEM SET autovacuum_work_mem = 32; -- fail, privileges not yet granted
+SET SESSION AUTHORIZATION regress_admin;
+GRANT SET, ALTER SYSTEM ON PARAMETER
+ autovacuum_work_mem, hash_mem_multiplier, max_stack_depth,
+ shared_buffers, temp_file_limit, work_mem
+TO regress_host_resource_admin;
+DROP ROLE regress_host_resource_admin; -- fail, privileges remain
+DROP OWNED BY regress_host_resource_admin RESTRICT; -- cascade should not be needed
+SET SESSION AUTHORIZATION regress_host_resource_admin;
+ALTER SYSTEM SET autovacuum_work_mem = 32; -- fail, "drop owned" has dropped privileges
+SET SESSION AUTHORIZATION regress_admin;
+DROP ROLE regress_host_resource_admin; -- ok
+
+-- Check that "reassign owned" doesn't affect privileges
+CREATE ROLE regress_host_resource_admin NOSUPERUSER;
+CREATE ROLE regress_host_resource_newadmin NOSUPERUSER;
+GRANT SET, ALTER SYSTEM ON PARAMETER
+ autovacuum_work_mem, hash_mem_multiplier, max_stack_depth,
+ shared_buffers, temp_file_limit, work_mem
+TO regress_host_resource_admin;
+REASSIGN OWNED BY regress_host_resource_admin TO regress_host_resource_newadmin;
+SET SESSION AUTHORIZATION regress_host_resource_admin;
+ALTER SYSTEM SET autovacuum_work_mem = 32; -- ok, "reassign owned" did not change privileges
+ALTER SYSTEM RESET autovacuum_work_mem; -- ok
+SET SESSION AUTHORIZATION regress_admin;
+DROP ROLE regress_host_resource_admin; -- fail, privileges remain
+DROP ROLE regress_host_resource_newadmin; -- ok, nothing was transferred
+-- Use "drop owned by" so we can drop the role
+DROP OWNED BY regress_host_resource_admin; -- ok
+DROP ROLE regress_host_resource_admin; -- ok
+
+-- Clean up
+RESET SESSION AUTHORIZATION;
+DROP ROLE regress_admin; -- ok