v20220410-0001-Catalogue-support-for-session-variables.patch
text/x-patch
Filename: v20220410-0001-Catalogue-support-for-session-variables.patch
Type: text/x-patch
Part: 10
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v20220410-0001
Subject: Catalogue support for session variables
| File | + | − |
|---|---|---|
| src/backend/catalog/aclchk.c | 307 | 0 |
| src/backend/catalog/dependency.c | 17 | 1 |
| src/backend/catalog/Makefile | 3 | 1 |
| src/backend/catalog/namespace.c | 374 | 0 |
| src/backend/catalog/objectaddress.c | 124 | 2 |
| src/backend/catalog/pg_shdepend.c | 2 | 0 |
| src/backend/catalog/pg_variable.c | 244 | 0 |
| src/backend/commands/alter.c | 9 | 0 |
| src/backend/commands/dropcmds.c | 4 | 0 |
| src/backend/commands/event_trigger.c | 6 | 0 |
| src/backend/commands/seclabel.c | 1 | 0 |
| src/backend/commands/tablecmds.c | 1 | 0 |
| src/backend/nodes/copyfuncs.c | 20 | 0 |
| src/backend/nodes/equalfuncs.c | 19 | 0 |
| src/backend/parser/gram.y | 140 | 4 |
| src/backend/tcop/utility.c | 15 | 0 |
| src/backend/utils/adt/acl.c | 21 | 0 |
| src/backend/utils/cache/lsyscache.c | 90 | 0 |
| src/backend/utils/cache/syscache.c | 23 | 0 |
| src/backend/utils/fmgr/fmgr.c | 10 | 6 |
| src/include/catalog/dependency.h | 3 | 2 |
| src/include/catalog/namespace.h | 6 | 0 |
| src/include/catalog/pg_default_acl.h | 1 | 0 |
| src/include/catalog/pg_proc.dat | 3 | 0 |
| src/include/catalog/pg_variable.h | 105 | 0 |
| src/include/nodes/nodes.h | 1 | 0 |
| src/include/nodes/parsenodes.h | 23 | 1 |
| src/include/parser/kwlist.h | 2 | 0 |
| src/include/tcop/cmdtaglist.h | 3 | 0 |
| src/include/utils/acl.h | 8 | 2 |
| src/include/utils/lsyscache.h | 8 | 0 |
| src/include/utils/syscache.h | 4 | 2 |
From b52036fb11952b16c2a828069277e2d7077120eb Mon Sep 17 00:00:00 2001
From: "okbob@github.com" <pavel.stehule@gmail.com>
Date: Mon, 4 Apr 2022 21:19:03 +0200
Subject: [PATCH 01/11] Catalogue support for session variables
Implementation new system object class - session variable with new access rights READ, WRITE,
with routines for creating session variable, initialization of session variable from system
catalogue, and lookups routines for identification of session variables.
---
src/backend/catalog/Makefile | 4 +-
src/backend/catalog/aclchk.c | 307 ++++++++++++++++++++++
src/backend/catalog/dependency.c | 18 +-
src/backend/catalog/namespace.c | 374 +++++++++++++++++++++++++++
src/backend/catalog/objectaddress.c | 126 ++++++++-
src/backend/catalog/pg_shdepend.c | 2 +
src/backend/catalog/pg_variable.c | 244 +++++++++++++++++
src/backend/commands/alter.c | 9 +
src/backend/commands/dropcmds.c | 4 +
src/backend/commands/event_trigger.c | 6 +
src/backend/commands/seclabel.c | 1 +
src/backend/commands/tablecmds.c | 1 +
src/backend/nodes/copyfuncs.c | 20 ++
src/backend/nodes/equalfuncs.c | 19 ++
src/backend/parser/gram.y | 144 ++++++++++-
src/backend/tcop/utility.c | 15 ++
src/backend/utils/adt/acl.c | 21 ++
src/backend/utils/cache/lsyscache.c | 90 +++++++
src/backend/utils/cache/syscache.c | 23 ++
src/backend/utils/fmgr/fmgr.c | 16 +-
src/include/catalog/dependency.h | 5 +-
src/include/catalog/namespace.h | 6 +
src/include/catalog/pg_default_acl.h | 1 +
src/include/catalog/pg_proc.dat | 3 +
src/include/catalog/pg_variable.h | 105 ++++++++
src/include/nodes/nodes.h | 1 +
src/include/nodes/parsenodes.h | 24 +-
src/include/parser/kwlist.h | 2 +
src/include/tcop/cmdtaglist.h | 3 +
src/include/utils/acl.h | 10 +-
src/include/utils/lsyscache.h | 8 +
src/include/utils/syscache.h | 6 +-
32 files changed, 1597 insertions(+), 21 deletions(-)
create mode 100644 src/backend/catalog/pg_variable.c
create mode 100644 src/include/catalog/pg_variable.h
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 89a0221ec9..f89e12eaf2 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -45,6 +45,7 @@ OBJS = \
pg_shdepend.o \
pg_subscription.o \
pg_type.o \
+ pg_variable.o \
storage.o \
toasting.o
@@ -72,7 +73,8 @@ CATALOG_HEADERS := \
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
+ pg_publication_rel.h pg_subscription.h pg_subscription_rel.h \
+ pg_variable.h
GENERATED_HEADERS := $(CATALOG_HEADERS:%.h=%_d.h) schemapg.h system_fk_info.h
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 5f1726c095..745ca712f1 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -59,6 +59,7 @@
#include "catalog/pg_ts_parser.h"
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
+#include "catalog/pg_variable.h"
#include "commands/dbcommands.h"
#include "commands/defrem.h"
#include "commands/event_trigger.h"
@@ -114,6 +115,7 @@ 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 ExecGrant_Variable(InternalGrant *grantStmt);
static void SetDefaultACLsInSchemas(InternalDefaultACL *iacls, List *nspnames);
static void SetDefaultACL(InternalDefaultACL *iacls);
@@ -265,6 +267,9 @@ restrict_and_check_grant(bool is_grant, AclMode avail_goptions, bool all_privs,
case OBJECT_PARAMETER_ACL:
whole_mask = ACL_ALL_RIGHTS_PARAMETER_ACL;
break;
+ case OBJECT_VARIABLE:
+ whole_mask = ACL_ALL_RIGHTS_VARIABLE;
+ break;
default:
elog(ERROR, "unrecognized object type: %d", objtype);
/* not reached, but keep compiler quiet */
@@ -509,6 +514,10 @@ ExecuteGrantStmt(GrantStmt *stmt)
all_privileges = ACL_ALL_RIGHTS_PARAMETER_ACL;
errormsg = gettext_noop("invalid privilege type %s for parameter");
break;
+ case OBJECT_VARIABLE:
+ all_privileges = ACL_ALL_RIGHTS_VARIABLE;
+ errormsg = gettext_noop("invalid privilege type %s for session variable");
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) stmt->objtype);
@@ -614,6 +623,9 @@ ExecGrantStmt_oids(InternalGrant *istmt)
case OBJECT_PARAMETER_ACL:
ExecGrant_Parameter(istmt);
break;
+ case OBJECT_VARIABLE:
+ ExecGrant_Variable(istmt);
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) istmt->objtype);
@@ -804,6 +816,16 @@ objectNamesToOids(ObjectType objtype, List *objnames, bool is_grant)
objects = lappend_oid(objects, parameterId);
}
break;
+ case OBJECT_VARIABLE:
+ foreach(cell, objnames)
+ {
+ RangeVar *varvar = (RangeVar *) lfirst(cell);
+ Oid relOid;
+
+ relOid = LookupVariable(varvar->schemaname, varvar->relname, false);
+ objects = lappend_oid(objects, relOid);
+ }
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) objtype);
@@ -893,6 +915,33 @@ objectsInSchemaToOids(ObjectType objtype, List *nspnames)
table_close(rel, AccessShareLock);
}
break;
+ case OBJECT_VARIABLE:
+ {
+ ScanKeyData key;
+ Relation rel;
+ TableScanDesc scan;
+ HeapTuple tuple;
+
+ ScanKeyInit(&key,
+ Anum_pg_variable_varnamespace,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(namespaceId));
+
+ rel = table_open(VariableRelationId, AccessShareLock);
+ scan = table_beginscan_catalog(rel, 1, &key);
+
+ while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ {
+ Oid oid = ((Form_pg_proc) GETSTRUCT(tuple))->oid;
+
+ objects = lappend_oid(objects, oid);
+ }
+
+ table_endscan(scan);
+ table_close(rel, AccessShareLock);
+ }
+ break;
+
default:
/* should not happen */
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
@@ -1052,6 +1101,10 @@ ExecAlterDefaultPrivilegesStmt(ParseState *pstate, AlterDefaultPrivilegesStmt *s
all_privileges = ACL_ALL_RIGHTS_SCHEMA;
errormsg = gettext_noop("invalid privilege type %s for schema");
break;
+ case OBJECT_VARIABLE:
+ all_privileges = ACL_ALL_RIGHTS_VARIABLE;
+ errormsg = gettext_noop("invalid privilege type %s for session variable");
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) action->objtype);
@@ -1249,6 +1302,12 @@ SetDefaultACL(InternalDefaultACL *iacls)
this_privileges = ACL_ALL_RIGHTS_SCHEMA;
break;
+ case OBJECT_VARIABLE:
+ objtype = DEFACLOBJ_VARIABLE;
+ if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
+ this_privileges = ACL_ALL_RIGHTS_VARIABLE;
+ break;
+
default:
elog(ERROR, "unrecognized objtype: %d",
(int) iacls->objtype);
@@ -1482,6 +1541,9 @@ RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid)
case DEFACLOBJ_NAMESPACE:
iacls.objtype = OBJECT_SCHEMA;
break;
+ case DEFACLOBJ_VARIABLE:
+ iacls.objtype = OBJECT_VARIABLE;
+ break;
default:
/* Shouldn't get here */
elog(ERROR, "unexpected default ACL type: %d",
@@ -1542,6 +1604,9 @@ RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid)
case ParameterAclRelationId:
istmt.objtype = OBJECT_PARAMETER_ACL;
break;
+ case VariableRelationId:
+ istmt.objtype = OBJECT_VARIABLE;
+ break;
default:
elog(ERROR, "unexpected object class %u", classid);
break;
@@ -3421,6 +3486,129 @@ ExecGrant_Parameter(InternalGrant *istmt)
table_close(relation, RowExclusiveLock);
}
+static void
+ExecGrant_Variable(InternalGrant *istmt)
+{
+ Relation relation;
+ ListCell *cell;
+
+ if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
+ istmt->privileges = ACL_ALL_RIGHTS_VARIABLE;
+
+ relation = table_open(VariableRelationId, RowExclusiveLock);
+
+ foreach(cell, istmt->objects)
+ {
+ Oid varId = lfirst_oid(cell);
+ Form_pg_variable pg_variable_tuple;
+ Datum aclDatum;
+ bool isNull;
+ AclMode avail_goptions;
+ AclMode this_privileges;
+ Acl *old_acl;
+ Acl *new_acl;
+ Oid grantorId;
+ Oid ownerId;
+ HeapTuple tuple;
+ HeapTuple newtuple;
+ Datum values[Natts_pg_variable];
+ bool nulls[Natts_pg_variable];
+ bool replaces[Natts_pg_variable];
+ int noldmembers;
+ int nnewmembers;
+ Oid *oldmembers;
+ Oid *newmembers;
+
+ tuple = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varId));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for session variable %u", varId);
+
+ pg_variable_tuple = (Form_pg_variable) GETSTRUCT(tuple);
+
+ /*
+ * Get owner ID and working copy of existing ACL. If there's no ACL,
+ * substitute the proper default.
+ */
+ ownerId = pg_variable_tuple->varowner;
+ aclDatum = SysCacheGetAttr(VARIABLEOID, tuple, Anum_pg_variable_varacl,
+ &isNull);
+ if (isNull)
+ {
+ old_acl = acldefault(OBJECT_VARIABLE, 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,
+ varId, grantorId, OBJECT_VARIABLE,
+ NameStr(pg_variable_tuple->varname),
+ 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);
+
+ /* finished building new ACL value, now insert it */
+ MemSet(values, 0, sizeof(values));
+ MemSet(nulls, false, sizeof(nulls));
+ MemSet(replaces, false, sizeof(replaces));
+
+ replaces[Anum_pg_variable_varacl - 1] = true;
+ values[Anum_pg_variable_varacl - 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(varId, VariableRelationId, 0, new_acl);
+
+ /* Update the shared dependency ACL info */
+ updateAclDependencies(VariableRelationId, varId, 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)
@@ -3457,6 +3645,10 @@ string_to_privilege(const char *privname)
return ACL_ALTER_SYSTEM;
if (strcmp(privname, "rule") == 0)
return 0; /* ignore old RULE privileges */
+ if (strcmp(privname, "read") == 0)
+ return ACL_READ;
+ if (strcmp(privname, "write") == 0)
+ return ACL_WRITE;
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("unrecognized privilege type \"%s\"", privname)));
@@ -3496,6 +3688,10 @@ privilege_to_string(AclMode privilege)
return "SET";
case ACL_ALTER_SYSTEM:
return "ALTER SYSTEM";
+ case ACL_READ:
+ return "READ";
+ case ACL_WRITE:
+ return "WRITE";
default:
elog(ERROR, "unrecognized privilege: %d", (int) privilege);
}
@@ -3622,6 +3818,9 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_TYPE:
msg = gettext_noop("permission denied for type %s");
break;
+ case OBJECT_VARIABLE:
+ msg = gettext_noop("permission denied for session variable %s");
+ break;
case OBJECT_VIEW:
msg = gettext_noop("permission denied for view %s");
break;
@@ -3733,6 +3932,9 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_TYPE:
msg = gettext_noop("must be owner of type %s");
break;
+ case OBJECT_VARIABLE:
+ msg = gettext_noop("must be owner of session variable %s");
+ break;
case OBJECT_VIEW:
msg = gettext_noop("must be owner of view %s");
break;
@@ -3881,6 +4083,8 @@ pg_aclmask(ObjectType objtype, Oid table_oid, AttrNumber attnum, Oid roleid,
return ACL_NO_RIGHTS;
case OBJECT_TYPE:
return pg_type_aclmask(table_oid, roleid, mask, how);
+ case OBJECT_VARIABLE:
+ return pg_variable_aclmask(table_oid, roleid, mask, how);
default:
elog(ERROR, "unrecognized objtype: %d",
(int) objtype);
@@ -4864,6 +5068,66 @@ pg_type_aclmask(Oid type_oid, Oid roleid, AclMode mask, AclMaskHow how)
return result;
}
+/*
+ * Exported routine for examining a user's privileges for a variable.
+ */
+AclMode
+pg_variable_aclmask(Oid var_oid, Oid roleid, AclMode mask, AclMaskHow how)
+{
+ AclMode result;
+ HeapTuple tuple;
+ Datum aclDatum;
+ bool isNull;
+ Acl *acl;
+ Oid ownerId;
+
+ Form_pg_variable varForm;
+
+ /* Bypass permission checks for superusers */
+ if (superuser_arg(roleid))
+ return mask;
+
+ /*
+ * Must get the variables's tuple from pg_variable
+ */
+ tuple = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(var_oid));
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("variable with OID %u does not exist",
+ var_oid)));
+ varForm = (Form_pg_variable) GETSTRUCT(tuple);
+
+ /*
+ * Now get the variable's owner and ACL from the tuple
+ */
+ ownerId = varForm->varowner;
+
+ aclDatum = SysCacheGetAttr(VARIABLEOID, tuple,
+ Anum_pg_variable_varacl, &isNull);
+ if (isNull)
+ {
+ /* No ACL, so build default ACL */
+ acl = acldefault(OBJECT_VARIABLE, ownerId);
+ aclDatum = (Datum) 0;
+ }
+ else
+ {
+ /* detoast rel's ACL if necessary */
+ acl = DatumGetAclP(aclDatum);
+ }
+
+ result = aclmask(acl, roleid, ownerId, 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 checking a user's access privileges to a column
*
@@ -5164,6 +5428,18 @@ pg_type_aclcheck(Oid type_oid, Oid roleid, AclMode mode)
return ACLCHECK_NO_PRIV;
}
+/*
+ * Exported routine for checking a user's access privileges to a variable
+ */
+AclResult
+pg_variable_aclcheck(Oid type_oid, Oid roleid, AclMode mode)
+{
+ if (pg_variable_aclmask(type_oid, roleid, mode, ACLMASK_ANY) != 0)
+ return ACLCHECK_OK;
+ else
+ return ACLCHECK_NO_PRIV;
+}
+
/*
* Ownership check for a relation (specified by OID).
*/
@@ -5781,6 +6057,33 @@ pg_statistics_object_ownercheck(Oid stat_oid, Oid roleid)
return has_privs_of_role(roleid, ownerId);
}
+/*
+ * Ownership check for a session variable (specified by OID).
+ */
+bool
+pg_variable_ownercheck(Oid db_oid, Oid roleid)
+{
+ HeapTuple tuple;
+ Oid ownerId;
+
+ /* Superusers bypass all permission checking. */
+ if (superuser_arg(roleid))
+ return true;
+
+ tuple = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(db_oid));
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_DATABASE),
+ errmsg("session variable with OID %u does not exist", db_oid)));
+
+ ownerId = ((Form_pg_variable) GETSTRUCT(tuple))->varowner;
+
+ ReleaseSysCache(tuple);
+
+ return has_privs_of_role(roleid, ownerId);
+}
+
+
/*
* Check whether specified role has CREATEROLE privilege (or is a superuser)
*
@@ -5909,6 +6212,10 @@ get_user_default_acl(ObjectType objtype, Oid ownerId, Oid nsp_oid)
defaclobjtype = DEFACLOBJ_NAMESPACE;
break;
+ case OBJECT_VARIABLE:
+ defaclobjtype = DEFACLOBJ_VARIABLE;
+ break;
+
default:
return NULL;
}
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index de10923391..8ffcaabc46 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -64,12 +64,15 @@
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
#include "catalog/pg_user_mapping.h"
+#include "catalog/pg_variable.h"
#include "commands/comment.h"
#include "commands/defrem.h"
#include "commands/event_trigger.h"
#include "commands/extension.h"
#include "commands/policy.h"
#include "commands/publicationcmds.h"
+#include "commands/schemacmds.h"
+#include "commands/session_variable.h"
#include "commands/seclabel.h"
#include "commands/sequence.h"
#include "commands/trigger.h"
@@ -185,7 +188,8 @@ static const Oid object_classes[] = {
PublicationRelationId, /* OCLASS_PUBLICATION */
PublicationRelRelationId, /* OCLASS_PUBLICATION_REL */
SubscriptionRelationId, /* OCLASS_SUBSCRIPTION */
- TransformRelationId /* OCLASS_TRANSFORM */
+ TransformRelationId, /* OCLASS_TRANSFORM */
+ VariableRelationId /* OCLASS_VARIABLE */
};
@@ -1502,6 +1506,10 @@ doDeletion(const ObjectAddress *object, int flags)
DropObjectById(object);
break;
+ case OCLASS_VARIABLE:
+ RemoveSessionVariable(object->objectId);
+ break;
+
/*
* These global object types are not supported here.
*/
@@ -1881,6 +1889,11 @@ find_expr_references_walker(Node *node,
{
Param *param = (Param *) node;
+ /* A variable parameter depends on the session variable */
+ if (param->paramkind == PARAM_VARIABLE)
+ add_object_address(OCLASS_VARIABLE, param->paramvarid, 0,
+ context->addrs);
+
/* A parameter must depend on the parameter's datatype */
add_object_address(OCLASS_TYPE, param->paramtype, 0,
context->addrs);
@@ -2884,6 +2897,9 @@ getObjectClass(const ObjectAddress *object)
case TransformRelationId:
return OCLASS_TRANSFORM;
+
+ case VariableRelationId:
+ return OCLASS_VARIABLE;
}
/* shouldn't get here */
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index fafb9349cc..8300aea87b 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -39,6 +39,7 @@
#include "catalog/pg_ts_parser.h"
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
+#include "catalog/pg_variable.h"
#include "commands/dbcommands.h"
#include "funcapi.h"
#include "mb/pg_wchar.h"
@@ -764,6 +765,69 @@ RelationIsVisible(Oid relid)
return visible;
}
+/*
+ * VariableIsVisible
+ * Determine whether a variable (identified by OID) is visible in the
+ * current search path. Visible means "would be found by searching
+ * for the unqualified variable name".
+ */
+bool
+VariableIsVisible(Oid varid)
+{
+ HeapTuple vartup;
+ Form_pg_variable varform;
+ Oid varnamespace;
+ bool visible;
+
+ vartup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+ if (!HeapTupleIsValid(vartup))
+ elog(ERROR, "cache lookup failed for session variable %u", varid);
+ varform = (Form_pg_variable) GETSTRUCT(vartup);
+
+ recomputeNamespacePath();
+
+ /*
+ * Quick check: if it ain't in the path at all, it ain't visible. Items in
+ * the system namespace are surely in the path and so we needn't even do
+ * list_member_oid() for them.
+ */
+ varnamespace = varform->varnamespace;
+ if (varnamespace != PG_CATALOG_NAMESPACE &&
+ !list_member_oid(activeSearchPath, varnamespace))
+ visible = false;
+ else
+ {
+ /*
+ * If it is in the path, it might still not be visible; it could be
+ * hidden by another variable of the same name earlier in the path. So
+ * we must do a slow check for conflicting relations.
+ */
+ char *varname = NameStr(varform->varname);
+ ListCell *l;
+
+ visible = false;
+ foreach(l, activeSearchPath)
+ {
+ Oid namespaceId = lfirst_oid(l);
+
+ if (namespaceId == varnamespace)
+ {
+ /* Found it first in path */
+ visible = true;
+ break;
+ }
+ if (OidIsValid(get_varname_varid(varname, namespaceId)))
+ {
+ /* Found something else first in path */
+ break;
+ }
+ }
+ }
+
+ ReleaseSysCache(vartup);
+
+ return visible;
+}
/*
* TypenameGetTypid
@@ -2843,6 +2907,305 @@ TSConfigIsVisible(Oid cfgid)
return visible;
}
+/*
+ * Returns oid of session variable specified by possibly qualified identifier.
+ *
+ * If not found, returns InvalidOid if missing_ok, else throws error.
+ */
+Oid
+LookupVariable(const char *nspname, const char *varname, bool missing_ok)
+{
+ Oid namespaceId;
+ Oid varoid = InvalidOid;
+ ListCell *l;
+
+ if (nspname)
+ {
+ namespaceId = LookupExplicitNamespace(nspname, missing_ok);
+
+ /*
+ * If nspname is not a known namespace, then nspname.varname cannot be
+ * any usable session variable.
+ */
+ if (!OidIsValid(namespaceId))
+ return InvalidOid;
+
+ varoid = GetSysCacheOid2(VARIABLENAMENSP, Anum_pg_variable_oid,
+ PointerGetDatum(varname),
+ ObjectIdGetDatum(namespaceId));
+ }
+ else
+ {
+ /* Iterate over schemas in search_path */
+ recomputeNamespacePath();
+
+ foreach(l, activeSearchPath)
+ {
+ namespaceId = lfirst_oid(l);
+
+ varoid = GetSysCacheOid2(VARIABLENAMENSP, Anum_pg_variable_oid,
+ PointerGetDatum(varname),
+ ObjectIdGetDatum(namespaceId));
+
+ if (OidIsValid(varoid))
+ break;
+ }
+ }
+
+ if (!OidIsValid(varoid) && !missing_ok)
+ {
+ if (nspname)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("session variable \"%s.%s\" does not exist",
+ nspname, varname)));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("session variable \"%s\" does not exist",
+ varname)));
+ }
+
+ return varoid;
+}
+
+/*
+ * The input list contains names with indirection expressions used as the left
+ * part of LET statement. The following routine returns a new list with only
+ * initial strings (names) - without indirection expressions.
+ */
+List *
+NamesFromList(List *names)
+{
+ ListCell *l;
+ List *result = NIL;
+
+ foreach(l, names)
+ {
+ Node *n = lfirst(l);
+
+ if (IsA(n, String))
+ {
+ result = lappend(result, n);
+ }
+ else
+ break;
+ }
+
+ return result;
+}
+
+/*
+ * IdentifyVariable - try to find variable identified by list of names.
+ *
+ * Before this call we don't know, how these fields should be mapped to
+ * schema name, variable name and attribute name. In this routine
+ * we try to apply passed names to all possible combinations of schema name,
+ * variable name and attribute name, and we count valid combinations.
+ *
+ * Returns oid of identified variable. When last field of names list is
+ * identified as an attribute, then output attrname argument is set to
+ * an string of this field.
+ *
+ * When there is not any valid combination, then we are sure, so the
+ * list of names cannot to identify any session variable. In this case
+ * we return InvalidOid.
+ *
+ * We can find more valid combination than one.
+ * Example: users can have session variable x in schema y, and
+ * session variable y with attribute x inside some schema from
+ * search path. In this situation the meaning of expression "y"."x"
+ * is ambiguous. In this case this routine returns InvalidOid, and
+ * sets the output parameter "not_unique" to true. This parameter is
+ * used for more meaningfull error message.
+ *
+ * When lockit is true, then AccessShareLock is created on related
+ * session variable. The lock will be kept for the whole transaction.
+ */
+Oid
+IdentifyVariable(List *names, char **attrname, bool lockit, bool *not_unique)
+{
+ Node *field1 = NULL;
+ Node *field2 = NULL;
+ Node *field3 = NULL;
+ Node *field4 = NULL;
+ char *a = NULL;
+ char *b = NULL;
+ char *c = NULL;
+ char *d = NULL;
+ Oid varoid_without_attr = InvalidOid;
+ Oid varoid_with_attr = InvalidOid;
+ Oid varid = InvalidOid;
+
+ *not_unique = false;
+ *attrname = NULL;
+
+ switch (list_length(names))
+ {
+ case 1:
+ field1 = linitial(names);
+
+ Assert(IsA(field1, String));
+
+ varid = LookupVariable(NULL, strVal(field1), true);
+ break;
+
+ case 2:
+ field1 = linitial(names);
+ field2 = lsecond(names);
+
+ Assert(IsA(field1, String));
+ a = strVal(field1);
+
+ if (IsA(field2, String))
+ {
+ b = strVal(field2);
+
+ /*
+ * a.b can mean "schema"."variable" or "variable"."field", Check
+ * both variants, and returns InvalidOid with not_unique flag, when
+ * both interpretations are possible. Second node can be star. In
+ * this case, the only allowed possibility is "variable"."*".
+ */
+ varoid_without_attr = LookupVariable(a, b, true);
+ varoid_with_attr = LookupVariable(NULL, a, true);
+ }
+ else
+ {
+ Assert(IsA(field2, A_Star));
+
+ /*
+ * Session variables doesn't support unboxing by star syntax. But
+ * this syntax have to be calculated here, because can come from
+ * non session variables related expressions.
+ */
+ return InvalidOid;
+ }
+
+ if (OidIsValid(varoid_without_attr) && OidIsValid(varoid_with_attr))
+ {
+ *not_unique = true;
+ return InvalidOid;
+ }
+ else if (OidIsValid(varoid_without_attr))
+ {
+ *attrname = NULL;
+ varid = varoid_without_attr;
+ }
+ else
+ {
+ *attrname = b;
+ varid = varoid_with_attr;
+ }
+ break;
+
+ case 3:
+ field1 = linitial(names);
+ field2 = lsecond(names);
+ field3 = lthird(names);
+
+ Assert(IsA(field1, String));
+ Assert(IsA(field2, String));
+
+ a = strVal(field1);
+ b = strVal(field2);
+
+ if (IsA(field3, String))
+ {
+ c = strVal(field3);
+
+ /*
+ * a.b.c can mean "catalog"."schema"."variable" or
+ * "schema"."variable"."field", Check both variants, and returns
+ * InvalidOid with not_unique flag, when both interpretations are
+ * possible. When third node is star, the only possible
+ * interpretation is "schema"."variable"."*".
+ */
+ varoid_without_attr = LookupVariable(b, c, true);
+ varoid_with_attr = LookupVariable(a, b, true);
+ }
+ else
+ {
+ Assert(IsA(field3, A_Star));
+ return InvalidOid;
+ }
+
+ if (OidIsValid(varoid_without_attr) && OidIsValid(varoid_with_attr))
+ {
+ *not_unique = true;
+ return InvalidOid;
+ }
+ else if (OidIsValid(varoid_without_attr))
+ {
+
+ /*
+ * In this case, "a" is used as catalog name - check it.
+ */
+ if (strcmp(a, get_database_name(MyDatabaseId)) != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cross-database references are not implemented: %s",
+ NameListToString(names))));
+
+ varid = varoid_without_attr;
+ }
+ else
+ {
+ *attrname = c;
+ varid = varoid_with_attr;
+ }
+ break;
+
+ case 4:
+ field1 = linitial(names);
+ field2 = lsecond(names);
+ field3 = lthird(names);
+ field4 = lfourth(names);
+
+ Assert(IsA(field1, String));
+ Assert(IsA(field2, String));
+ Assert(IsA(field3, String));
+
+ a = strVal(field1);
+ b = strVal(field2);
+ c = strVal(field3);
+
+ /*
+ * In this case, "a" is used as catalog name - check it.
+ */
+ if (strcmp(a, get_database_name(MyDatabaseId)) != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cross-database references are not implemented: %s",
+ NameListToString(names))));
+
+ if (IsA(field4, String))
+ {
+ d = strVal(field4);
+ }
+ else
+ {
+ Assert(IsA(field4, A_Star));
+ return InvalidOid;
+ }
+
+ *attrname = d;
+ varid = LookupVariable(b, c, true);
+ break;
+
+ default:
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("improper qualified name (too many dotted names): %s",
+ NameListToString(names))));
+ break;
+ }
+
+ if (OidIsValid(varid) && lockit)
+ LockDatabaseObject(VariableRelationId, varid, 0, AccessShareLock);
+
+ return varid;
+}
/*
* DeconstructQualifiedName
@@ -4660,3 +5023,14 @@ pg_is_other_temp_schema(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(isOtherTempNamespace(oid));
}
+
+Datum
+pg_variable_is_visible(PG_FUNCTION_ARGS)
+{
+ Oid oid = PG_GETARG_OID(0);
+
+ if (!SearchSysCacheExists1(VARIABLEOID, ObjectIdGetDatum(oid)))
+ PG_RETURN_NULL();
+
+ PG_RETURN_BOOL(VariableIsVisible(oid));
+}
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index ac6043514f..1cd07430e7 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -63,6 +63,7 @@
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
#include "catalog/pg_user_mapping.h"
+#include "catalog/pg_variable.h"
#include "commands/dbcommands.h"
#include "commands/defrem.h"
#include "commands/event_trigger.h"
@@ -618,6 +619,20 @@ static const ObjectPropertyType ObjectProperty[] =
OBJECT_USER_MAPPING,
false
},
+ {
+ "session variable",
+ VariableRelationId,
+ VariableObjectIndexId,
+ VARIABLEOID,
+ VARIABLENAMENSP,
+ Anum_pg_variable_oid,
+ Anum_pg_variable_varname,
+ Anum_pg_variable_varnamespace,
+ Anum_pg_variable_varowner,
+ Anum_pg_variable_varacl,
+ OBJECT_VARIABLE,
+ true
+ }
};
/*
@@ -850,6 +865,10 @@ static const struct object_type_map
/* OCLASS_STATISTIC_EXT */
{
"statistics object", OBJECT_STATISTIC_EXT
+ },
+ /* OCLASS_VARIABLE */
+ {
+ "session variable", OBJECT_VARIABLE
}
};
@@ -875,6 +894,7 @@ static ObjectAddress get_object_address_attrdef(ObjectType objtype,
bool missing_ok);
static ObjectAddress get_object_address_type(ObjectType objtype,
TypeName *typename, bool missing_ok);
+static ObjectAddress get_object_address_variable(List *object, bool missing_ok);
static ObjectAddress get_object_address_opcf(ObjectType objtype, List *object,
bool missing_ok);
static ObjectAddress get_object_address_opf_member(ObjectType objtype,
@@ -1145,6 +1165,9 @@ get_object_address(ObjectType objtype, Node *object,
missing_ok);
address.objectSubId = 0;
break;
+ case OBJECT_VARIABLE:
+ address = get_object_address_variable(castNode(List, object), missing_ok);
+ break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
/* placate compiler, in case it thinks elog might return */
@@ -2021,16 +2044,20 @@ get_object_address_defacl(List *object, bool missing_ok)
case DEFACLOBJ_NAMESPACE:
objtype_str = "schemas";
break;
+ case DEFACLOBJ_VARIABLE:
+ objtype_str = "variables";
+ break;
default:
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("unrecognized default ACL object type \"%c\"", objtype),
- errhint("Valid object types are \"%c\", \"%c\", \"%c\", \"%c\", \"%c\".",
+ errhint("Valid object types are \"%c\", \"%c\", \"%c\", \"%c\", \"%c\", \"%c\".",
DEFACLOBJ_RELATION,
DEFACLOBJ_SEQUENCE,
DEFACLOBJ_FUNCTION,
DEFACLOBJ_TYPE,
- DEFACLOBJ_NAMESPACE)));
+ DEFACLOBJ_NAMESPACE,
+ DEFACLOBJ_VARIABLE)));
}
/*
@@ -2115,6 +2142,24 @@ textarray_to_strvaluelist(ArrayType *arr)
return list;
}
+/*
+ * Find the ObjectAddress for a session variable
+ */
+static ObjectAddress
+get_object_address_variable(List *object, bool missing_ok)
+{
+ ObjectAddress address;
+ char *nspname = NULL;
+ char *varname = NULL;
+
+ ObjectAddressSet(address, VariableRelationId, InvalidOid);
+
+ DeconstructQualifiedName(object, &nspname, &varname);
+ address.objectId = LookupVariable(nspname, varname, missing_ok);
+
+ return address;
+}
+
/*
* SQL-callable version of get_object_address
*/
@@ -2305,6 +2350,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
case OBJECT_TABCONSTRAINT:
case OBJECT_OPCLASS:
case OBJECT_OPFAMILY:
+ case OBJECT_VARIABLE:
objnode = (Node *) name;
break;
case OBJECT_ACCESS_METHOD:
@@ -2615,6 +2661,11 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
aclcheck_error(ACLCHECK_NOT_OWNER, objtype,
NameListToString(castNode(List, object)));
break;
+ case OBJECT_VARIABLE:
+ if (!pg_variable_ownercheck(address.objectId, roleid))
+ aclcheck_error(ACLCHECK_NOT_OWNER, objtype,
+ NameListToString(castNode(List, object)));
+ break;
default:
elog(ERROR, "unrecognized object type: %d",
(int) objtype);
@@ -3516,6 +3567,32 @@ getObjectDescription(const ObjectAddress *object, bool missing_ok)
break;
}
+ case OCLASS_VARIABLE:
+ {
+ char *nspname;
+ HeapTuple tup;
+ Form_pg_variable varform;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for session variable %u",
+ object->objectId);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ if (VariableIsVisible(object->objectId))
+ nspname = NULL;
+ else
+ nspname = get_namespace_name(varform->varnamespace);
+
+ appendStringInfo(&buffer, _("session variable %s"),
+ quote_qualified_identifier(nspname,
+ NameStr(varform->varname)));
+
+ ReleaseSysCache(tup);
+ break;
+ }
+
case OCLASS_TSPARSER:
{
HeapTuple tup;
@@ -3826,6 +3903,16 @@ getObjectDescription(const ObjectAddress *object, bool missing_ok)
_("default privileges on new schemas belonging to role %s"),
rolename);
break;
+ case DEFACLOBJ_VARIABLE:
+ if (nspname)
+ appendStringInfo(&buffer,
+ _("default privileges on new session variables belonging to role %s in schema %s"),
+ rolename, nspname);
+ else
+ appendStringInfo(&buffer,
+ _("default privileges on new session variables belonging to role %s"),
+ rolename);
+ break;
default:
/* shouldn't get here */
if (nspname)
@@ -4598,6 +4685,10 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "transform");
break;
+ case OCLASS_VARIABLE:
+ appendStringInfoString(&buffer, "session variable");
+ break;
+
/*
* There's intentionally no default: case here; we want the
* compiler to warn if a new OCLASS hasn't been handled above.
@@ -5663,6 +5754,10 @@ getObjectIdentityParts(const ObjectAddress *object,
appendStringInfoString(&buffer,
" on schemas");
break;
+ case DEFACLOBJ_VARIABLE:
+ appendStringInfoString(&buffer,
+ " on session variables");
+ break;
}
if (objname)
@@ -5906,6 +6001,33 @@ getObjectIdentityParts(const ObjectAddress *object,
}
break;
+ case OCLASS_VARIABLE:
+ {
+ char *schema;
+ char *varname;
+ HeapTuple tup;
+ Form_pg_variable varform;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for session variable %u",
+ object->objectId);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ schema = get_namespace_name_or_temp(varform->varnamespace);
+ varname = NameStr(varform->varname);
+
+ appendStringInfo(&buffer, "%s",
+ quote_qualified_identifier(schema, varname));
+
+ if (objname)
+ *objname = list_make2(schema, varname);
+
+ ReleaseSysCache(tup);
+ break;
+ }
+
/*
* There's intentionally no default: case here; we want the
* compiler to warn if a new OCLASS hasn't been handled above.
diff --git a/src/backend/catalog/pg_shdepend.c b/src/backend/catalog/pg_shdepend.c
index 3e8fa008b9..cb58d9a0fd 100644
--- a/src/backend/catalog/pg_shdepend.c
+++ b/src/backend/catalog/pg_shdepend.c
@@ -46,6 +46,7 @@
#include "catalog/pg_ts_dict.h"
#include "catalog/pg_type.h"
#include "catalog/pg_user_mapping.h"
+#include "catalog/pg_variable.h"
#include "commands/alter.h"
#include "commands/collationcmds.h"
#include "commands/conversioncmds.h"
@@ -1593,6 +1594,7 @@ shdepReassignOwned(List *roleids, Oid newrole)
case DatabaseRelationId:
case TSConfigRelationId:
case TSDictionaryRelationId:
+ case VariableRelationId:
{
Oid classId = sdepForm->classid;
Relation catalog;
diff --git a/src/backend/catalog/pg_variable.c b/src/backend/catalog/pg_variable.c
new file mode 100644
index 0000000000..aea99ae705
--- /dev/null
+++ b/src/backend/catalog/pg_variable.c
@@ -0,0 +1,244 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_variable.c
+ * session variables
+ *
+ * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/catalog/pg_variable.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/htup_details.h"
+
+#include "catalog/catalog.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/namespace.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_collation.h"
+#include "catalog/pg_namespace.h"
+#include "catalog/pg_type.h"
+#include "catalog/pg_variable.h"
+#include "commands/session_variable.h"
+#include "storage/lmgr.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/syscache.h"
+
+/*
+ * Fetch attributes (without acl) of session variable from the syscache.
+ * We don't work with acl directly, so we don't need to read it here.
+ * Skip deserialization of defexpr when fast_only is true.
+ */
+void
+initVariable(Variable *var, Oid varid, bool fast_only)
+{
+ HeapTuple tup;
+ Form_pg_variable varform;
+ Datum defexpr_datum;
+ bool defexpr_isnull;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for session variable %u", varid);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ var->oid = varid;
+ var->name = pstrdup(NameStr(varform->varname));
+ var->namespace = varform->varnamespace;
+ var->typid = varform->vartype;
+ var->typmod = varform->vartypmod;
+ var->owner = varform->varowner;
+ var->collation = varform->varcollation;
+ var->eoxaction = varform->vareoxaction;
+ var->is_not_null = varform->varisnotnull;
+ var->is_immutable = varform->varisimmutable;
+
+ /* Get defexpr */
+ defexpr_datum = SysCacheGetAttr(VARIABLEOID,
+ tup,
+ Anum_pg_variable_vardefexpr,
+ &defexpr_isnull);
+
+ var->has_defexpr = !defexpr_isnull;
+
+ /*
+ * Deserialize defexpr only when it is requested.
+ * We need to deserialize Node with default expression,
+ * only when we read from session variable, and this
+ * session variable has not assigned value, and this
+ * session variable has default expression. For other
+ * cases, we skip skip this operation.
+ */
+ if (!fast_only && !defexpr_isnull)
+ var->defexpr = stringToNode(TextDatumGetCString(defexpr_datum));
+ else
+ var->defexpr = NULL;
+
+ ReleaseSysCache(tup);
+}
+
+/*
+ * Create entry in pg_variable table
+ */
+ObjectAddress
+VariableCreate(const char *varName,
+ Oid varNamespace,
+ Oid varType,
+ int32 varTypmod,
+ Oid varOwner,
+ Oid varCollation,
+ Node *varDefexpr,
+ VariableEOXAction eoxaction,
+ bool is_not_null,
+ bool if_not_exists,
+ bool is_immutable)
+{
+ Acl *varacl;
+ NameData varname;
+ bool nulls[Natts_pg_variable];
+ Datum values[Natts_pg_variable];
+ Relation rel;
+ HeapTuple tup;
+ TupleDesc tupdesc;
+ ObjectAddress myself,
+ referenced;
+ ObjectAddresses *addrs;
+ Oid varid;
+
+ AssertArg(varName);
+ AssertArg(OidIsValid(varNamespace));
+ AssertArg(OidIsValid(varType));
+ AssertArg(OidIsValid(varOwner));
+
+ rel = table_open(VariableRelationId, RowExclusiveLock);
+
+ /*
+ * Check for duplicates. Note that this does not really prevent
+ * duplicates, it's here just to provide nicer error message in common
+ * case. The real protection is the unique key on the catalog.
+ */
+ if (SearchSysCacheExists2(VARIABLENAMENSP,
+ PointerGetDatum(varName),
+ ObjectIdGetDatum(varNamespace)))
+ {
+ if (if_not_exists)
+ ereport(NOTICE,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("session variable \"%s\" already exists, skipping",
+ varName)));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("session variable \"%s\" already exists",
+ varName)));
+
+ table_close(rel, RowExclusiveLock);
+
+ return InvalidObjectAddress;
+ }
+
+ memset(values, 0, sizeof(values));
+ memset(nulls, false, sizeof(nulls));
+
+ namestrcpy(&varname, varName);
+
+ varid = GetNewOidWithIndex(rel, VariableObjectIndexId, Anum_pg_variable_oid);
+
+ values[Anum_pg_variable_oid - 1] = ObjectIdGetDatum(varid);
+ values[Anum_pg_variable_varname - 1] = NameGetDatum(&varname);
+ values[Anum_pg_variable_varnamespace - 1] = ObjectIdGetDatum(varNamespace);
+ values[Anum_pg_variable_vartype - 1] = ObjectIdGetDatum(varType);
+ values[Anum_pg_variable_vartypmod - 1] = Int32GetDatum(varTypmod);
+ values[Anum_pg_variable_varowner - 1] = ObjectIdGetDatum(varOwner);
+ values[Anum_pg_variable_varcollation - 1] = ObjectIdGetDatum((char) varCollation);
+ values[Anum_pg_variable_vareoxaction - 1] = CharGetDatum(eoxaction);
+ values[Anum_pg_variable_varisnotnull - 1] = BoolGetDatum(is_not_null);
+ values[Anum_pg_variable_varisimmutable - 1] = BoolGetDatum(is_immutable);
+ /* varacl will be determined later */
+
+ if (varDefexpr)
+ values[Anum_pg_variable_vardefexpr - 1] = CStringGetTextDatum(nodeToString(varDefexpr));
+ else
+ nulls[Anum_pg_variable_vardefexpr - 1] = true;
+
+ tupdesc = RelationGetDescr(rel);
+
+ varacl = get_user_default_acl(OBJECT_VARIABLE, varOwner,
+ varNamespace);
+
+ if (varacl != NULL)
+ values[Anum_pg_variable_varacl - 1] = PointerGetDatum(varacl);
+ else
+ nulls[Anum_pg_variable_varacl - 1] = true;
+
+ tup = heap_form_tuple(tupdesc, values, nulls);
+ CatalogTupleInsert(rel, tup);
+ Assert(OidIsValid(varid));
+
+ addrs = new_object_addresses();
+
+ ObjectAddressSet(myself, VariableRelationId, varid);
+
+ /* dependency on namespace */
+ ObjectAddressSet(referenced, NamespaceRelationId, varNamespace);
+ add_exact_object_address(&referenced, addrs);
+
+ /* dependency on used type */
+ ObjectAddressSet(referenced, TypeRelationId, varType);
+ add_exact_object_address(&referenced, addrs);
+
+ /* dependency on collation */
+ if (OidIsValid(varCollation) &&
+ varCollation != DEFAULT_COLLATION_OID)
+ {
+ ObjectAddressSet(referenced, CollationRelationId, varCollation);
+ add_exact_object_address(&referenced, addrs);
+ }
+
+ /* dependency on default expr */
+ if (varDefexpr)
+ recordDependencyOnExpr(&myself, (Node *) varDefexpr,
+ NIL, DEPENDENCY_NORMAL);
+
+ record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
+ free_object_addresses(addrs);
+
+ /* dependency on owner */
+ recordDependencyOnOwner(VariableRelationId, varid, varOwner);
+
+ /* dependencies on roles mentioned in default ACL */
+ recordDependencyOnNewAcl(VariableRelationId, varid, 0, varOwner, varacl);
+
+ /* dependency on extension */
+ recordDependencyOnCurrentExtension(&myself, false);
+
+ /*
+ * For temporary variables, we need to create a new end of xact action to
+ * ensure deletion from catalog.
+ */
+ if (eoxaction == VARIABLE_EOX_DROP)
+ {
+ Assert(isTempNamespace(varNamespace));
+
+ RegisterOnCommitDropSessionVariable(myself.objectId);
+ }
+
+ heap_freetuple(tup);
+
+ /* Post creation hook for new function */
+ InvokeObjectPostCreateHook(VariableRelationId, varid, 0);
+
+ table_close(rel, RowExclusiveLock);
+
+ return myself;
+}
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index 5456b8222b..e8244f5cd6 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -40,6 +40,7 @@
#include "catalog/pg_ts_dict.h"
#include "catalog/pg_ts_parser.h"
#include "catalog/pg_ts_template.h"
+#include "catalog/pg_variable.h"
#include "commands/alter.h"
#include "commands/collationcmds.h"
#include "commands/conversioncmds.h"
@@ -141,6 +142,10 @@ report_namespace_conflict(Oid classId, const char *name, Oid nspOid)
Assert(OidIsValid(nspOid));
msgfmt = gettext_noop("text search configuration \"%s\" already exists in schema \"%s\"");
break;
+ case VariableRelationId:
+ Assert(OidIsValid(nspOid));
+ msgfmt = gettext_noop("session variable \"%s\" already exists in schema \"%s\"");
+ break;
default:
elog(ERROR, "unsupported object class %u", classId);
break;
@@ -393,6 +398,7 @@ ExecRenameStmt(RenameStmt *stmt)
case OBJECT_TSTEMPLATE:
case OBJECT_PUBLICATION:
case OBJECT_SUBSCRIPTION:
+ case OBJECT_VARIABLE:
{
ObjectAddress address;
Relation catalog;
@@ -536,6 +542,7 @@ ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt,
case OBJECT_TSDICTIONARY:
case OBJECT_TSPARSER:
case OBJECT_TSTEMPLATE:
+ case OBJECT_VARIABLE:
{
Relation catalog;
Relation relation;
@@ -626,6 +633,7 @@ AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid,
case OCLASS_TSDICT:
case OCLASS_TSTEMPLATE:
case OCLASS_TSCONFIG:
+ case OCLASS_VARIABLE:
{
Relation catalog;
@@ -885,6 +893,7 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
case OBJECT_TABLESPACE:
case OBJECT_TSDICTIONARY:
case OBJECT_TSCONFIGURATION:
+ case OBJECT_VARIABLE:
{
Relation catalog;
Relation relation;
diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c
index c9b5732448..e526143bdc 100644
--- a/src/backend/commands/dropcmds.c
+++ b/src/backend/commands/dropcmds.c
@@ -481,6 +481,10 @@ does_not_exist_skipping(ObjectType objtype, Node *object)
msg = gettext_noop("publication \"%s\" does not exist, skipping");
name = strVal(object);
break;
+ case OBJECT_VARIABLE:
+ msg = gettext_noop("session variable \"%s\" does not exist, skipping");
+ name = NameListToString(castNode(List, object));
+ break;
default:
elog(ERROR, "unrecognized object type: %d", (int) objtype);
break;
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 4642527881..d32112ae5f 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -992,6 +992,7 @@ EventTriggerSupportsObjectType(ObjectType obtype)
case OBJECT_TSTEMPLATE:
case OBJECT_TYPE:
case OBJECT_USER_MAPPING:
+ case OBJECT_VARIABLE:
case OBJECT_VIEW:
return true;
@@ -1057,6 +1058,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
case OCLASS_PUBLICATION_REL:
case OCLASS_SUBSCRIPTION:
case OCLASS_TRANSFORM:
+ case OCLASS_VARIABLE:
return true;
/*
@@ -2054,6 +2056,8 @@ stringify_grant_objtype(ObjectType objtype)
return "TABLESPACE";
case OBJECT_TYPE:
return "TYPE";
+ case OBJECT_VARIABLE:
+ return "VARIABLE";
/* these currently aren't used */
case OBJECT_ACCESS_METHOD:
case OBJECT_AGGREGATE:
@@ -2137,6 +2141,8 @@ stringify_adefprivs_objtype(ObjectType objtype)
return "TABLESPACES";
case OBJECT_TYPE:
return "TYPES";
+ case OBJECT_VARIABLE:
+ return "VARIABLES";
/* these currently aren't used */
case OBJECT_ACCESS_METHOD:
case OBJECT_AGGREGATE:
diff --git a/src/backend/commands/seclabel.c b/src/backend/commands/seclabel.c
index 7ae19b98bb..9b11374535 100644
--- a/src/backend/commands/seclabel.c
+++ b/src/backend/commands/seclabel.c
@@ -92,6 +92,7 @@ SecLabelSupportsObjectType(ObjectType objtype)
case OBJECT_TSPARSER:
case OBJECT_TSTEMPLATE:
case OBJECT_USER_MAPPING:
+ case OBJECT_VARIABLE:
return false;
/*
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 90edd0bb97..8af3c87f1e 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -12686,6 +12686,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
case OCLASS_PUBLICATION_REL:
case OCLASS_SUBSCRIPTION:
case OCLASS_TRANSFORM:
+ case OCLASS_VARIABLE:
/*
* We don't expect any of these sorts of objects to depend on
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 836f427ea8..7a5c1a7d5d 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -5452,6 +5452,23 @@ _copyDropSubscriptionStmt(const DropSubscriptionStmt *from)
return newnode;
}
+static CreateSessionVarStmt *
+_copyCreateSessionVarStmt(const CreateSessionVarStmt *from)
+{
+ CreateSessionVarStmt *newnode = makeNode(CreateSessionVarStmt);
+
+ COPY_NODE_FIELD(variable);
+ COPY_NODE_FIELD(typeName);
+ COPY_NODE_FIELD(collClause);
+ COPY_NODE_FIELD(defexpr);
+ COPY_SCALAR_FIELD(eoxaction);
+ COPY_SCALAR_FIELD(if_not_exists);
+ COPY_SCALAR_FIELD(is_not_null);
+ COPY_SCALAR_FIELD(is_immutable);
+
+ return newnode;
+}
+
/* ****************************************************************
* extensible.h copy functions
* ****************************************************************
@@ -6403,6 +6420,9 @@ copyObjectImpl(const void *from)
case T_DropSubscriptionStmt:
retval = _copyDropSubscriptionStmt(from);
break;
+ case T_CreateSessionVarStmt:
+ retval = _copyCreateSessionVarStmt(from);
+ break;
case T_A_Expr:
retval = _copyA_Expr(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index e013c1bbfe..0ac336df3a 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2742,6 +2742,22 @@ _equalDropSubscriptionStmt(const DropSubscriptionStmt *a,
return true;
}
+static bool
+_equalCreateSessionVarStmt(const CreateSessionVarStmt *a,
+ const CreateSessionVarStmt *b)
+{
+ COMPARE_NODE_FIELD(variable);
+ COMPARE_NODE_FIELD(typeName);
+ COMPARE_NODE_FIELD(collClause);
+ COMPARE_NODE_FIELD(defexpr);
+ COMPARE_SCALAR_FIELD(eoxaction);
+ COMPARE_SCALAR_FIELD(if_not_exists);
+ COMPARE_SCALAR_FIELD(is_not_null);
+ COMPARE_SCALAR_FIELD(is_immutable);
+
+ return true;
+}
+
static bool
_equalCreatePolicyStmt(const CreatePolicyStmt *a, const CreatePolicyStmt *b)
{
@@ -4206,6 +4222,9 @@ equal(const void *a, const void *b)
case T_DropSubscriptionStmt:
retval = _equalDropSubscriptionStmt(a, b);
break;
+ case T_CreateSessionVarStmt:
+ retval = _equalCreateSessionVarStmt(a, b);
+ break;
case T_A_Expr:
retval = _equalA_Expr(a, b);
break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index c9941d9cb4..64ffbbe14d 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -53,6 +53,7 @@
#include "catalog/namespace.h"
#include "catalog/pg_am.h"
#include "catalog/pg_trigger.h"
+#include "catalog/pg_variable.h"
#include "commands/defrem.h"
#include "commands/trigger.h"
#include "nodes/makefuncs.h"
@@ -304,8 +305,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt
CreateDomainStmt CreateExtensionStmt CreateGroupStmt CreateOpClassStmt
CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt
- CreateSchemaStmt CreateSeqStmt CreateStmt CreateStatsStmt CreateTableSpaceStmt
- CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt
+ CreateSchemaStmt CreateSessionVarStmt CreateSeqStmt CreateStmt CreateStatsStmt
+ CreateTableSpaceStmt CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt
CreateAssertionStmt CreateTransformStmt CreateTrigStmt CreateEventTrigStmt
CreateUserStmt CreateUserMappingStmt CreateRoleStmt CreatePolicyStmt
CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt
@@ -478,6 +479,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <ival> OptTemp
%type <ival> OptNoLog
%type <oncommit> OnCommitOption
+%type <ival> OnEOXActionOption
%type <ival> for_locking_strength
%type <node> for_locking_item
@@ -646,6 +648,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <partboundspec> PartitionBoundSpec
%type <list> hash_partbound
%type <defelt> hash_partbound_elem
+%type <node> OptSessionVarDefExpr
+%type <boolean> OptNotNull OptImmutable
%type <node> json_format_clause_opt
@@ -852,8 +856,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
UESCAPE UNBOUNDED UNCONDITIONAL UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN
UNLISTEN UNLOGGED UNTIL UPDATE USER USING
- VACUUM VALID VALIDATE VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING
- VERBOSE VERSION_P VIEW VIEWS VOLATILE
+ VACUUM VALID VALIDATE VALIDATOR VALUE_P VALUES VARCHAR VARIABLE VARIABLES
+ VARIADIC VARYING VERBOSE VERSION_P VIEW VIEWS VOLATILE
WHEN WHERE WHITESPACE_P WINDOW WITH WITHIN WITHOUT WORK WRAPPER WRITE
@@ -1108,6 +1112,7 @@ stmt:
| CreatePolicyStmt
| CreatePLangStmt
| CreateSchemaStmt
+ | CreateSessionVarStmt
| CreateSeqStmt
| CreateStmt
| CreateSubscriptionStmt
@@ -4876,6 +4881,69 @@ create_extension_opt_item:
}
;
+/*****************************************************************************
+ *
+ * QUERY :
+ * CREATE VARIABLE varname [AS] type
+ *
+ *****************************************************************************/
+
+CreateSessionVarStmt:
+ CREATE OptTemp OptImmutable VARIABLE qualified_name opt_as Typename opt_collate_clause OptNotNull OptSessionVarDefExpr OnEOXActionOption
+ {
+ CreateSessionVarStmt *n = makeNode(CreateSessionVarStmt);
+ $5->relpersistence = $2;
+ n->is_immutable = $3;
+ n->variable = $5;
+ n->typeName = $7;
+ n->collClause = (CollateClause *) $8;
+ n->is_not_null = $9;
+ n->defexpr = $10;
+ n->eoxaction = $11;
+ n->if_not_exists = false;
+ $$ = (Node *) n;
+ }
+ | CREATE OptTemp OptImmutable VARIABLE IF_P NOT EXISTS qualified_name opt_as Typename opt_collate_clause OptNotNull OptSessionVarDefExpr OnEOXActionOption
+ {
+ CreateSessionVarStmt *n = makeNode(CreateSessionVarStmt);
+ $8->relpersistence = $2;
+ n->is_immutable = $3;
+ n->variable = $8;
+ n->typeName = $10;
+ n->collClause = (CollateClause *) $11;
+ n->is_not_null = $12;
+ n->defexpr = $13;
+ n->eoxaction = $14;
+ n->if_not_exists = true;
+ $$ = (Node *) n;
+ }
+ ;
+
+OptSessionVarDefExpr: DEFAULT b_expr { $$ = $2; }
+ | /* EMPTY */ { $$ = NULL; }
+ ;
+
+/*
+ * Temporary session variables can be dropped on successful
+ * transaction end like tables. RESET can only be forced on
+ * transaction end. Since the session variables are not
+ * transactional, we have to handle ROLLBACK too.
+ * The clause ON TRANSACTION END is clearer than some
+ * ON COMMIT ROLLBACK RESET clause.
+ */
+OnEOXActionOption: ON COMMIT DROP { $$ = VARIABLE_EOX_DROP; }
+ | ON TRANSACTION END_P RESET { $$ = VARIABLE_EOX_RESET; }
+ | /*EMPTY*/ { $$ = VARIABLE_EOX_NOOP; }
+ ;
+
+OptNotNull: NOT NULL_P { $$ = true; }
+ | /* EMPTY */ { $$ = false; }
+ ;
+
+OptImmutable: IMMUTABLE { $$ = true; }
+ | /* EMPTY */ { $$ = false; }
+ ;
+
/*****************************************************************************
*
* ALTER EXTENSION name UPDATE [ TO version ]
@@ -6563,6 +6631,7 @@ object_type_any_name:
| TEXT_P SEARCH DICTIONARY { $$ = OBJECT_TSDICTIONARY; }
| TEXT_P SEARCH TEMPLATE { $$ = OBJECT_TSTEMPLATE; }
| TEXT_P SEARCH CONFIGURATION { $$ = OBJECT_TSCONFIGURATION; }
+ | VARIABLE { $$ = OBJECT_VARIABLE; }
;
/*
@@ -7368,6 +7437,14 @@ privilege_target:
n->objs = $2;
$$ = n;
}
+ | VARIABLE qualified_name_list
+ {
+ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
+ n->targtype = ACL_TARGET_OBJECT;
+ n->objtype = OBJECT_VARIABLE;
+ n->objs = $2;
+ $$ = n;
+ }
| ALL TABLES IN_P SCHEMA name_list
{
PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
@@ -7408,6 +7485,14 @@ privilege_target:
n->objs = $5;
$$ = n;
}
+ | ALL VARIABLES IN_P SCHEMA name_list
+ {
+ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
+ n->targtype = ACL_TARGET_ALL_IN_SCHEMA;
+ n->objtype = OBJECT_VARIABLE;
+ n->objs = $5;
+ $$ = n;
+ }
;
@@ -7568,6 +7653,7 @@ defacl_privilege_target:
| SEQUENCES { $$ = OBJECT_SEQUENCE; }
| TYPES_P { $$ = OBJECT_TYPE; }
| SCHEMAS { $$ = OBJECT_SCHEMA; }
+ | VARIABLES { $$ = OBJECT_VARIABLE; }
;
@@ -9272,6 +9358,25 @@ RenameStmt: ALTER AGGREGATE aggregate_with_argtypes RENAME TO name
n->missing_ok = false;
$$ = (Node *)n;
}
+ | ALTER VARIABLE any_name RENAME TO name
+ {
+ RenameStmt *n = makeNode(RenameStmt);
+ n->renameType = OBJECT_VARIABLE;
+ n->object = (Node *) $3;
+ n->newname = $6;
+ n->missing_ok = false;
+ $$ = (Node *)n;
+ }
+ | ALTER VARIABLE IF_P EXISTS any_name RENAME TO name
+ {
+ RenameStmt *n = makeNode(RenameStmt);
+ n->renameType = OBJECT_VARIABLE;
+ n->object = (Node *) $5;
+ n->newname = $8;
+ n->missing_ok = true;
+ $$ = (Node *)n;
+ }
+
;
opt_column: COLUMN
@@ -9600,6 +9705,25 @@ AlterObjectSchemaStmt:
n->missing_ok = false;
$$ = (Node *)n;
}
+ | ALTER VARIABLE any_name SET SCHEMA name
+ {
+ AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
+ n->objectType = OBJECT_VARIABLE;
+ n->object = (Node *) $3;
+ n->newschema = $6;
+ n->missing_ok = false;
+ $$ = (Node *)n;
+ }
+ | ALTER VARIABLE IF_P EXISTS any_name SET SCHEMA name
+ {
+ AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
+ n->objectType = OBJECT_VARIABLE;
+ n->object = (Node *) $5;
+ n->newschema = $8;
+ n->missing_ok = true;
+ $$ = (Node *)n;
+ }
+
;
/*****************************************************************************
@@ -9853,6 +9977,14 @@ AlterOwnerStmt: ALTER AGGREGATE aggregate_with_argtypes OWNER TO RoleSpec
n->newowner = $6;
$$ = (Node *)n;
}
+ | ALTER VARIABLE any_name OWNER TO RoleSpec
+ {
+ AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
+ n->objectType = OBJECT_VARIABLE;
+ n->object = (Node *) $3;
+ n->newowner = $6;
+ $$ = (Node *)n;
+ }
;
@@ -17193,6 +17325,8 @@ unreserved_keyword:
| VALIDATE
| VALIDATOR
| VALUE_P
+ | VARIABLE
+ | VARIABLES
| VARYING
| VERSION_P
| VIEW
@@ -17840,6 +17974,8 @@ bare_label_keyword:
| VALUE_P
| VALUES
| VARCHAR
+ | VARIABLE
+ | VARIABLES
| VARIADIC
| VERBOSE
| VERSION_P
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index f364a9b88a..388b8afbd5 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -189,6 +189,7 @@ ClassifyUtilityCommandAsReadOnly(Node *parsetree)
case T_CreateRangeStmt:
case T_CreateRoleStmt:
case T_CreateSchemaStmt:
+ case T_CreateSessionVarStmt:
case T_CreateSeqStmt:
case T_CreateStatsStmt:
case T_CreateStmt:
@@ -1393,6 +1394,10 @@ ProcessUtilitySlow(ParseState *pstate,
}
break;
+ case T_CreateSessionVarStmt:
+ address = DefineSessionVariable(pstate, (CreateSessionVarStmt *) parsetree);
+ break;
+
/*
* ************* object creation / destruction **************
*/
@@ -2326,6 +2331,9 @@ AlterObjectTypeCommandTag(ObjectType objtype)
case OBJECT_STATISTIC_EXT:
tag = CMDTAG_ALTER_STATISTICS;
break;
+ case OBJECT_VARIABLE:
+ tag = CMDTAG_ALTER_VARIABLE;
+ break;
default:
tag = CMDTAG_UNKNOWN;
break;
@@ -2634,6 +2642,9 @@ CreateCommandTag(Node *parsetree)
case OBJECT_STATISTIC_EXT:
tag = CMDTAG_DROP_STATISTICS;
break;
+ case OBJECT_VARIABLE:
+ tag = CMDTAG_DROP_VARIABLE;
+ break;
default:
tag = CMDTAG_UNKNOWN;
}
@@ -3210,6 +3221,10 @@ CreateCommandTag(Node *parsetree)
}
break;
+ case T_CreateSessionVarStmt:
+ tag = CMDTAG_CREATE_VARIABLE;
+ break;
+
default:
elog(WARNING, "unrecognized node type: %d",
(int) nodeTag(parsetree));
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index 8bdba1c42a..53d7d59fae 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -315,6 +315,12 @@ aclparse(const char *s, AclItem *aip)
case ACL_ALTER_SYSTEM_CHR:
read = ACL_ALTER_SYSTEM;
break;
+ case ACL_READ_CHR:
+ read = ACL_READ;
+ break;
+ case ACL_WRITE_CHR:
+ read = ACL_WRITE;
+ break;
case 'R': /* ignore old RULE privileges */
read = 0;
break;
@@ -807,6 +813,10 @@ acldefault(ObjectType objtype, Oid ownerId)
world_default = ACL_NO_RIGHTS;
owner_default = ACL_ALL_RIGHTS_PARAMETER_ACL;
break;
+ case OBJECT_VARIABLE:
+ world_default = ACL_NO_RIGHTS;
+ owner_default = ACL_ALL_RIGHTS_VARIABLE;
+ break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
world_default = ACL_NO_RIGHTS; /* keep compiler quiet */
@@ -904,6 +914,9 @@ acldefault_sql(PG_FUNCTION_ARGS)
case 'T':
objtype = OBJECT_TYPE;
break;
+ case 'V':
+ objtype = OBJECT_VARIABLE;
+ break;
default:
elog(ERROR, "unrecognized objtype abbreviation: %c", objtypec);
}
@@ -1624,6 +1637,10 @@ convert_priv_string(text *priv_type_text)
return ACL_ALTER_SYSTEM;
if (pg_strcasecmp(priv_type, "RULE") == 0)
return 0; /* ignore old RULE privileges */
+ if (pg_strcasecmp(priv_type, "READ") == 0)
+ return ACL_READ;
+ if (pg_strcasecmp(priv_type, "WRITE") == 0)
+ return ACL_WRITE;
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -1722,6 +1739,10 @@ convert_aclright_to_string(int aclright)
return "SET";
case ACL_ALTER_SYSTEM:
return "ALTER SYSTEM";
+ case ACL_READ:
+ return "READ";
+ case ACL_WRITE:
+ return "WRITE";
default:
elog(ERROR, "unrecognized aclright: %d", aclright);
return NULL;
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 1b7e11b93e..4459b18287 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -35,6 +35,7 @@
#include "catalog/pg_statistic.h"
#include "catalog/pg_transform.h"
#include "catalog/pg_type.h"
+#include "catalog/pg_variable.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "utils/array.h"
@@ -3578,3 +3579,92 @@ get_index_isclustered(Oid index_oid)
return isclustered;
}
+
+/* ---------- PG_VARIABLE CACHE ---------- */
+
+
+/*
+ * get_varname_varid
+ * Given name and namespace of variable, look up the OID.
+ */
+Oid
+get_varname_varid(const char *varname, Oid varnamespace)
+{
+ return GetSysCacheOid2(VARIABLENAMENSP, Anum_pg_variable_oid,
+ PointerGetDatum(varname),
+ ObjectIdGetDatum(varnamespace));
+}
+
+/*
+ * get_session_variable_name
+ * Returns a palloc'd copy of the name of a given session variable.
+ */
+char *
+get_session_variable_name(Oid varid)
+{
+ HeapTuple tup;
+ Form_pg_variable varform;
+ char *varname;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for session variable %u", varid);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ varname = pstrdup(NameStr(varform->varname));
+
+ ReleaseSysCache(tup);
+
+ return varname;
+}
+
+/*
+ * get_session_variable_namespace
+ * Returns the pg_namespace OID associated with a given session variable.
+ */
+Oid
+get_session_variable_namespace(Oid varid)
+{
+ HeapTuple tup;
+ Form_pg_variable varform;
+ Oid varnamespace;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for variable %u", varid);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ varnamespace = varform->varnamespace;
+
+ ReleaseSysCache(tup);
+
+ return varnamespace;
+}
+
+/*
+ * Returns the type, typmod and collid of the given session variable.
+ */
+void
+get_session_variable_type_typmod_collid(Oid varid, Oid *typid, int32 *typmod,
+ Oid *collid)
+{
+ HeapTuple tup;
+ Form_pg_variable varform;
+
+ tup = SearchSysCache1(VARIABLEOID, ObjectIdGetDatum(varid));
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for session variable %u", varid);
+
+ varform = (Form_pg_variable) GETSTRUCT(tup);
+
+ *typid = varform->vartype;
+ *typmod = varform->vartypmod;
+ *collid = varform->varcollation;
+
+ ReleaseSysCache(tup);
+}
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 1912b12146..7b16f75d1f 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -75,6 +75,7 @@
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
#include "catalog/pg_user_mapping.h"
+#include "catalog/pg_variable.h"
#include "lib/qunique.h"
#include "utils/catcache.h"
#include "utils/rel.h"
@@ -1037,6 +1038,28 @@ static const struct cachedesc cacheinfo[] = {
0
},
2
+ },
+ {VariableRelationId, /* VARIABLENAMENSP */
+ VariableNameNspIndexId,
+ 2,
+ {
+ Anum_pg_variable_varname,
+ Anum_pg_variable_varnamespace,
+ 0,
+ 0
+ },
+ 8
+ },
+ {VariableRelationId, /* VARIABLEOID */
+ VariableObjectIndexId,
+ 1,
+ {
+ Anum_pg_variable_oid,
+ 0,
+ 0,
+ 0
+ },
+ 8
}
};
diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index b2e72b3243..a1396f2352 100644
--- a/src/backend/utils/fmgr/fmgr.c
+++ b/src/backend/utils/fmgr/fmgr.c
@@ -1910,15 +1910,19 @@ get_call_expr_arg_stable(Node *expr, int argnum)
arg = (Node *) list_nth(args, argnum);
/*
- * Either a true Const or an external Param will have a value that doesn't
- * change during the execution of the query. In future we might want to
- * consider other cases too, e.g. now().
+ * Either a true Const or an external Param or variable will have a value
+ * that doesn't change during the execution of the query. In future we
+ * might want to consider other cases too, e.g. now().
*/
if (IsA(arg, Const))
return true;
- if (IsA(arg, Param) &&
- ((Param *) arg)->paramkind == PARAM_EXTERN)
- return true;
+ if (IsA(arg, Param))
+ {
+ Param *p = (Param *) arg;
+
+ if (p->paramkind == PARAM_EXTERN || p->paramkind == PARAM_VARIABLE)
+ return true;
+ }
return false;
}
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index d027075a4c..423864b9a2 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -126,10 +126,11 @@ typedef enum ObjectClass
OCLASS_PUBLICATION_NAMESPACE, /* pg_publication_namespace */
OCLASS_PUBLICATION_REL, /* pg_publication_rel */
OCLASS_SUBSCRIPTION, /* pg_subscription */
- OCLASS_TRANSFORM /* pg_transform */
+ OCLASS_TRANSFORM, /* pg_transform */
+ OCLASS_VARIABLE /* pg_variable */
} ObjectClass;
-#define LAST_OCLASS OCLASS_TRANSFORM
+#define LAST_OCLASS OCLASS_VARIABLE
/* flag bits for performDeletion/performMultipleDeletions: */
#define PERFORM_DELETION_INTERNAL 0x0001 /* internal action */
diff --git a/src/include/catalog/namespace.h b/src/include/catalog/namespace.h
index 1bc55c01a5..c5d445b9e1 100644
--- a/src/include/catalog/namespace.h
+++ b/src/include/catalog/namespace.h
@@ -96,6 +96,8 @@ extern Oid TypenameGetTypid(const char *typname);
extern Oid TypenameGetTypidExtended(const char *typname, bool temp_ok);
extern bool TypeIsVisible(Oid typid);
+extern bool VariableIsVisible(Oid varid);
+
extern FuncCandidateList FuncnameGetCandidates(List *names,
int nargs, List *argnames,
bool expand_variadic,
@@ -164,6 +166,10 @@ extern void SetTempNamespaceState(Oid tempNamespaceId,
Oid tempToastNamespaceId);
extern void ResetTempTableNamespace(void);
+extern List *NamesFromList(List *names);
+extern Oid LookupVariable(const char *nspname, const char *varname, bool missing_ok);
+extern Oid IdentifyVariable(List *names, char **attrname, bool lockit, bool *not_unique);
+
extern OverrideSearchPath *GetOverrideSearchPath(MemoryContext context);
extern OverrideSearchPath *CopyOverrideSearchPath(OverrideSearchPath *path);
extern bool OverrideSearchPathMatchesCurrent(OverrideSearchPath *path);
diff --git a/src/include/catalog/pg_default_acl.h b/src/include/catalog/pg_default_acl.h
index 2a79155636..672c571562 100644
--- a/src/include/catalog/pg_default_acl.h
+++ b/src/include/catalog/pg_default_acl.h
@@ -66,6 +66,7 @@ DECLARE_UNIQUE_INDEX_PKEY(pg_default_acl_oid_index, 828, DefaultAclOidIndexId, o
#define DEFACLOBJ_FUNCTION 'f' /* function */
#define DEFACLOBJ_TYPE 'T' /* type */
#define DEFACLOBJ_NAMESPACE 'n' /* namespace */
+#define DEFACLOBJ_VARIABLE 'V' /* variable */
#endif /* EXPOSE_TO_CLIENT_CODE */
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 6d378ff785..ce0003c1bf 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6275,6 +6275,9 @@
proname => 'pg_collation_is_visible', procost => '10', provolatile => 's',
prorettype => 'bool', proargtypes => 'oid',
prosrc => 'pg_collation_is_visible' },
+{ oid => '9221', descr => 'is session variable visible in search path?',
+ proname => 'pg_variable_is_visible', procost => '10', provolatile => 's',
+ prorettype => 'bool', proargtypes => 'oid', prosrc => 'pg_variable_is_visible' },
{ oid => '2854', descr => 'get OID of current session\'s temp schema, if any',
proname => 'pg_my_temp_schema', provolatile => 's', proparallel => 'r',
diff --git a/src/include/catalog/pg_variable.h b/src/include/catalog/pg_variable.h
new file mode 100644
index 0000000000..3dd4455921
--- /dev/null
+++ b/src/include/catalog/pg_variable.h
@@ -0,0 +1,105 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_variable.h
+ * definition of session variables system catalog (pg_variables)
+ *
+ *
+ * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_variable.h
+ *
+ * NOTES
+ * The Catalog.pm module reads this file and derives schema
+ * information.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_VARIABLE_H
+#define PG_VARIABLE_H
+
+#include "catalog/genbki.h"
+#include "catalog/objectaddress.h"
+#include "catalog/pg_variable_d.h"
+#include "utils/acl.h"
+
+/* ----------------
+ * pg_variable definition. cpp turns this into
+ * typedef struct FormData_pg_variable
+ * ----------------
+ */
+CATALOG(pg_variable,9222,VariableRelationId)
+{
+ Oid oid; /* oid */
+ NameData varname; /* variable name */
+ Oid varnamespace; /* OID of namespace containing variable class */
+ Oid vartype; /* OID of entry in pg_type for variable's type */
+ int32 vartypmod; /* typmode for variable's type */
+ Oid varowner; /* class owner */
+ Oid varcollation; /* variable collation */
+ bool varisnotnull; /* Don't allow NULL */
+ bool varisimmutable; /* Don't allow changes */
+ char vareoxaction; /* action on transaction end */
+
+#ifdef CATALOG_VARLEN /* variable-length fields start here */
+
+ /* list of expression trees for variable default (NULL if none) */
+ pg_node_tree vardefexpr BKI_DEFAULT(_null_);
+
+ aclitem varacl[1] BKI_DEFAULT(_null_); /* access permissions */
+
+#endif
+} FormData_pg_variable;
+
+typedef enum VariableEOXAction
+{
+ VARIABLE_EOX_NOOP = 'n', /* NOOP */
+ VARIABLE_EOX_DROP = 'd', /* ON COMMIT DROP */
+ VARIABLE_EOX_RESET = 'r', /* ON TRANSACTION END RESET */
+} VariableEOXAction;
+
+/* ----------------
+ * Form_pg_variable corresponds to a pointer to a tuple with
+ * the format of pg_variable relation.
+ * ----------------
+ */
+typedef FormData_pg_variable *Form_pg_variable;
+
+DECLARE_UNIQUE_INDEX_PKEY(pg_variable_oid_index, 9223, VariableOidIndexId, on pg_variable using btree(oid oid_ops));
+#define VariableObjectIndexId 9223
+
+DECLARE_UNIQUE_INDEX(pg_variable_varname_nsp_index, 9224, VariableNameNspIndexId, on pg_variable using btree(varname name_ops, varnamespace oid_ops));
+#define VariableNameNspIndexId 9224
+
+typedef struct Variable
+{
+ Oid oid;
+ char *name;
+ Oid namespace;
+ Oid typid;
+ int32 typmod;
+ Oid owner;
+ Oid collation;
+ bool is_not_null;
+ bool is_immutable;
+ VariableEOXAction eoxaction;
+ bool has_defexpr;
+ Node *defexpr;
+} Variable;
+
+extern void initVariable(Variable *var,
+ Oid varid,
+ bool fast_only);
+extern ObjectAddress VariableCreate(const char *varName,
+ Oid varNamespace,
+ Oid varType,
+ int32 varTypmod,
+ Oid varOwner,
+ Oid varCollation,
+ Node *varDefexpr,
+ VariableEOXAction eoxaction,
+ bool is_not_null,
+ bool if_not_exists,
+ bool is_immutable);
+
+#endif /* PG_VARIABLE_H */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 340d28f4e1..cec659aa21 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -372,6 +372,7 @@ typedef enum NodeTag
T_CreateTableAsStmt,
T_CreateSeqStmt,
T_AlterSeqStmt,
+ T_CreateSessionVarStmt,
T_VariableSetStmt,
T_VariableShowStmt,
T_DiscardStmt,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index da02658c81..0f42310f9b 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -94,7 +94,9 @@ typedef uint32 AclMode; /* a bitmask of privilege bits */
#define ACL_CONNECT (1<<11) /* for databases */
#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_READ (1<<14) /* for variables */
+#define ACL_WRITE (1<<15) /* for variables */
+#define N_ACL_RIGHTS 16 /* 1 plus the last 1<<x */
#define ACL_NO_RIGHTS 0
/* Currently, SELECT ... FOR [KEY] UPDATE/SHARE requires UPDATE privileges */
#define ACL_SELECT_FOR_UPDATE ACL_UPDATE
@@ -2187,6 +2189,7 @@ typedef enum ObjectType
OBJECT_TSTEMPLATE,
OBJECT_TYPE,
OBJECT_USER_MAPPING,
+ OBJECT_VARIABLE,
OBJECT_VIEW
} ObjectType;
@@ -3311,6 +3314,25 @@ typedef struct AlterStatsStmt
bool missing_ok; /* skip error if statistics object is missing */
} AlterStatsStmt;
+
+/* ----------------------
+ * {Create|Alter} VARIABLE Statement
+ * ----------------------
+ */
+typedef struct CreateSessionVarStmt
+{
+ NodeTag type;
+ RangeVar *variable; /* the variable to create */
+ TypeName *typeName; /* the type of variable */
+ CollateClause *collClause;
+ Node *defexpr; /* default expression */
+ char eoxaction; /* on commit action */
+ bool if_not_exists; /* do nothing if it already exists */
+ bool is_not_null; /* Disallow nulls */
+ bool is_immutable; /* Don't allow changes */
+} CreateSessionVarStmt;
+
+
/* ----------------------
* Create Function Statement
* ----------------------
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 8a2ab405a2..114a97865d 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -479,6 +479,8 @@ PG_KEYWORD("validator", VALIDATOR, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("value", VALUE_P, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("values", VALUES, COL_NAME_KEYWORD, BARE_LABEL)
PG_KEYWORD("varchar", VARCHAR, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("variable", VARIABLE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("variables", VARIABLES, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("variadic", VARIADIC, RESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("varying", VARYING, UNRESERVED_KEYWORD, AS_LABEL)
PG_KEYWORD("verbose", VERBOSE, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL)
diff --git a/src/include/tcop/cmdtaglist.h b/src/include/tcop/cmdtaglist.h
index 2b1163ce33..e76bec9fad 100644
--- a/src/include/tcop/cmdtaglist.h
+++ b/src/include/tcop/cmdtaglist.h
@@ -68,6 +68,7 @@ PG_CMDTAG(CMDTAG_ALTER_TRANSFORM, "ALTER TRANSFORM", true, false, false)
PG_CMDTAG(CMDTAG_ALTER_TRIGGER, "ALTER TRIGGER", true, false, false)
PG_CMDTAG(CMDTAG_ALTER_TYPE, "ALTER TYPE", true, true, false)
PG_CMDTAG(CMDTAG_ALTER_USER_MAPPING, "ALTER USER MAPPING", true, false, false)
+PG_CMDTAG(CMDTAG_ALTER_VARIABLE, "ALTER VARIABLE", true, false, false)
PG_CMDTAG(CMDTAG_ALTER_VIEW, "ALTER VIEW", true, false, false)
PG_CMDTAG(CMDTAG_ANALYZE, "ANALYZE", false, false, false)
PG_CMDTAG(CMDTAG_BEGIN, "BEGIN", false, false, false)
@@ -123,6 +124,7 @@ PG_CMDTAG(CMDTAG_CREATE_TRANSFORM, "CREATE TRANSFORM", true, false, false)
PG_CMDTAG(CMDTAG_CREATE_TRIGGER, "CREATE TRIGGER", true, false, false)
PG_CMDTAG(CMDTAG_CREATE_TYPE, "CREATE TYPE", true, false, false)
PG_CMDTAG(CMDTAG_CREATE_USER_MAPPING, "CREATE USER MAPPING", true, false, false)
+PG_CMDTAG(CMDTAG_CREATE_VARIABLE, "CREATE VARIABLE", true, false, false)
PG_CMDTAG(CMDTAG_CREATE_VIEW, "CREATE VIEW", true, false, false)
PG_CMDTAG(CMDTAG_DEALLOCATE, "DEALLOCATE", false, false, false)
PG_CMDTAG(CMDTAG_DEALLOCATE_ALL, "DEALLOCATE ALL", false, false, false)
@@ -175,6 +177,7 @@ PG_CMDTAG(CMDTAG_DROP_TRANSFORM, "DROP TRANSFORM", true, false, false)
PG_CMDTAG(CMDTAG_DROP_TRIGGER, "DROP TRIGGER", true, false, false)
PG_CMDTAG(CMDTAG_DROP_TYPE, "DROP TYPE", true, false, false)
PG_CMDTAG(CMDTAG_DROP_USER_MAPPING, "DROP USER MAPPING", true, false, false)
+PG_CMDTAG(CMDTAG_DROP_VARIABLE, "DROP VARIABLE", true, false, false)
PG_CMDTAG(CMDTAG_DROP_VIEW, "DROP VIEW", true, false, false)
PG_CMDTAG(CMDTAG_EXECUTE, "EXECUTE", false, false, false)
PG_CMDTAG(CMDTAG_EXPLAIN, "EXPLAIN", false, false, false)
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index 48f7d72add..ecdc49e59a 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -148,9 +148,11 @@ typedef struct ArrayType Acl;
#define ACL_CONNECT_CHR 'c'
#define ACL_SET_CHR 's'
#define ACL_ALTER_SYSTEM_CHR 'A'
+#define ACL_READ_CHR 'S' /* 'R' is occupated by old RULE priv */
+#define ACL_WRITE_CHR 'W'
/* string holding all privilege code chars, in order by bitmask position */
-#define ACL_ALL_RIGHTS_STR "arwdDxtXUCTcsA"
+#define ACL_ALL_RIGHTS_STR "arwdDxtXUCTcsASW"
/*
* Bitmasks defining "all rights" for each supported object type
@@ -168,6 +170,7 @@ typedef struct ArrayType Acl;
#define ACL_ALL_RIGHTS_SCHEMA (ACL_USAGE|ACL_CREATE)
#define ACL_ALL_RIGHTS_TABLESPACE (ACL_CREATE)
#define ACL_ALL_RIGHTS_TYPE (ACL_USAGE)
+#define ACL_ALL_RIGHTS_VARIABLE (ACL_READ|ACL_WRITE)
/* operation codes for pg_*_aclmask */
typedef enum
@@ -266,7 +269,8 @@ extern AclMode pg_foreign_server_aclmask(Oid srv_oid, Oid roleid,
AclMode mask, AclMaskHow how);
extern AclMode pg_type_aclmask(Oid type_oid, Oid roleid,
AclMode mask, AclMaskHow how);
-
+extern AclMode pg_variable_aclmask(Oid var_oid, Oid roleid,
+ AclMode mask, AclMaskHow how);
extern AclResult pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum,
Oid roleid, AclMode mode);
extern AclResult pg_attribute_aclcheck_ext(Oid table_oid, AttrNumber attnum,
@@ -291,6 +295,7 @@ extern AclResult pg_tablespace_aclcheck(Oid spc_oid, Oid roleid, AclMode mode);
extern AclResult pg_foreign_data_wrapper_aclcheck(Oid fdw_oid, Oid roleid, AclMode mode);
extern AclResult pg_foreign_server_aclcheck(Oid srv_oid, Oid roleid, AclMode mode);
extern AclResult pg_type_aclcheck(Oid type_oid, Oid roleid, AclMode mode);
+extern AclResult pg_variable_aclcheck(Oid type_oid, Oid roleid, AclMode mode);
extern void aclcheck_error(AclResult aclerr, ObjectType objtype,
const char *objectname);
@@ -327,6 +332,7 @@ extern bool pg_extension_ownercheck(Oid ext_oid, Oid roleid);
extern bool pg_publication_ownercheck(Oid pub_oid, Oid roleid);
extern bool pg_subscription_ownercheck(Oid sub_oid, Oid roleid);
extern bool pg_statistics_object_ownercheck(Oid stat_oid, Oid roleid);
+extern bool pg_variable_ownercheck(Oid stat_oid, Oid roleid);
extern bool has_createrole_privilege(Oid roleid);
extern bool has_bypassrls_privilege(Oid roleid);
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index b8dd27d4a9..faac8ed8e7 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -132,6 +132,7 @@ extern char get_func_prokind(Oid funcid);
extern bool get_func_leakproof(Oid funcid);
extern RegProcedure get_func_support(Oid funcid);
extern Oid get_relname_relid(const char *relname, Oid relnamespace);
+extern Oid get_varname_varid(const char *varname, Oid varnamespace);
extern char *get_rel_name(Oid relid);
extern Oid get_rel_namespace(Oid relid);
extern Oid get_rel_type_id(Oid relid);
@@ -199,6 +200,13 @@ extern bool get_index_isreplident(Oid index_oid);
extern bool get_index_isvalid(Oid index_oid);
extern bool get_index_isclustered(Oid index_oid);
+extern char *get_session_variable_name(Oid varid);
+extern Oid get_session_variable_namespace(Oid varid);
+extern void get_session_variable_type_typmod_collid(Oid varid,
+ Oid *typid,
+ int32 *typmod,
+ Oid *collid);
+
#define type_is_array(typid) (get_element_type(typid) != InvalidOid)
/* type_is_array_domain accepts both plain arrays and domains over arrays */
#define type_is_array_domain(typid) (get_base_element_type(typid) != InvalidOid)
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 4463ea66be..3d18dc88ac 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -113,9 +113,11 @@ enum SysCacheIdentifier
TYPENAMENSP,
TYPEOID,
USERMAPPINGOID,
- USERMAPPINGUSERSERVER
+ USERMAPPINGUSERSERVER,
+ VARIABLENAMENSP,
+ VARIABLEOID
-#define SysCacheSize (USERMAPPINGUSERSERVER + 1)
+#define SysCacheSize (VARIABLEOID + 1)
};
extern void InitCatalogCache(void);
--
2.35.1