aboutsummaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend')
-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
14 files changed, 886 insertions, 43 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);
}
/*