Hi

Most important features:
>>
>> 1. the values are stored in native types
>> 2. access to content is protected by ACL - like the content of tables
>> 3. the content is not MVCC based - no any cost of UPDATE
>> 4. simple API allows access to content of variables from any supported
>> environment.
>>
>
> next update - setattr, getattr functions are working now
>

new update - rebased after partitioning patch

Regards

Pavel


>
> notes, comments?
>
> Regards
>
> Pavel
>
>
>>
>> Regards
>>
>> Pavel
>>
>>
>>> --
>>>  Craig Ringer                   http://www.2ndQuadrant.com/
>>>  PostgreSQL Development, 24x7 Support, Training & Services
>>>
>>
>>
>
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 3086021..6bd88b8 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -274,6 +274,9 @@ restrict_and_check_grant(bool is_grant, AclMode avail_goptions, bool all_privs,
 		case ACL_KIND_TYPE:
 			whole_mask = ACL_ALL_RIGHTS_TYPE;
 			break;
+		case ACL_KIND_VARIABLE:
+			whole_mask = ACL_ALL_RIGHTS_VARIABLE;
+			break;
 		default:
 			elog(ERROR, "unrecognized object kind: %d", objkind);
 			/* not reached, but keep compiler quiet */
@@ -488,6 +491,10 @@ ExecuteGrantStmt(GrantStmt *stmt)
 			all_privileges = ACL_ALL_RIGHTS_FOREIGN_SERVER;
 			errormsg = gettext_noop("invalid privilege type %s for foreign server");
 			break;
+		case ACL_OBJECT_VARIABLE:
+			all_privileges = ACL_ALL_RIGHTS_VARIABLE;
+			errormsg = gettext_noop("invalid privilege type %s for variable");
+			break;
 		default:
 			elog(ERROR, "unrecognized GrantStmt.objtype: %d",
 				 (int) stmt->objtype);
@@ -558,6 +565,7 @@ ExecGrantStmt_oids(InternalGrant *istmt)
 	{
 		case ACL_OBJECT_RELATION:
 		case ACL_OBJECT_SEQUENCE:
+		case ACL_OBJECT_VARIABLE:
 			ExecGrant_Relation(istmt);
 			break;
 		case ACL_OBJECT_DATABASE:
@@ -625,6 +633,7 @@ objectNamesToOids(GrantObjectType objtype, List *objnames)
 	{
 		case ACL_OBJECT_RELATION:
 		case ACL_OBJECT_SEQUENCE:
+		case ACL_OBJECT_VARIABLE:
 			foreach(cell, objnames)
 			{
 				RangeVar   *relvar = (RangeVar *) lfirst(cell);
@@ -775,6 +784,10 @@ objectsInSchemaToOids(GrantObjectType objtype, List *nspnames)
 				objs = getRelationsInNamespace(namespaceId, RELKIND_SEQUENCE);
 				objects = list_concat(objects, objs);
 				break;
+			case ACL_OBJECT_VARIABLE:
+				objs = getRelationsInNamespace(namespaceId, RELKIND_VARIABLE);
+				objects = list_concat(objects, objs);
+				break;
 			case ACL_OBJECT_FUNCTION:
 				{
 					ScanKeyData key[1];
@@ -950,6 +963,10 @@ ExecAlterDefaultPrivilegesStmt(ParseState *pstate, AlterDefaultPrivilegesStmt *s
 			all_privileges = ACL_ALL_RIGHTS_TYPE;
 			errormsg = gettext_noop("invalid privilege type %s for type");
 			break;
+		case ACL_OBJECT_VARIABLE:
+			all_privileges = ACL_ALL_RIGHTS_VARIABLE;
+			errormsg = gettext_noop("invalid privilege type %s for variable");
+			break;
 		default:
 			elog(ERROR, "unrecognized GrantStmt.objtype: %d",
 				 (int) action->objtype);
@@ -1137,6 +1154,12 @@ SetDefaultACL(InternalDefaultACL *iacls)
 				this_privileges = ACL_ALL_RIGHTS_TYPE;
 			break;
 
+		case ACL_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);
@@ -1363,6 +1386,9 @@ RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid)
 			case DEFACLOBJ_TYPE:
 				iacls.objtype = ACL_OBJECT_TYPE;
 				break;
+			case DEFACLOBJ_VARIABLE:
+				iacls.objtype = ACL_OBJECT_VARIABLE;
+				break;
 			default:
 				/* Shouldn't get here */
 				elog(ERROR, "unexpected default ACL type: %d",
@@ -1710,7 +1736,7 @@ ExecGrant_Attribute(InternalGrant *istmt, Oid relOid, const char *relname,
 }
 
 /*
- *	This processes both sequences and non-sequences.
+ *	This processes both sequences, variables and others.
  */
 static void
 ExecGrant_Relation(InternalGrant *istmt)
@@ -1767,11 +1793,21 @@ ExecGrant_Relation(InternalGrant *istmt)
 					 errmsg("\"%s\" is not a sequence",
 							NameStr(pg_class_tuple->relname))));
 
+		/* Used GRANT VARIABLE on a non-variable? */
+		if (istmt->objtype == ACL_OBJECT_VARIABLE &&
+			pg_class_tuple->relkind != RELKIND_VARIABLE)
+			ereport(ERROR,
+					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+					 errmsg("\"%s\" is not a variable",
+							NameStr(pg_class_tuple->relname))));
+
 		/* Adjust the default permissions based on object type */
 		if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
 		{
 			if (pg_class_tuple->relkind == RELKIND_SEQUENCE)
 				this_privileges = ACL_ALL_RIGHTS_SEQUENCE;
+			else if (pg_class_tuple->relkind == RELKIND_VARIABLE)
+				this_privileges = ACL_ALL_RIGHTS_VARIABLE;
 			else
 				this_privileges = ACL_ALL_RIGHTS_RELATION;
 		}
@@ -1864,6 +1900,9 @@ ExecGrant_Relation(InternalGrant *istmt)
 				case RELKIND_SEQUENCE:
 					old_acl = acldefault(ACL_OBJECT_SEQUENCE, ownerId);
 					break;
+				case RELKIND_VARIABLE:
+					old_acl = acldefault(ACL_OBJECT_VARIABLE, ownerId);
+					break;
 				default:
 					old_acl = acldefault(ACL_OBJECT_RELATION, ownerId);
 					break;
@@ -1908,6 +1947,9 @@ ExecGrant_Relation(InternalGrant *istmt)
 				case RELKIND_SEQUENCE:
 					aclkind = ACL_KIND_SEQUENCE;
 					break;
+				case RELKIND_VARIABLE:
+					aclkind = ACL_KIND_VARIABLE;
+					break;
 				default:
 					aclkind = ACL_KIND_CLASS;
 					break;
@@ -3343,6 +3385,8 @@ static const char *const no_priv_msg[MAX_ACL_KIND] =
 	gettext_noop("permission denied for event trigger %s"),
 	/* ACL_KIND_EXTENSION */
 	gettext_noop("permission denied for extension %s"),
+	/* ACL_KIND_VARIABLE */
+	gettext_noop("permission denied for variable %s"),
 };
 
 static const char *const not_owner_msg[MAX_ACL_KIND] =
@@ -3389,6 +3433,8 @@ static const char *const not_owner_msg[MAX_ACL_KIND] =
 	gettext_noop("must be owner of event trigger %s"),
 	/* ACL_KIND_EXTENSION */
 	gettext_noop("must be owner of extension %s"),
+	/* ACL_KIND_VARIABLE */
+	gettext_noop("must be owner of variable %s"),
 };
 
 
@@ -3474,6 +3520,7 @@ pg_aclmask(AclObjectKind objkind, Oid table_oid, AttrNumber attnum, Oid roleid,
 				pg_attribute_aclmask(table_oid, attnum, roleid, mask, how);
 		case ACL_KIND_CLASS:
 		case ACL_KIND_SEQUENCE:
+		case ACL_KIND_VARIABLE:
 			return pg_class_aclmask(table_oid, roleid, mask, how);
 		case ACL_KIND_DATABASE:
 			return pg_database_aclmask(table_oid, roleid, mask, how);
@@ -3681,6 +3728,9 @@ pg_class_aclmask(Oid table_oid, Oid roleid,
 			case RELKIND_SEQUENCE:
 				acl = acldefault(ACL_OBJECT_SEQUENCE, ownerId);
 				break;
+			case RELKIND_VARIABLE:
+				acl = acldefault(ACL_OBJECT_VARIABLE, ownerId);
+				break;
 			default:
 				acl = acldefault(ACL_OBJECT_RELATION, ownerId);
 				break;
@@ -5191,6 +5241,10 @@ get_user_default_acl(GrantObjectType objtype, Oid ownerId, Oid nsp_oid)
 			defaclobjtype = DEFACLOBJ_TYPE;
 			break;
 
+		case ACL_OBJECT_VARIABLE:
+			defaclobjtype = DEFACLOBJ_VARIABLE;
+			break;
+
 		default:
 			return NULL;
 	}
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index c09c9f2..818f866 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -291,6 +291,7 @@ heap_create(const char *relname,
 		case RELKIND_VIEW:
 		case RELKIND_COMPOSITE_TYPE:
 		case RELKIND_FOREIGN_TABLE:
+		case RELKIND_VARIABLE:
 			create_storage = false;
 
 			/*
@@ -421,10 +422,10 @@ CheckAttributeNamesTypes(TupleDesc tupdesc, char relkind,
 	/*
 	 * first check for collision with system attribute names
 	 *
-	 * Skip this for a view or type relation, since those don't have system
+	 * Skip this for a view, variable or type relation, since those don't have system
 	 * attributes.
 	 */
-	if (relkind != RELKIND_VIEW && relkind != RELKIND_COMPOSITE_TYPE)
+	if (relkind != RELKIND_VIEW && relkind != RELKIND_COMPOSITE_TYPE && relkind != RELKIND_VARIABLE)
 	{
 		for (i = 0; i < natts; i++)
 		{
@@ -1115,7 +1116,7 @@ heap_create_with_catalog(const char *relname,
 			(relkind == RELKIND_RELATION || relkind == RELKIND_SEQUENCE ||
 			 relkind == RELKIND_VIEW || relkind == RELKIND_MATVIEW ||
 			 relkind == RELKIND_COMPOSITE_TYPE || relkind == RELKIND_FOREIGN_TABLE ||
-			 relkind == RELKIND_PARTITIONED_TABLE))
+			 relkind == RELKIND_PARTITIONED_TABLE || relkind == RELKIND_VARIABLE))
 		{
 			if (!OidIsValid(binary_upgrade_next_heap_pg_class_oid))
 				ereport(ERROR,
@@ -1157,6 +1158,10 @@ heap_create_with_catalog(const char *relname,
 				relacl = get_user_default_acl(ACL_OBJECT_SEQUENCE, ownerid,
 											  relnamespace);
 				break;
+			case RELKIND_VARIABLE:
+				relacl = get_user_default_acl(ACL_OBJECT_VARIABLE, ownerid,
+											  relnamespace);
+				break;
 			default:
 				relacl = NULL;
 				break;
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index bb4b080..d66d6fa 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -656,6 +656,10 @@ static const struct object_type_map
 	/* OCLASS_TRANSFORM */
 	{
 		"transform", OBJECT_TRANSFORM
+	},
+	/* OCLASS_VARIABLE */
+	{
+		"variable", OBJECT_VARIABLE
 	}
 };
 
@@ -762,6 +766,7 @@ get_object_address(ObjectType objtype, List *objname, List *objargs,
 			case OBJECT_VIEW:
 			case OBJECT_MATVIEW:
 			case OBJECT_FOREIGN_TABLE:
+			case OBJECT_VARIABLE:
 				address =
 					get_relation_by_qualified_name(objtype, objname,
 												   &relation, lockmode,
@@ -1232,6 +1237,12 @@ get_relation_by_qualified_name(ObjectType objtype, List *objname,
 						 errmsg("\"%s\" is not a foreign table",
 								RelationGetRelationName(relation))));
 			break;
+		case OBJECT_VARIABLE:
+			if (relation->rd_rel->relkind != RELKIND_VARIABLE)
+				ereport(ERROR,
+						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+						 errmsg("\"%s\" is not a variable",
+								RelationGetRelationName(relation))));
 		default:
 			elog(ERROR, "unrecognized objtype: %d", (int) objtype);
 			break;
@@ -2081,6 +2092,7 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
 		case OBJECT_TRIGGER:
 		case OBJECT_POLICY:
 		case OBJECT_TABCONSTRAINT:
+		case OBJECT_VARIABLE:
 			if (!pg_class_ownercheck(RelationGetRelid(relation), roleid))
 				aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
 							   RelationGetRelationName(relation));
@@ -3277,6 +3289,10 @@ getRelationDescription(StringInfo buffer, Oid relid)
 			appendStringInfo(buffer, _("foreign table %s"),
 							 relname);
 			break;
+		case RELKIND_VARIABLE:
+			appendStringInfo(buffer, _("variable %s"),
+							 relname);
+			break;
 		default:
 			/* shouldn't get here */
 			appendStringInfo(buffer, _("relation %s"),
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
index 6b3742c..9be5174 100644
--- a/src/backend/commands/Makefile
+++ b/src/backend/commands/Makefile
@@ -18,8 +18,8 @@ OBJS = amcmds.o aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
 	event_trigger.o explain.o extension.o foreigncmds.o functioncmds.o \
 	indexcmds.o lockcmds.o matview.o operatorcmds.o opclasscmds.o \
 	policy.o portalcmds.o prepare.o proclang.o \
-	schemacmds.o seclabel.o sequence.o tablecmds.o tablespace.o trigger.o \
-	tsearchcmds.o typecmds.o user.o vacuum.o vacuumlazy.o \
-	variable.o view.o
+	schemacmds.o seclabel.o sequence.o session_variable.o tablecmds.o \
+	tablespace.o trigger.o tsearchcmds.o typecmds.o user.o vacuum.o \
+	vacuumlazy.o variable.o view.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index 03c0433..9e76b2d 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -329,6 +329,7 @@ ExecRenameStmt(RenameStmt *stmt)
 		case OBJECT_MATVIEW:
 		case OBJECT_INDEX:
 		case OBJECT_FOREIGN_TABLE:
+		case OBJECT_VARIABLE:
 			return RenameRelation(stmt);
 
 		case OBJECT_COLUMN:
@@ -454,6 +455,7 @@ ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt,
 		case OBJECT_TABLE:
 		case OBJECT_VIEW:
 		case OBJECT_MATVIEW:
+		case OBJECT_VARIABLE:
 			address = AlterTableNamespace(stmt,
 										  oldSchemaAddr ? &oldNspOid : NULL);
 			break;
diff --git a/src/backend/commands/discard.c b/src/backend/commands/discard.c
index 5b8bd67..143e938 100644
--- a/src/backend/commands/discard.c
+++ b/src/backend/commands/discard.c
@@ -19,6 +19,7 @@
 #include "commands/discard.h"
 #include "commands/prepare.h"
 #include "commands/sequence.h"
+#include "commands/session_variable.h"
 #include "utils/guc.h"
 #include "utils/portal.h"
 
@@ -75,4 +76,5 @@ DiscardAll(bool isTopLevel)
 	ResetPlanCache();
 	ResetTempTableNamespace();
 	ResetSequenceCaches();
+	ResetVariablesCache();
 }
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index e87fce7..0b8ce34 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -122,6 +122,7 @@ static event_trigger_support_data event_trigger_support[] = {
 	{"TYPE", true},
 	{"USER MAPPING", true},
 	{"VIEW", true},
+	{"VARIABLE", true},
 	{NULL, false}
 };
 
@@ -1117,6 +1118,7 @@ EventTriggerSupportsObjectType(ObjectType obtype)
 		case OBJECT_TYPE:
 		case OBJECT_USER_MAPPING:
 		case OBJECT_VIEW:
+		case OBJECT_VARIABLE:
 			return true;
 	}
 	return true;
@@ -1195,6 +1197,7 @@ EventTriggerSupportsGrantObjectType(GrantObjectType objtype)
 		case ACL_OBJECT_LARGEOBJECT:
 		case ACL_OBJECT_NAMESPACE:
 		case ACL_OBJECT_TYPE:
+		case ACL_OBJECT_VARIABLE:
 			return true;
 		default:
 			Assert(false);
@@ -2221,6 +2224,8 @@ stringify_grantobjtype(GrantObjectType objtype)
 			return "TABLESPACE";
 		case ACL_OBJECT_TYPE:
 			return "TYPE";
+		case ACL_OBJECT_VARIABLE:
+			return "VARIABLE";
 		default:
 			elog(ERROR, "unrecognized type %d", objtype);
 			return "???";		/* keep compiler quiet */
@@ -2248,6 +2253,8 @@ stringify_adefprivs_objtype(GrantObjectType objtype)
 		case ACL_OBJECT_TYPE:
 			return "TYPES";
 			break;
+		case ACL_OBJECT_VARIABLE:
+			return "VARIABLES";
 		default:
 			elog(ERROR, "unrecognized type %d", objtype);
 			return "???";		/* keep compiler quiet */
diff --git a/src/backend/commands/session_variable.c b/src/backend/commands/session_variable.c
new file mode 100644
index 0000000..9422b53
--- /dev/null
+++ b/src/backend/commands/session_variable.c
@@ -0,0 +1,499 @@
+/*-------------------------------------------------------------------------
+ *
+ * session_variable.c
+ *	  PostgreSQL session variable support code.
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/commands/session_variable.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "catalog/dependency.h"
+#include "catalog/namespace.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_type.h"
+#include "commands/defrem.h"
+#include "commands/session_variable.h"
+#include "commands/tablecmds.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "nodes/makefuncs.h"
+#include "parser/parse_type.h"
+#include "storage/lmgr.h"
+#include "storage/proc.h"
+#include "storage/smgr.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/datum.h"
+#include "utils/memutils.h"
+#include "utils/lsyscache.h"
+#include "utils/resowner.h"
+#include "utils/syscache.h"
+#include "utils/typcache.h"
+
+typedef struct VarTableData
+{
+	Oid			varid;			/* pg_class OID of this sequence (hash key) */
+	Oid			typid;			/* OID of the data type */
+	int32		typmod;
+	int16		typlen;
+	bool		typbyval;
+	bool		isnull;
+	bool		freeval;
+	Datum		value;
+	TupleDesc	tupdesc;		/* when value is composite, then related tuple desc */
+} VarTableData;
+
+typedef VarTableData *VarTable;
+
+static HTAB *varhashtab = NULL;		/* hash table for session variables */
+
+/*
+ * Create the hash table for storing session variables
+ */
+static void
+create_var_hashtable(void)
+{
+	HASHCTL		ctl;
+
+	memset(&ctl, 0, sizeof(ctl));
+	ctl.keysize = sizeof(Oid);
+	ctl.entrysize = sizeof(VarTableData);
+
+	varhashtab = hash_create("Session variables", 64, &ctl,
+										HASH_ELEM | HASH_BLOBS);
+}
+
+static void
+SetAttribute(VarTable var, const char *attrname, Datum value, bool isNull,
+						Oid	typid, int32 typmod,
+						int16 typlen, bool typbyval, char typalign)
+{
+	AttrNumber	attrno;
+	HeapTuple tuple;
+	HeapTupleHeader		result;
+	MemoryContext	oldcxt;
+	TupleDesc	tupDesc;
+	Datum	   *values;
+	bool	   *nulls;
+	int		natts;
+	int		i;
+
+	/* fast leaving, when there are not any change */
+	if (var->isnull && isNull)
+		return;
+
+	tupDesc = lookup_rowtype_tupdesc(var->typid, var->typmod);
+	natts = tupDesc->natts;
+
+	attrno = InvalidAttrNumber;
+	for (i = 0; i < natts; i++)
+	{
+		if (namestrcmp(&(tupDesc->attrs[i]->attname), attrname) == 0 &&
+			!tupDesc->attrs[i]->attisdropped)
+		{
+			attrno = tupDesc->attrs[i]->attnum;
+			if (typid != tupDesc->attrs[i]->atttypid)
+				ereport(ERROR,
+						(errcode(ERRCODE_DATATYPE_MISMATCH),
+						 errmsg("variable has wrong type"),
+						 errdetail("value has type %s, but query expects %s.",
+							   format_type_be(tupDesc->attrs[i]->atttypid),
+							   format_type_be(typid))));
+			break;
+		}
+	}
+
+	if (attrno == InvalidAttrNumber)
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_COLUMN),
+				 errmsg("attribute \"%s\" of variable \"%s\" does not exist",
+						  attrname, get_rel_name(var->varid))));
+
+	values = (Datum *) palloc0(natts * sizeof(Datum));
+	nulls = (bool *) palloc0(natts * sizeof(bool));
+
+	if (!var->isnull)
+	{
+		HeapTupleHeader rec = DatumGetHeapTupleHeader(var->value);
+		HeapTupleData	tup;
+
+		/* Build a temporary HeapTuple control structure */
+		tup.t_len = HeapTupleHeaderGetDatumLength(rec);
+		ItemPointerSetInvalid(&(tup.t_self));
+		tup.t_tableOid = InvalidOid;
+		tup.t_data = rec;
+
+		heap_deform_tuple(&tup, tupDesc, values, nulls);
+	}
+	else
+	{
+		for(i = 0; i < natts; i++)
+			nulls[i] = true;
+	}
+
+	values[attrno - 1] = value;
+	nulls[attrno - 1] = isNull;
+
+	tuple = heap_form_tuple(tupDesc, values, nulls);
+
+	/* alloc result in persistent memory */
+	oldcxt = MemoryContextSwitchTo(TopMemoryContext);
+	result = (HeapTupleHeader) palloc(tuple->t_len);
+	MemoryContextSwitchTo(oldcxt);
+
+	memcpy(result, tuple->t_data, tuple->t_len);
+
+	heap_freetuple(tuple);
+	pfree(values);
+	pfree(nulls);
+
+	if (var->freeval)
+		pfree(DatumGetPointer(var->value));
+
+	var->value = HeapTupleHeaderGetDatum(result);
+	var->isnull = false;
+	var->freeval = true;
+	var->typid = tupDesc->tdtypeid;
+	var->typmod = tupDesc->tdtypmod;
+
+	ReleaseTupleDesc(tupDesc);
+
+	return;
+}
+
+static void
+SetValue(VarTable var, Datum value, bool isNull,
+						Oid	typid, int32 typmod,
+						int16 typlen, bool typbyval, char typalign)
+{
+	if (var->freeval)
+	{
+		pfree(DatumGetPointer(var->value));
+		var->freeval = false;
+	}
+
+	if (!isNull)
+	{
+		MemoryContext oldcxt;
+
+		var->isnull = false;
+		var->typid = typid;
+		var->typmod = typmod;
+		var->typbyval = typbyval;
+		var->typlen = typlen;
+
+		oldcxt = MemoryContextSwitchTo(TopMemoryContext);
+
+		var->value = datumCopy(value, typbyval, typlen);
+		if (var->value != value)
+			var->freeval = true;
+
+		MemoryContextSwitchTo(oldcxt);
+	}
+	else
+	{
+		var->value = (Datum) 0;
+		var->isnull = true;
+	}
+}
+
+/*
+ * Access functions to session variables.
+ */
+void
+SetSessionVariable(Oid varid, char *attrname, Datum value, bool isNull,
+						Oid	typid, int32 typmod,
+						int16 typlen, bool typbyval, char typalign)
+{
+	VarTable	var;
+	bool		found;
+
+	if (varhashtab == NULL)
+	{
+		/* don't init hashtable for NULL values */
+		if (isNull)
+			return;
+
+		create_var_hashtable();
+	}
+
+	var = (VarTable) hash_search(varhashtab, &varid, HASH_ENTER, &found);
+	if (!found)
+	{
+		var->value = (Datum) 0;
+		var->isnull = true;
+		var->freeval = false;
+	}
+
+	if (attrname != NULL)
+		SetAttribute(var, attrname, value, isNull,
+						  typid, typmod, typlen, typbyval, typalign);
+	else
+		SetValue(var, value, isNull, typid, typmod,
+										 typlen, typbyval, typalign);
+										 
+}
+
+Datum
+GetSessionVariable(Oid varid, char *attrname, bool *isNull,
+						Oid	typid, int32 typmod,
+						int16 typlen, bool typbyval, char typalign)
+{
+	VarTable		var;
+	Datum		result = (Datum) 0;
+	Oid			result_typid;
+	bool	found;
+
+	if (varhashtab == NULL)
+		create_var_hashtable();
+
+	var = (VarTable) hash_search(varhashtab, &varid, HASH_FIND, &found);
+	if (found)
+	{
+		if (!var->isnull)
+		{
+			if (attrname != NULL)
+			{
+				/* This code is based on GetAttributeBy... function */
+				TupleDesc	tupDesc;
+				HeapTupleHeader	tuple;
+				AttrNumber	attrno;
+				HeapTupleData tmptup;
+				int		i;
+
+				tuple = DatumGetHeapTupleHeader(var->value);
+				tupDesc = lookup_rowtype_tupdesc(var->typid, var->typmod);
+
+				attrno = InvalidAttrNumber;
+				for (i = 0; i < tupDesc->natts; i++)
+				{
+					if (namestrcmp(&(tupDesc->attrs[i]->attname), attrname) == 0 &&
+							!tupDesc->attrs[i]->attisdropped)
+					{
+						attrno = tupDesc->attrs[i]->attnum;
+						result_typid = tupDesc->attrs[i]->atttypid;
+						break;
+					}
+				}
+
+				if (attrno == InvalidAttrNumber)
+					ereport(ERROR,
+							(errcode(ERRCODE_UNDEFINED_COLUMN),
+							 errmsg("attribute \"%s\" of variable \"%s\" does not exist",
+									  attrname, get_rel_name(varid))));
+
+				/*
+				 * heap_getattr needs a HeapTuple not a bare HeapTupleHeader.  We set all
+				 * the fields in the struct just in case user tries to inspect system
+				 * columns.
+				 */
+				tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
+				ItemPointerSetInvalid(&(tmptup.t_self));
+				tmptup.t_tableOid = InvalidOid;
+				tmptup.t_data = tuple;
+
+				result = heap_getattr(&tmptup,
+									  attrno,
+									  tupDesc,
+									  isNull);
+
+				ReleaseTupleDesc(tupDesc);
+			}
+			else
+			{
+				result = datumCopy(var->value, var->typbyval, var->typlen);
+				result_typid = var->typid;
+			}
+
+			*isNull = false;
+		}
+		else
+			*isNull = true;
+	}
+	else
+		*isNull = true;
+
+	/* recheck result */
+	if (!*isNull && result_typid != typid)
+		ereport(ERROR,
+				(errcode(ERRCODE_DATATYPE_MISMATCH),
+				 errmsg("variable has wrong type"),
+				 errdetail("value has type %s, but query expects %s.",
+						   format_type_be(result_typid),
+						   format_type_be(typid))));
+
+	return result;
+}
+
+/*
+ * DefineSessionVariable
+ *				Creates a new variable related relation
+ */
+ObjectAddress
+DefineSessionVariable(ParseState *pstate, CreateVarStmt *var)
+{
+	CreateStmt *stmt = makeNode(CreateStmt);
+	Oid			varoid;
+	ObjectAddress address;
+	Type		tup;
+	int32		typmod;
+	Form_pg_type typeStruct;
+	bool			is_composite;
+	Oid				typrelid = InvalidOid;
+
+	/*
+	 * If if_not_exists was given and a relation with the same name already
+	 * exists, bail out. (Note: we needn't check this when not if_not_exists,
+	 * because DefineRelation will complain anyway.)
+	 */
+	if (var->if_not_exists)
+	{
+		RangeVarGetAndCheckCreationNamespace(var->variable, NoLock, &varoid);
+		if (OidIsValid(varoid))
+		{
+			ereport(NOTICE,
+					(errcode(ERRCODE_DUPLICATE_TABLE),
+					 errmsg("relation \"%s\" already exists, skipping",
+							var->variable->relname)));
+			return InvalidObjectAddress;
+		}
+	}
+
+	tup = LookupTypeName(pstate, var->typeName, &typmod, false);
+	if (tup == NULL)
+			ereport(ERROR,
+					(errcode(ERRCODE_UNDEFINED_OBJECT),
+					 errmsg("type \"%s\" does not exist",
+							TypeNameToString(var->typeName)),
+					 parser_errposition(pstate, var->typeName->location)));
+
+	typeStruct = (Form_pg_type) GETSTRUCT(tup);
+	//typoid = HeapTupleGetOid(tup);
+
+	switch (typeStruct->typtype)
+	{
+		case TYPTYPE_BASE:
+		case TYPTYPE_DOMAIN:
+		case TYPTYPE_ENUM:
+		case TYPTYPE_RANGE:
+			is_composite = false;
+			break;
+		case TYPTYPE_COMPOSITE:
+			Assert(OidIsValid(typeStruct->typrelid));
+			typrelid = typeStruct->typrelid;
+			is_composite = true;
+			break;
+		case TYPTYPE_PSEUDO:
+			elog(ERROR, "variable cannot cannot be pseudotype");
+		default:
+			elog(ERROR, "unrecognized typtype: %d",
+				  (int) typeStruct->typtype);
+	}
+
+	ReleaseSysCache(tup);
+
+	stmt->tableElts = NIL;
+
+	/*
+	 * When variable type is not composite, then the related relation will have
+	 * one column named "value", else the created relation is based on unpacked
+	 * composite type.
+	 */
+	if (!is_composite)
+	{
+		ColumnDef *coldef = makeNode(ColumnDef);
+
+		coldef->inhcount = 0;
+		coldef->is_local = true;
+		coldef->is_not_null = false;
+		coldef->is_from_type = false;
+		coldef->storage = 0;
+		coldef->raw_default = NULL;
+		coldef->cooked_default = NULL;
+		coldef->collClause = NULL;
+		coldef->collOid = InvalidOid;
+		coldef->constraints = NIL;
+		coldef->location = -1;
+
+		coldef->colname = "value";
+		coldef->typeName = var->typeName;
+
+		stmt->tableElts = lappend(stmt->tableElts, coldef);
+	}
+	else
+	{
+		TupleDesc	tupdesc;
+		Relation	rel;
+		int			i;
+
+		rel = relation_open(typrelid, AccessShareLock);
+		tupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
+
+		for (i = 0; i < tupdesc->natts; i++)
+		{
+			Form_pg_attribute attrStruct;
+
+			attrStruct = tupdesc->attrs[i];
+
+			if (!attrStruct->attisdropped)
+			{
+				ColumnDef *coldef = makeNode(ColumnDef);
+
+				coldef->inhcount = 0;
+				coldef->is_local = true;
+				coldef->is_not_null = false;
+				coldef->is_from_type = false;
+				coldef->storage = 0;
+				coldef->raw_default = NULL;
+				coldef->cooked_default = NULL;
+				coldef->collClause = NULL;
+				coldef->collOid = InvalidOid;
+				coldef->constraints = NIL;
+				coldef->location = -1;
+
+				coldef->colname = pstrdup(NameStr(attrStruct->attname));
+				coldef->typeName = makeTypeNameFromOid(attrStruct->atttypid,
+													   attrStruct->atttypmod);
+
+				stmt->tableElts = lappend(stmt->tableElts, coldef);
+			}
+		}
+
+		FreeTupleDesc(tupdesc);
+		relation_close(rel, AccessShareLock);
+
+		stmt->ofTypename = var->typeName;
+	}
+
+	stmt->relation = var->variable;
+	stmt->inhRelations = NIL;
+	stmt->constraints = NIL;
+	stmt->options = NIL;
+	stmt->oncommit = ONCOMMIT_NOOP;
+	stmt->tablespacename = NULL;
+	stmt->if_not_exists = var->if_not_exists;
+
+	address = DefineRelation(stmt, RELKIND_VARIABLE, InvalidOid, NULL, NULL);
+	varoid = address.objectId;
+	Assert(varoid != InvalidOid);
+
+	return address;
+}
+
+void
+ResetVariablesCache(void)
+{
+	if (varhashtab)
+	{
+		hash_destroy(varhashtab);
+		varhashtab = NULL;
+	}
+}
\ No newline at end of file
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 7a574dc..feccf7c 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -263,6 +263,12 @@ static const struct dropmsgstrings dropmsgstringarray[] = {
 		gettext_noop("table \"%s\" does not exist, skipping"),
 		gettext_noop("\"%s\" is not a table"),
 	gettext_noop("Use DROP TABLE to remove a table.")},
+	{RELKIND_VARIABLE,
+		ERRCODE_UNDEFINED_TABLE,
+		gettext_noop("variable \"%s\" does not exist"),
+		gettext_noop("variable \"%s\" does not exist, skipping"),
+		gettext_noop("\"%s\" is not a variable"),
+	gettext_noop("Use DROP VARIABLE to remove a variable.")},
 	{'\0', 0, NULL, NULL, NULL, NULL}
 };
 
@@ -1010,6 +1016,10 @@ RemoveRelations(DropStmt *drop)
 			relkind = RELKIND_FOREIGN_TABLE;
 			break;
 
+		case OBJECT_VARIABLE:
+			relkind = RELKIND_VARIABLE;
+			break;
+
 		default:
 			elog(ERROR, "unrecognized drop object type: %d",
 				 (int) drop->removeType);
@@ -12702,6 +12712,11 @@ RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
 				 errmsg("\"%s\" is not an index", rv->relname)));
 
+	if (reltype == OBJECT_VARIABLE && relkind != RELKIND_VARIABLE)
+		ereport(ERROR,
+				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+				 errmsg("\"%s\" is not a variable", rv->relname)));
+
 	/*
 	 * Don't allow ALTER TABLE on composite types. We want people to use ALTER
 	 * TYPE for that.
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index d43a204..182bb12 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1036,6 +1036,11 @@ CheckValidResultRel(Relation resultRel, CmdType operation)
 					 errmsg("cannot change TOAST relation \"%s\"",
 							RelationGetRelationName(resultRel))));
 			break;
+		case RELKIND_VARIABLE:
+			ereport(ERROR,
+					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+					 errmsg("cannot change session variable \"%s\"",
+							RelationGetRelationName(resultRel))));
 		case RELKIND_VIEW:
 
 			/*
@@ -1165,6 +1170,13 @@ CheckValidRowMarkRel(Relation rel, RowMarkType markType)
 					 errmsg("cannot lock rows in sequence \"%s\"",
 							RelationGetRelationName(rel))));
 			break;
+		case RELKIND_VARIABLE:
+			/* Must disallow this because we cannot locks session variables */
+			ereport(ERROR,
+					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+					 errmsg("cannot lock rows in session variable \"%s\"",
+							RelationGetRelationName(rel))));
+			break;
 		case RELKIND_TOASTVALUE:
 			/* We could allow this, but there seems no good reason to */
 			ereport(ERROR,
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 743e7d6..a18620e 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -41,6 +41,7 @@
 #include "access/tupconvert.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_type.h"
+#include "commands/session_variable.h"
 #include "executor/execdebug.h"
 #include "executor/nodeSubplan.h"
 #include "funcapi.h"
@@ -189,7 +190,9 @@ static Datum ExecEvalCurrentOfExpr(ExprState *exprstate, ExprContext *econtext,
 static Datum ExecEvalGroupingFuncExpr(GroupingFuncExprState *gstate,
 						 ExprContext *econtext,
 						 bool *isNull, ExprDoneCond *isDone);
-
+static Datum ExecEvalUseVarExpr(UseVarExprState *usevarstate,
+						 ExprContext *econtext,
+						 bool *isNull, ExprDoneCond *isDone);
 
 /* ----------------------------------------------------------------
  *		ExecEvalExpr routines
@@ -3994,6 +3997,44 @@ ExecEvalNullTest(NullTestState *nstate,
 }
 
 /* ----------------------------------------------------------------
+ *		ExecEvalUseVarExpr
+ *
+ *		Evaluate a access to session variable node.
+ * ----------------------------------------------------------------
+ */
+static Datum
+ExecEvalUseVarExpr(UseVarExprState *usevarstate,
+						 ExprContext *econtext,
+						 bool *isNull, ExprDoneCond *isDone)
+{
+	Datum value = (Datum) 0;
+
+	if (isDone)
+		*isDone = ExprSingleResult;
+
+	if (usevarstate->expr != NULL)
+	{
+		/* setvar, setattr */
+		value = ExecEvalExpr(usevarstate->expr, econtext, isNull, NULL);
+
+		SetSessionVariable(usevarstate->varid, usevarstate->attrname,
+							  value, *isNull,
+									usevarstate->typid, usevarstate->typmod,
+									usevarstate->typlen, usevarstate->typbyval, usevarstate->typalign);
+	}
+	else
+	{
+		/* getvar, getattr */
+		value = GetSessionVariable(usevarstate->varid, usevarstate->attrname,
+							  isNull,
+									usevarstate->typid, usevarstate->typmod,
+									usevarstate->typlen, usevarstate->typbyval, usevarstate->typalign);
+	}
+
+	return value;
+}
+
+/* ----------------------------------------------------------------
  *		ExecEvalBooleanTest
  *
  *		Evaluate a BooleanTest node.
@@ -5197,6 +5238,34 @@ ExecInitExpr(Expr *node, PlanState *parent)
 				state = (ExprState *) xstate;
 			}
 			break;
+		case T_UseVarExpr:
+			{
+				AclResult	aclresult;
+				UseVarExpr *uvexpr = (UseVarExpr *) node;
+				UseVarExprState *uvstate = makeNode(UseVarExprState);
+
+				/* Recheck permissions */
+				aclresult = pg_class_aclcheck(uvexpr->varid, GetUserId(),
+						uvexpr->expr != NULL ? ACL_UPDATE : ACL_SELECT);
+				if (aclresult != ACLCHECK_OK)
+					aclcheck_error(aclresult, ACL_KIND_VARIABLE,
+													get_rel_name(uvexpr->varid));
+
+				uvstate->varid = uvexpr->varid;
+				uvstate->attrname = uvexpr->attrname;
+
+				/* get info about expected type */
+				uvstate->typid = uvexpr->typid;
+				uvstate->typmod = uvexpr->typmod;
+				get_typlenbyvalalign(uvexpr->typid,
+										&uvstate->typlen, &uvstate->typbyval, &uvstate->typalign);
+
+				uvstate->expr = ExecInitExpr((Expr *) uvexpr->expr, parent);
+				uvstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalUseVarExpr;
+
+				state = (ExprState *) uvstate;
+			}
+			break;
 		case T_NullTest:
 			{
 				NullTest   *ntest = (NullTest *) node;
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index a3bcb10..020e942 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -802,6 +802,15 @@ ExecOpenScanRelation(EState *estate, Index scanrelid, int eflags)
 	rel = heap_open(reloid, lockmode);
 
 	/*
+	 * In this moment, the session variables relation is not scannable
+	 */
+	if (rel->rd_rel->relkind == RELKIND_VARIABLE)
+		ereport(ERROR,
+				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+				 errmsg("cannot read session variable \"%s\" as relation",
+					   RelationGetRelationName(rel))));
+
+	/*
 	 * Complain if we're attempting a scan of an unscannable relation, except
 	 * when the query won't actually be run.  This is a slightly klugy place
 	 * to do this, perhaps, but there is no better place.
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index d973225..b03b6fc 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -1987,6 +1987,25 @@ _copyOnConflictExpr(const OnConflictExpr *from)
 	return newnode;
 }
 
+/*
+ * _SetVarExpr
+ */
+static UseVarExpr *
+_copyUseVarExpr(const UseVarExpr *from)
+{
+	UseVarExpr *newnode = makeNode(UseVarExpr);
+
+	COPY_SCALAR_FIELD(varid);
+	COPY_STRING_FIELD(attrname);
+	COPY_NODE_FIELD(expr);
+	COPY_SCALAR_FIELD(typid);
+	COPY_SCALAR_FIELD(typmod);
+	COPY_SCALAR_FIELD(collation);
+	COPY_LOCATION_FIELD(location);
+
+	return newnode;
+}
+
 /* ****************************************************************
  *						relation.h copy functions
  *
@@ -2455,6 +2474,19 @@ _copyA_ArrayExpr(const A_ArrayExpr *from)
 	return newnode;
 }
 
+static UseVar *
+_copyUseVar(const UseVar *from)
+{
+	UseVar *newnode = makeNode(UseVar);
+
+	COPY_STRING_FIELD(name);
+	COPY_STRING_FIELD(attrname);
+	COPY_NODE_FIELD(expr);
+	COPY_LOCATION_FIELD(location);
+
+	return newnode;
+}
+
 static ResTarget *
 _copyResTarget(const ResTarget *from)
 {
@@ -3630,6 +3662,18 @@ _copyAlterSeqStmt(const AlterSeqStmt *from)
 	return newnode;
 }
 
+static CreateVarStmt *
+_copyCreateVarStmt(const CreateVarStmt *from)
+{
+	CreateVarStmt *newnode = makeNode(CreateVarStmt);
+
+	COPY_NODE_FIELD(variable);
+	COPY_NODE_FIELD(typeName);
+	COPY_SCALAR_FIELD(if_not_exists);
+
+	return newnode;
+}
+
 static VariableSetStmt *
 _copyVariableSetStmt(const VariableSetStmt *from)
 {
@@ -4666,6 +4710,9 @@ copyObject(const void *from)
 		case T_OnConflictExpr:
 			retval = _copyOnConflictExpr(from);
 			break;
+		case T_UseVarExpr:
+			retval = _copyUseVarExpr(from);
+			break;
 
 			/*
 			 * RELATION NODES
@@ -4912,6 +4959,9 @@ copyObject(const void *from)
 		case T_AlterSeqStmt:
 			retval = _copyAlterSeqStmt(from);
 			break;
+		case T_CreateVarStmt:
+			retval = _copyCreateVarStmt(from);
+			break;
 		case T_VariableSetStmt:
 			retval = _copyVariableSetStmt(from);
 			break;
@@ -5074,6 +5124,9 @@ copyObject(const void *from)
 		case T_A_ArrayExpr:
 			retval = _copyA_ArrayExpr(from);
 			break;
+		case T_UseVar:
+			retval = _copyUseVar(from);
+			break;
 		case T_ResTarget:
 			retval = _copyResTarget(from);
 			break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index edc1797..af275bc 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -783,6 +783,20 @@ _equalOnConflictExpr(const OnConflictExpr *a, const OnConflictExpr *b)
 	return true;
 }
 
+static bool
+_equalUseVarExpr(const UseVarExpr *a, const UseVarExpr *b)
+{
+	COMPARE_SCALAR_FIELD(varid);
+	COMPARE_STRING_FIELD(attrname);
+	COMPARE_NODE_FIELD(expr);
+	COMPARE_SCALAR_FIELD(typid);
+	COMPARE_SCALAR_FIELD(typmod);
+	COMPARE_SCALAR_FIELD(collation);
+	COMPARE_LOCATION_FIELD(location);
+
+	return true;
+}
+
 /*
  * Stuff from relation.h
  */
@@ -1673,6 +1687,17 @@ _equalAlterSeqStmt(const AlterSeqStmt *a, const AlterSeqStmt *b)
 }
 
 static bool
+_equalCreateVarStmt(const CreateVarStmt *a, const CreateVarStmt *b)
+{
+	COMPARE_NODE_FIELD(variable);
+	COMPARE_NODE_FIELD(typeName);
+	COMPARE_SCALAR_FIELD(if_not_exists);
+
+	return true;
+}
+
+
+static bool
 _equalVariableSetStmt(const VariableSetStmt *a, const VariableSetStmt *b)
 {
 	COMPARE_SCALAR_FIELD(kind);
@@ -2239,6 +2264,17 @@ _equalA_ArrayExpr(const A_ArrayExpr *a, const A_ArrayExpr *b)
 }
 
 static bool
+_equalUseVar(const UseVar *a, const UseVar *b)
+{
+	COMPARE_STRING_FIELD(name);
+	COMPARE_STRING_FIELD(attrname);
+	COMPARE_NODE_FIELD(expr);
+	COMPARE_LOCATION_FIELD(location);
+
+	return true;
+}
+
+static bool
 _equalResTarget(const ResTarget *a, const ResTarget *b)
 {
 	COMPARE_STRING_FIELD(name);
@@ -2962,6 +2998,9 @@ equal(const void *a, const void *b)
 		case T_OnConflictExpr:
 			retval = _equalOnConflictExpr(a, b);
 			break;
+		case T_UseVarExpr:
+			retval = _equalUseVarExpr(a, b);
+			break;
 		case T_JoinExpr:
 			retval = _equalJoinExpr(a, b);
 			break;
@@ -3198,6 +3237,9 @@ equal(const void *a, const void *b)
 		case T_AlterSeqStmt:
 			retval = _equalAlterSeqStmt(a, b);
 			break;
+		case T_CreateVarStmt:
+			retval = _equalCreateVarStmt(a, b);
+			break;
 		case T_VariableSetStmt:
 			retval = _equalVariableSetStmt(a, b);
 			break;
@@ -3360,6 +3402,9 @@ equal(const void *a, const void *b)
 		case T_A_ArrayExpr:
 			retval = _equalA_ArrayExpr(a, b);
 			break;
+		case T_UseVar:
+			retval = _equalUseVar(a, b);
+			break;
 		case T_ResTarget:
 			retval = _equalResTarget(a, b);
 			break;
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index 973fb15..69a111d 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -257,6 +257,9 @@ exprType(const Node *expr)
 		case T_PlaceHolderVar:
 			type = exprType((Node *) ((const PlaceHolderVar *) expr)->phexpr);
 			break;
+		case T_UseVarExpr:
+			type = ((const UseVarExpr *) expr)->typid;
+			break;
 		default:
 			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
 			type = InvalidOid;	/* keep compiler quiet */
@@ -492,6 +495,8 @@ exprTypmod(const Node *expr)
 			return ((const SetToDefault *) expr)->typeMod;
 		case T_PlaceHolderVar:
 			return exprTypmod((Node *) ((const PlaceHolderVar *) expr)->phexpr);
+		case T_UseVarExpr:
+			return ((const UseVarExpr *) expr)->typmod;
 		default:
 			break;
 	}
@@ -727,6 +732,8 @@ expression_returns_set_walker(Node *node, void *context)
 		return false;
 	if (IsA(node, XmlExpr))
 		return false;
+	if (IsA(node, UseVarExpr))
+		return false;
 
 	return expression_tree_walker(node, expression_returns_set_walker,
 								  context);
@@ -929,6 +936,9 @@ exprCollation(const Node *expr)
 		case T_PlaceHolderVar:
 			coll = exprCollation((Node *) ((const PlaceHolderVar *) expr)->phexpr);
 			break;
+		case T_UseVarExpr:
+			coll = ((const UseVarExpr *) expr)->collation;
+			break;
 		default:
 			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
 			coll = InvalidOid;	/* keep compiler quiet */
@@ -1127,6 +1137,9 @@ exprSetCollation(Node *expr, Oid collation)
 		case T_CurrentOfExpr:
 			Assert(!OidIsValid(collation));		/* result is always boolean */
 			break;
+		case T_UseVarExpr:
+			((UseVarExpr *) expr)->collation = collation;
+			break;
 		default:
 			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
 			break;
@@ -1557,6 +1570,8 @@ exprLocation(const Node *expr)
 			break;
 		case T_PartitionRangeDatum:
 			loc = ((const PartitionRangeDatum *) expr)->location;
+		case T_UseVarExpr:
+			loc = ((const UseVarExpr *) expr)->location;
 			break;
 		default:
 			/* for any other node type it's just unknown... */
@@ -2217,6 +2232,8 @@ expression_tree_walker(Node *node,
 					return true;
 			}
 			break;
+		case T_UseVarExpr:
+			return walker(((UseVarExpr *) node)->expr, context);
 		default:
 			elog(ERROR, "unrecognized node type: %d",
 				 (int) nodeTag(node));
@@ -3013,6 +3030,16 @@ expression_tree_mutator(Node *node,
 				return (Node *) newnode;
 			}
 			break;
+		case T_UseVarExpr:
+			{
+				UseVarExpr *varexpr = (UseVarExpr *) node;
+				UseVarExpr *newnode;
+
+				FLATCOPY(newnode, varexpr, UseVarExpr);
+				MUTATE(newnode->expr, varexpr->expr, Node *);
+				return (Node *) newnode;
+			}
+			break;
 		default:
 			elog(ERROR, "unrecognized node type: %d",
 				 (int) nodeTag(node));
@@ -3480,6 +3507,8 @@ raw_expression_tree_walker(Node *node,
 			break;
 		case T_A_ArrayExpr:
 			return walker(((A_ArrayExpr *) node)->elements, context);
+		case T_UseVarExpr:
+			return walker(((UseVarExpr *) node)->expr, context);
 		case T_ResTarget:
 			{
 				ResTarget  *rt = (ResTarget *) node;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 7258c03..58fa46d 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -1574,6 +1574,20 @@ _outOnConflictExpr(StringInfo str, const OnConflictExpr *node)
 	WRITE_NODE_FIELD(exclRelTlist);
 }
 
+static void
+_outUseVarExpr(StringInfo str, const UseVarExpr *node)
+{
+	WRITE_NODE_TYPE("USEVAREXPR");
+
+	WRITE_OID_FIELD(varid);
+	WRITE_STRING_FIELD(attrname);
+	WRITE_NODE_FIELD(expr);
+	WRITE_OID_FIELD(typid);
+	WRITE_INT_FIELD(typmod);
+	WRITE_OID_FIELD(collation);
+	WRITE_LOCATION_FIELD(location);
+}
+
 /*****************************************************************************
  *
  *	Stuff from relation.h.
@@ -3079,6 +3093,17 @@ _outA_ArrayExpr(StringInfo str, const A_ArrayExpr *node)
 }
 
 static void
+_outUseVar(StringInfo str, const UseVar *node)
+{
+	WRITE_NODE_TYPE("UseVAR");
+
+	WRITE_STRING_FIELD(name);
+	WRITE_STRING_FIELD(attrname);
+	WRITE_NODE_FIELD(expr);
+	WRITE_LOCATION_FIELD(location);
+}
+
+static void
 _outResTarget(StringInfo str, const ResTarget *node)
 {
 	WRITE_NODE_TYPE("RESTARGET");
@@ -3620,6 +3645,9 @@ outNode(StringInfo str, const void *obj)
 			case T_OnConflictExpr:
 				_outOnConflictExpr(str, obj);
 				break;
+			case T_UseVarExpr:
+				_outUseVarExpr(str, obj);
+				break;
 			case T_Path:
 				_outPath(str, obj);
 				break;
@@ -3865,6 +3893,9 @@ outNode(StringInfo str, const void *obj)
 			case T_A_ArrayExpr:
 				_outA_ArrayExpr(str, obj);
 				break;
+			case T_UseVar:
+				_outUseVar(str, obj);
+				break;
 			case T_ResTarget:
 				_outResTarget(str, obj);
 				break;
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index d608530..909436b 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -1277,6 +1277,25 @@ _readOnConflictExpr(void)
 }
 
 /*
+ * _readUseVarExpr
+ */
+static UseVarExpr *
+_readUseVarExpr(void)
+{
+	READ_LOCALS(UseVarExpr);
+
+	READ_OID_FIELD(varid);
+	READ_STRING_FIELD(attrname);
+	READ_NODE_FIELD(expr);
+	READ_OID_FIELD(typid);
+	READ_INT_FIELD(typmod);
+	READ_OID_FIELD(collation);
+	READ_LOCATION_FIELD(location);
+
+	READ_DONE();
+}
+
+/*
  *	Stuff from parsenodes.h.
  */
 
@@ -2427,6 +2446,8 @@ parseNodeString(void)
 		return_value = _readFromExpr();
 	else if (MATCH("ONCONFLICTEXPR", 14))
 		return_value = _readOnConflictExpr();
+	else if (MATCH("USEVAREXPR", 10))
+		return_value = _readUseVarExpr();
 	else if (MATCH("RTE", 3))
 		return_value = _readRangeTblEntry();
 	else if (MATCH("RANGETBLFUNCTION", 16))
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 9af29dd..b723106 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -861,6 +861,8 @@ expression_returns_set_rows_walker(Node *node, double *count)
 		return false;
 	if (IsA(node, XmlExpr))
 		return false;
+	if (IsA(node, UseVarExpr))
+		return false;
 
 	return expression_tree_walker(node, expression_returns_set_rows_walker,
 								  (void *) count);
@@ -1405,6 +1407,8 @@ contain_nonstrict_functions_walker(Node *node, void *context)
 		return true;
 	if (IsA(node, BooleanTest))
 		return true;
+	if (IsA(node, UseVarExpr))
+		return true;
 
 	/*
 	 * Check other function-containing nodes; but ArrayCoerceExpr is strict at
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 2ed7b52..58cd915 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -248,7 +248,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 		ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt
 		CreateDomainStmt CreateExtensionStmt CreateGroupStmt CreateOpClassStmt
 		CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt
-		CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt
+		CreateSchemaStmt CreateSeqStmt CreateVarStmt CreateStmt CreateTableSpaceStmt
 		CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt
 		CreateAssertStmt CreateTransformStmt CreateTrigStmt CreateEventTrigStmt
 		CreateUserStmt CreateUserMappingStmt CreateRoleStmt CreatePolicyStmt
@@ -610,7 +610,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 	FALSE_P FAMILY FETCH FILTER FIRST_P FLOAT_P FOLLOWING FOR
 	FORCE FOREIGN FORWARD FREEZE FROM FULL FUNCTION FUNCTIONS
 
-	GLOBAL GRANT GRANTED GREATEST GROUP_P GROUPING
+	GETATTR GETVAR GLOBAL GRANT GRANTED GREATEST GROUP_P GROUPING
 
 	HANDLER HAVING HEADER_P HOLD HOUR_P
 
@@ -648,9 +648,9 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 	ROW ROWS RULE
 
 	SAVEPOINT SCHEMA SCROLL SEARCH SECOND_P SECURITY SELECT SEQUENCE SEQUENCES
-	SERIALIZABLE SERVER SESSION SESSION_USER SET SETS SETOF SHARE SHOW
-	SIMILAR SIMPLE SKIP SMALLINT SNAPSHOT SOME SQL_P STABLE STANDALONE_P START
-	STATEMENT STATISTICS STDIN STDOUT STORAGE STRICT_P STRIP_P SUBSTRING
+	SERIALIZABLE SERVER SESSION SESSION_USER SET SETATTR SETS SETOF SETVAR SHARE
+	SHOW SIMILAR SIMPLE SKIP SMALLINT SNAPSHOT SOME SQL_P STABLE STANDALONE_P
+	START STATEMENT STATISTICS STDIN STDOUT STORAGE STRICT_P STRIP_P SUBSTRING
 	SYMMETRIC SYSID SYSTEM_P
 
 	TABLE TABLES TABLESAMPLE TABLESPACE TEMP TEMPLATE TEMPORARY TEXT_P THEN
@@ -660,8 +660,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 	UNBOUNDED 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 VARIADIC
+	VARYING VERBOSE VERSION_P VIEW VIEWS VOLATILE
 
 	WHEN WHERE WHITESPACE_P WINDOW WITH WITHIN WITHOUT WORK WRAPPER WRITE
 
@@ -833,6 +833,7 @@ stmt :
 			| CreatePLangStmt
 			| CreateSchemaStmt
 			| CreateSeqStmt
+			| CreateVarStmt
 			| CreateStmt
 			| CreateTableSpaceStmt
 			| CreateTransformStmt
@@ -1343,6 +1344,7 @@ schema_stmt:
 			CreateStmt
 			| IndexStmt
 			| CreateSeqStmt
+			| CreateVarStmt
 			| CreateTrigStmt
 			| GrantStmt
 			| ViewStmt
@@ -3977,6 +3979,32 @@ NumericOnly_list:	NumericOnly						{ $$ = list_make1($1); }
 
 /*****************************************************************************
  *
+ *		QUERY :
+ *				CREATE VARIABLE varname
+ *
+ *****************************************************************************/
+
+CreateVarStmt:
+			CREATE VARIABLE qualified_name opt_as Typename
+				{
+					CreateVarStmt *n = makeNode(CreateVarStmt);
+					n->variable = $3;
+					n->typeName = $5;
+					n->if_not_exists = false;
+					$$ = (Node *)n;
+				}
+			| CREATE VARIABLE IF_P NOT EXISTS qualified_name opt_as Typename
+				{
+					CreateVarStmt *n = makeNode(CreateVarStmt);
+					n->variable = $6;
+					n->typeName = $8;
+					n->if_not_exists = true;
+					$$ = (Node *)n;
+				}
+		;
+
+/*****************************************************************************
+ *
  *		QUERIES :
  *				CREATE [OR REPLACE] [TRUSTED] [PROCEDURAL] LANGUAGE ...
  *				DROP [PROCEDURAL] LANGUAGE ...
@@ -6050,6 +6078,7 @@ drop_type:	TABLE									{ $$ = OBJECT_TABLE; }
 			| TEXT_P SEARCH DICTIONARY				{ $$ = OBJECT_TSDICTIONARY; }
 			| TEXT_P SEARCH TEMPLATE				{ $$ = OBJECT_TSTEMPLATE; }
 			| TEXT_P SEARCH CONFIGURATION			{ $$ = OBJECT_TSCONFIGURATION; }
+			| VARIABLE								{ $$ = OBJECT_VARIABLE; }
 		;
 
 any_name_list:
@@ -6318,6 +6347,7 @@ comment_type:
 			| TEXT_P SEARCH DICTIONARY			{ $$ = OBJECT_TSDICTIONARY; }
 			| TEXT_P SEARCH PARSER				{ $$ = OBJECT_TSPARSER; }
 			| TEXT_P SEARCH TEMPLATE			{ $$ = OBJECT_TSTEMPLATE; }
+			| VARIABLE							{ $$ = OBJECT_VARIABLE; }
 		;
 
 comment_text:
@@ -6431,6 +6461,7 @@ security_label_type:
 			| TABLESPACE						{ $$ = OBJECT_TABLESPACE; }
 			| VIEW								{ $$ = OBJECT_VIEW; }
 			| MATERIALIZED VIEW					{ $$ = OBJECT_MATVIEW; }
+			| VARIABLE							{ $$ = OBJECT_VARIABLE; }
 		;
 
 security_label:	Sconst				{ $$ = $1; }
@@ -6848,6 +6879,14 @@ privilege_target:
 					n->objs = $5;
 					$$ = n;
 				}
+			| VARIABLE qualified_name_list
+				{
+					PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
+					n->targtype = ACL_TARGET_OBJECT;
+					n->objtype = ACL_OBJECT_VARIABLE;
+					n->objs = $2;
+					$$ = n;
+				}
 		;
 
 
@@ -12779,6 +12818,27 @@ func_expr_common_subexpr:
 				{
 					$$ = (Node *) makeFuncCall(SystemFuncName("date_part"), $3, @1);
 				}
+			| GETVAR '(' Sconst ')'
+				{
+					/*
+					 * The name of session variable must be known in transformation
+					 * time - it is same situation like any other database object.
+					 * The qualified_name is not used to be consistent with any other
+					 * function like syntax.
+					 */
+					UseVar *g = makeNode(UseVar);
+					g->name = $3;
+					g->location = @1;
+					$$ = (Node *) g;
+				}
+			| GETATTR '(' Sconst ',' Sconst ')'
+				{
+					UseVar *g = makeNode(UseVar);
+					g->name = $3;
+					g->attrname = $5;
+					g->location = @1;
+					$$ = (Node *) g;
+				}
 			| OVERLAY '(' overlay_list ')'
 				{
 					/* overlay(A PLACING B FROM C FOR D) is converted to
@@ -12793,6 +12853,23 @@ func_expr_common_subexpr:
 					/* position(A in B) is converted to position(B, A) */
 					$$ = (Node *) makeFuncCall(SystemFuncName("position"), $3, @1);
 				}
+			| SETATTR '(' Sconst ',' Sconst ',' a_expr ')'
+				{
+					UseVar *s = makeNode(UseVar);
+					s->name = $3;
+					s->attrname = $5;
+					s->expr = $7;
+					s->location = @1;
+					$$ = (Node *) s;
+				}
+			| SETVAR '(' Sconst ',' a_expr ')'
+				{
+					UseVar *s = makeNode(UseVar);
+					s->name = $3;
+					s->expr = $5;
+					s->location = @1;
+					$$ = (Node *) s;
+				}
 			| SUBSTRING '(' substr_list ')'
 				{
 					/* substring(A from B for C) is converted to
@@ -14261,6 +14338,7 @@ unreserved_keyword:
 			| VALIDATE
 			| VALIDATOR
 			| VALUE_P
+			| VARIABLE
 			| VARYING
 			| VERSION_P
 			| VIEW
@@ -14301,6 +14379,8 @@ col_name_keyword:
 			| EXISTS
 			| EXTRACT
 			| FLOAT_P
+			| GETATTR
+			| GETVAR
 			| GREATEST
 			| GROUPING
 			| INOUT
@@ -14319,7 +14399,9 @@ col_name_keyword:
 			| PRECISION
 			| REAL
 			| ROW
+			| SETATTR
 			| SETOF
+			| SETVAR
 			| SMALLINT
 			| SUBSTRING
 			| TIME
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 8a2bdf0..a8f722c 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -15,7 +15,9 @@
 
 #include "postgres.h"
 
+#include "access/htup_details.h"
 #include "catalog/pg_type.h"
+#include "catalog/pg_class.h"
 #include "commands/dbcommands.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
@@ -36,6 +38,7 @@
 #include "utils/builtins.h"
 #include "utils/date.h"
 #include "utils/lsyscache.h"
+#include "utils/syscache.h"
 #include "utils/timestamp.h"
 #include "utils/xml.h"
 
@@ -122,6 +125,7 @@ static Node *transformIndirection(ParseState *pstate, Node *basenode,
 					 List *indirection);
 static Node *transformTypeCast(ParseState *pstate, TypeCast *tc);
 static Node *transformCollateClause(ParseState *pstate, CollateClause *c);
+static Node *transformUseVar(ParseState *pstate, UseVar *varexpr);
 static Node *make_row_comparison_op(ParseState *pstate, List *opname,
 					   List *largs, List *rargs, int location);
 static Node *make_row_distinct_op(ParseState *pstate, List *opname,
@@ -376,6 +380,10 @@ transformExprRecurse(ParseState *pstate, Node *expr)
 				break;
 			}
 
+		case T_UseVar:
+				result = transformUseVar(pstate, (UseVar *) expr);
+				break;
+
 		default:
 			/* should not reach here */
 			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
@@ -2768,6 +2776,144 @@ transformCollateClause(ParseState *pstate, CollateClause *c)
 }
 
 /*
+ * Transform access to session variables - setvar, setattr, getvar, getattr
+ *
+ */
+static Node *
+transformUseVar(ParseState *pstate, UseVar *varexpr)
+{
+	UseVarExpr *newe = makeNode(UseVarExpr);
+	char	   *class_name_or_oid = varexpr->name;
+	Oid			varid = InvalidOid;
+	HeapTuple	tuple;
+	Form_pg_class classForm;
+	Oid 		expected_typid;
+	int32		expected_typmod;
+	Oid			expected_collid;
+	AclResult	aclresult;
+
+	/*
+	 * Currently regclass functions are not used. The main reason in this
+	 * moment is a messy error messagers - related to relations. Should be
+	 * fixed.
+	 */
+
+	/* Numeric OID? */
+	if (class_name_or_oid[0] >= '0' &&
+		class_name_or_oid[0] <= '9' &&
+		strspn(class_name_or_oid, "0123456789") == strlen(class_name_or_oid))
+	{
+		varid = DatumGetObjectId(DirectFunctionCall1(oidin,
+										CStringGetDatum(class_name_or_oid)));
+	}
+	else
+	{
+		List	   *names;
+
+		names = stringToQualifiedNameList(class_name_or_oid);
+		varid = RangeVarGetRelid(makeRangeVarFromNameList(names), NoLock, true);
+		if (!OidIsValid(varid))
+			ereport(ERROR,
+					(errcode(ERRCODE_UNDEFINED_TABLE),
+					 errmsg("session variable \"%s\" does not exits",
+						class_name_or_oid),
+				 parser_errposition(pstate, varexpr->location)));
+	}
+
+	tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(varid));
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "cache lookup failed for session variable %u", varid);
+
+	classForm = (Form_pg_class) GETSTRUCT(tuple);
+
+	if (classForm->relkind != RELKIND_VARIABLE)
+		ereport(ERROR,
+				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+				 errmsg("\"%s\" is not a session variable",
+						class_name_or_oid),
+			parser_errposition(pstate, varexpr->location)));
+
+	/* Check permissions */
+	aclresult = pg_class_aclcheck(varid, GetUserId(),
+					varexpr->expr != NULL ? ACL_UPDATE : ACL_SELECT);
+	if (aclresult != ACLCHECK_OK)
+		aclcheck_error(aclresult, ACL_KIND_VARIABLE,
+									class_name_or_oid);
+
+	/*
+	 * Design of session variables allows a variables of composite types. The
+	 * read of this variable can be composite type, when GETVAR is used or
+	 * some other type, when GETATTR is used. The identifier of composite
+	 * variable is valid reloftype attribute.
+	 */
+	if (!OidIsValid(classForm->reloftype))
+	{
+		/* scalar variable */
+		if (varexpr->attrname != NULL)
+			ereport(ERROR,
+				  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				   errmsg("session variable \"%s\" is not a composite variable",
+						  class_name_or_oid),
+				 parser_errposition(pstate, varexpr->location)));
+
+		get_atttypetypmodcoll(varid, (AttrNumber) 1,
+										&expected_typid,
+										&expected_typmod,
+										&expected_collid);
+	}
+	else
+	{
+		/* composite variable */
+		if (varexpr->attrname != NULL)
+		{
+			AttrNumber attnum = get_attnum(varid, varexpr->attrname);
+
+			if (attnum == InvalidAttrNumber)
+				ereport(ERROR,
+						(errcode(ERRCODE_UNDEFINED_COLUMN),
+					errmsg("attribute \"%s\" of variable \"%s\" does not exist",
+						   varexpr->attrname, class_name_or_oid),
+						 parser_errposition(pstate, varexpr->location)));
+
+			get_atttypetypmodcoll(varid, attnum,
+												&expected_typid,
+												&expected_typmod,
+												&expected_collid);
+		}
+		else
+		{
+			expected_typid = classForm->reloftype;
+			expected_typmod = -1;
+
+			/* result is a composite */
+			expected_collid = InvalidOid;
+		}
+	}
+
+	/* enforce casting of expression to expected type */
+	if (varexpr->expr)
+	{
+		/* Note: can be used coerce_to_specific_type_typmod when it will be available */
+
+		newe->expr = coerce_to_specific_type(pstate,
+						transformExprRecurse(pstate, (Node *) varexpr->expr),
+									expected_typid, "transform variable");
+	}
+
+	ReleaseSysCache(tuple);
+
+	newe->varid = varid;
+	newe->attrname = varexpr->attrname;
+	newe->typid = expected_typid;
+	newe->typmod = expected_typmod;
+	newe->collation = expected_collid;
+
+	newe->location = varexpr->location;
+
+	return (Node *) newe;
+}
+
+/*
  * Transform a "row compare-op row" construct
  *
  * The inputs are lists of already-transformed expressions.
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index d440dec..b8e8cc9 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -1868,6 +1868,20 @@ FigureColnameInternal(Node *node, char **name)
 		case T_XmlSerialize:
 			*name = "xmlserialize";
 			return 2;
+		case T_UseVar:
+			{
+				UseVar *usevar = (UseVar *) node;
+
+				if (usevar->attrname != NULL)
+				{
+					*name = usevar->attrname;
+					return 2;
+				}
+
+				*name = usevar->name;
+				return 2;
+			}
+
 		default:
 			break;
 	}
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index fd4eff4..b7ab53d 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -47,6 +47,7 @@
 #include "commands/schemacmds.h"
 #include "commands/seclabel.h"
 #include "commands/sequence.h"
+#include "commands/session_variable.h"
 #include "commands/tablecmds.h"
 #include "commands/tablespace.h"
 #include "commands/trigger.h"
@@ -174,6 +175,7 @@ check_xact_readonly(Node *parsetree)
 		case T_RuleStmt:
 		case T_CreateSchemaStmt:
 		case T_CreateSeqStmt:
+		case T_CreateVarStmt:
 		case T_CreateStmt:
 		case T_CreateTableAsStmt:
 		case T_RefreshMatViewStmt:
@@ -1404,6 +1406,10 @@ ProcessUtilitySlow(ParseState *pstate,
 				address = AlterSequence(pstate, (AlterSeqStmt *) parsetree);
 				break;
 
+			case T_CreateVarStmt:
+				address = DefineSessionVariable(pstate, (CreateVarStmt *) parsetree);
+				break;
+
 			case T_CreateTableAsStmt:
 				address = ExecCreateTableAs((CreateTableAsStmt *) parsetree,
 										 queryString, params, completionTag);
@@ -1607,6 +1613,7 @@ ExecDropStmt(DropStmt *stmt, bool isTopLevel)
 		case OBJECT_VIEW:
 		case OBJECT_MATVIEW:
 		case OBJECT_FOREIGN_TABLE:
+		case OBJECT_VARIABLE:
 			RemoveRelations(stmt);
 			break;
 		default:
@@ -2199,6 +2206,9 @@ CreateCommandTag(Node *parsetree)
 				case OBJECT_ACCESS_METHOD:
 					tag = "DROP ACCESS METHOD";
 					break;
+				case OBJECT_VARIABLE:
+					tag = "DROP VARIABLE";
+					break;
 				default:
 					tag = "???";
 			}
@@ -2347,6 +2357,10 @@ CreateCommandTag(Node *parsetree)
 			tag = "ALTER SEQUENCE";
 			break;
 
+		case T_CreateVarStmt:
+			tag = "CREATE VARIABLE";
+			break;
+
 		case T_DoStmt:
 			tag = "DO";
 			break;
@@ -2939,6 +2953,10 @@ GetCommandLogLevel(Node *parsetree)
 			lev = LOGSTMT_DDL;
 			break;
 
+		case T_CreateVarStmt:
+			lev = LOGSTMT_DDL;
+			break;
+
 		case T_DoStmt:
 			lev = LOGSTMT_ALL;
 			break;
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index 025a99e..79658cf 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -791,6 +791,10 @@ acldefault(GrantObjectType objtype, Oid ownerId)
 			world_default = ACL_USAGE;
 			owner_default = ACL_ALL_RIGHTS_TYPE;
 			break;
+		case ACL_OBJECT_VARIABLE:
+			world_default = ACL_NO_RIGHTS;
+			owner_default = ACL_ALL_RIGHTS_SEQUENCE;
+			break;
 		default:
 			elog(ERROR, "unrecognized objtype: %d", (int) objtype);
 			world_default = ACL_NO_RIGHTS;		/* keep compiler quiet */
@@ -886,6 +890,9 @@ acldefault_sql(PG_FUNCTION_ARGS)
 		case 'T':
 			objtype = ACL_OBJECT_TYPE;
 			break;
+		case 'v':
+			objtype = ACL_OBJECT_VARIABLE;
+			break;
 		default:
 			elog(ERROR, "unrecognized objtype abbreviation: %c", objtypec);
 	}
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 4e2ba19..2f493b1 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -8547,6 +8547,49 @@ get_rule_expr(Node *node, deparse_context *context,
 			}
 			break;
 
+		case T_UseVarExpr:
+			{
+				UseVarExpr *varexpr = (UseVarExpr *) node;
+				char *varname = generate_relation_name(varexpr->varid, NIL);
+
+				if (varexpr->expr != NULL)
+				{
+					if (varexpr->attrname != NULL)
+						appendStringInfoString(buf, "SETATTR(");
+					else
+						appendStringInfoString(buf, "SETVAR(");
+
+					simple_quote_literal(buf, varname);
+
+					if (varexpr->attrname != NULL)
+					{
+						appendStringInfoChar(buf, ',');
+						simple_quote_literal(buf, varexpr->attrname);
+					}
+
+					appendStringInfoChar(buf, ',');
+					get_rule_expr(varexpr->expr, context, showimplicit);
+				}
+				else
+				{
+					if (varexpr->attrname != NULL)
+						appendStringInfoString(buf, "GETATTR(");
+					else
+						appendStringInfoString(buf, "GETVAR(");
+
+					simple_quote_literal(buf, varname);
+
+					if (varexpr->attrname != NULL)
+					{
+						appendStringInfoChar(buf, ',');
+						simple_quote_literal(buf, varexpr->attrname);
+					}
+				}
+
+				appendStringInfoChar(buf, ')');
+			}
+			break;
+
 		default:
 			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
 			break;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index a582a37..f3d5626 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -1676,6 +1676,10 @@ describeOneTableDetails(const char *schemaname,
 				printfPQExpBuffer(&title, _("Table \"%s.%s\""),
 								  schemaname, relationname);
 			break;
+		case 'V':
+			printfPQExpBuffer(&title, _("Variable \"%s.%s\""),
+							  schemaname, relationname);
+			break;
 		default:
 			/* untranslated unknown relkind */
 			printfPQExpBuffer(&title, "?%c? \"%s.%s\"",
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index cd64c39..d9b4242 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -584,6 +584,21 @@ static const SchemaQuery Query_for_list_of_matviews = {
 	NULL
 };
 
+static const SchemaQuery Query_for_list_of_variables = {
+	/* catname */
+	"pg_catalog.pg_class c",
+	/* selcondition */
+	"c.relkind IN ('V')",
+	/* viscondition */
+	"pg_catalog.pg_table_is_visible(c.oid)",
+	/* namespace */
+	"c.relnamespace",
+	/* result */
+	"pg_catalog.quote_ident(c.relname)",
+	/* qualresult */
+	NULL
+};
+
 
 /*
  * Queries to get lists of names of various kinds of things, possibly
@@ -977,6 +992,7 @@ static const pgsql_thing_t words_after_create[] = {
 												 * ... */
 	{"USER", Query_for_list_of_roles},
 	{"USER MAPPING FOR", NULL, NULL},
+	{"VARIABLE", NULL, &Query_for_list_of_variables},
 	{"VIEW", NULL, &Query_for_list_of_views},
 	{NULL}						/* end of list */
 };
@@ -2390,6 +2406,10 @@ psql_completion(const char *text, int start, int end)
 	else if (Matches5("CREATE", "EVENT", "TRIGGER", MatchAny, "ON"))
 		COMPLETE_WITH_LIST3("ddl_command_start", "ddl_command_end", "sql_drop");
 
+/* CREATE VARIABLE */
+	else if (Matches4("CREATE", "VARIABLE", MatchAny, "AS"))
+			COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
+
 /* DECLARE */
 	else if (Matches2("DECLARE", MatchAny))
 		COMPLETE_WITH_LIST5("BINARY", "INSENSITIVE", "SCROLL", "NO SCROLL",
@@ -2511,6 +2531,8 @@ psql_completion(const char *text, int start, int end)
 	}
 	else if (Matches5("DROP", "RULE", MatchAny, "ON", MatchAny))
 		COMPLETE_WITH_LIST2("CASCADE", "RESTRICT");
+	else if (Matches2("DROP", "VARIABLE"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_variables, NULL);
 
 /* EXECUTE */
 	else if (Matches1("EXECUTE"))
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index a61b7a2..42ec21d 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -166,6 +166,7 @@ DESCR("");
 #define		  RELKIND_FOREIGN_TABLE   'f'		/* foreign table */
 #define		  RELKIND_MATVIEW		  'm'		/* materialized view */
 #define		  RELKIND_PARTITIONED_TABLE 'P'		/* partitioned table */
+#define		  RELKIND_VARIABLE		  'V'		/* session variable */
 
 #define		  RELPERSISTENCE_PERMANENT	'p'		/* regular table */
 #define		  RELPERSISTENCE_UNLOGGED	'u'		/* unlogged permanent table */
diff --git a/src/include/catalog/pg_default_acl.h b/src/include/catalog/pg_default_acl.h
index 06aaaba..6e47c06 100644
--- a/src/include/catalog/pg_default_acl.h
+++ b/src/include/catalog/pg_default_acl.h
@@ -70,5 +70,6 @@ typedef FormData_pg_default_acl *Form_pg_default_acl;
 #define DEFACLOBJ_SEQUENCE		'S'		/* sequence */
 #define DEFACLOBJ_FUNCTION		'f'		/* function */
 #define DEFACLOBJ_TYPE			'T'		/* type */
+#define DEFACLOBJ_VARIABLE		'v'		/* session variable */
 
 #endif   /* PG_DEFAULT_ACL_H */
diff --git a/src/include/commands/session_variable.h b/src/include/commands/session_variable.h
new file mode 100644
index 0000000..d7c5a39
--- /dev/null
+++ b/src/include/commands/session_variable.h
@@ -0,0 +1,31 @@
+/*-------------------------------------------------------------------------
+ *
+ * session_variable.h
+ *	  prototypes for session_variable.c.
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/commands/session_variable.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef SESSION_VARIABLE_H
+#define SESSION_VARIABLE_H
+
+#include "catalog/objectaddress.h"
+#include "nodes/parsenodes.h"
+
+extern void SetSessionVariable(Oid varid, char *attrname, Datum value, bool isNull,
+						Oid	typid, int32 typmod,
+						int16 typlen, bool typbyval, char typalign);
+
+extern Datum GetSessionVariable(Oid varid, char *attrname, bool *isNull,
+						Oid	typid, int32 typmod,
+						int16 typlen, bool typbyval, char typalign);
+
+extern ObjectAddress DefineSessionVariable(ParseState *pstate, CreateVarStmt *stmt);
+
+extern void ResetVariablesCache(void);
+
+#endif   /* SESSION_VARIABLE_H */
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 1de5c81..89b328b 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -2082,4 +2082,23 @@ typedef struct LimitState
 	TupleTableSlot *subSlot;	/* tuple last obtained from subplan */
 } LimitState;
 
+/* ----------------
+ * UseVarExprState information
+ *
+ * Controlled access to session variables
+ * ----------------
+ */
+typedef struct UseVarExprState
+{
+	ExprState	xprstate;
+	ExprState  *expr;
+	Oid			varid;
+	char	   *attrname;
+	Oid			typid;
+	int32		typmod;
+	int16		typlen;
+	bool		typbyval;
+	char		typalign;
+} UseVarExprState;
+
 #endif   /* EXECNODES_H */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index c514d3f..b0bc214 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -181,6 +181,7 @@ typedef enum NodeTag
 	T_FromExpr,
 	T_OnConflictExpr,
 	T_IntoClause,
+	T_UseVarExpr,
 
 	/*
 	 * TAGS FOR EXPRESSION STATE NODES (execnodes.h)
@@ -216,6 +217,7 @@ typedef enum NodeTag
 	T_NullTestState,
 	T_CoerceToDomainState,
 	T_DomainConstraintState,
+	T_UseVarExprState,
 
 	/*
 	 * TAGS FOR PLANNER NODES (relation.h)
@@ -342,6 +344,7 @@ typedef enum NodeTag
 	T_ExplainStmt,
 	T_CreateTableAsStmt,
 	T_CreateSeqStmt,
+	T_CreateVarStmt,
 	T_AlterSeqStmt,
 	T_VariableSetStmt,
 	T_VariableShowStmt,
@@ -420,6 +423,7 @@ typedef enum NodeTag
 	T_A_Indices,
 	T_A_Indirection,
 	T_A_ArrayExpr,
+	T_UseVar,
 	T_ResTarget,
 	T_MultiAssignRef,
 	T_TypeCast,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index fc532fb..f0ad2b4 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -400,6 +400,18 @@ typedef struct A_ArrayExpr
 } A_ArrayExpr;
 
 /*
+ * SETVAR(string, expr), SETATTR(string, string, expr)
+ */
+typedef struct UseVar
+{
+	NodeTag		type;
+	char	   *name;			/* Sconst of session variable name */
+	char	   *attrname;		/* Sconst of attr name or NULL */
+	Node	   *expr;			/* future value, NULL for GETVAR, GETATTR */
+	int			location;		/* token location */
+} UseVar;
+
+/*
  * ResTarget -
  *	  result target (used in target list of pre-transformed parse trees)
  *
@@ -1527,6 +1539,7 @@ typedef enum ObjectType
 	OBJECT_TSTEMPLATE,
 	OBJECT_TYPE,
 	OBJECT_USER_MAPPING,
+	OBJECT_VARIABLE,
 	OBJECT_VIEW
 } ObjectType;
 
@@ -1706,7 +1719,8 @@ typedef enum GrantObjectType
 	ACL_OBJECT_LARGEOBJECT,		/* largeobject */
 	ACL_OBJECT_NAMESPACE,		/* namespace */
 	ACL_OBJECT_TABLESPACE,		/* tablespace */
-	ACL_OBJECT_TYPE				/* type */
+	ACL_OBJECT_TYPE,			/* type */
+	ACL_OBJECT_VARIABLE			/* session variable */
 } GrantObjectType;
 
 typedef struct GrantStmt
@@ -2331,6 +2345,19 @@ typedef struct AlterSeqStmt
 } AlterSeqStmt;
 
 /* ----------------------
+ *		Create VARIABLE Statement
+ * ----------------------
+ */
+
+typedef struct CreateVarStmt
+{
+	NodeTag		type;
+	RangeVar   *variable;		/* the variable to create */
+	TypeName   *typeName;		/* the variable type */
+	bool		if_not_exists;	/* just do nothing if it already exists? */
+} CreateVarStmt;
+
+/* ----------------------
  *		Create {Aggregate|Operator|Type} Statement
  * ----------------------
  */
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 65510b0..58c0c69 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -1467,4 +1467,21 @@ typedef struct OnConflictExpr
 	List	   *exclRelTlist;	/* tlist of the EXCLUDED pseudo relation */
 } OnConflictExpr;
 
+/*----------
+ * UseVarExpr  used for manipulation with session variables
+ *
+ *----------
+ */
+typedef struct UseVarExpr
+{
+	NodeTag		type;
+	Oid			varid;
+	char	   *attrname;
+	Node	   *expr;		/* NULL, when read session var */
+	Oid			typid;		/* typid of result (var or varattr) */
+	int32		typmod;		/* typmod of result (var or varattr) */
+	Oid			collation;
+	int			location;
+} UseVarExpr;
+
 #endif   /* PRIMNODES_H */
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 581ff6e..6253dda 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -173,6 +173,8 @@ PG_KEYWORD("from", FROM, RESERVED_KEYWORD)
 PG_KEYWORD("full", FULL, TYPE_FUNC_NAME_KEYWORD)
 PG_KEYWORD("function", FUNCTION, UNRESERVED_KEYWORD)
 PG_KEYWORD("functions", FUNCTIONS, UNRESERVED_KEYWORD)
+PG_KEYWORD("getattr", GETATTR, COL_NAME_KEYWORD)
+PG_KEYWORD("getvar", GETVAR, COL_NAME_KEYWORD)
 PG_KEYWORD("global", GLOBAL, UNRESERVED_KEYWORD)
 PG_KEYWORD("grant", GRANT, RESERVED_KEYWORD)
 PG_KEYWORD("granted", GRANTED, UNRESERVED_KEYWORD)
@@ -353,8 +355,10 @@ PG_KEYWORD("server", SERVER, UNRESERVED_KEYWORD)
 PG_KEYWORD("session", SESSION, UNRESERVED_KEYWORD)
 PG_KEYWORD("session_user", SESSION_USER, RESERVED_KEYWORD)
 PG_KEYWORD("set", SET, UNRESERVED_KEYWORD)
+PG_KEYWORD("setattr", SETATTR, COL_NAME_KEYWORD)
 PG_KEYWORD("setof", SETOF, COL_NAME_KEYWORD)
 PG_KEYWORD("sets", SETS, UNRESERVED_KEYWORD)
+PG_KEYWORD("setvar", SETVAR, COL_NAME_KEYWORD)
 PG_KEYWORD("share", SHARE, UNRESERVED_KEYWORD)
 PG_KEYWORD("show", SHOW, UNRESERVED_KEYWORD)
 PG_KEYWORD("similar", SIMILAR, TYPE_FUNC_NAME_KEYWORD)
@@ -420,6 +424,7 @@ PG_KEYWORD("validator", VALIDATOR, UNRESERVED_KEYWORD)
 PG_KEYWORD("value", VALUE_P, UNRESERVED_KEYWORD)
 PG_KEYWORD("values", VALUES, COL_NAME_KEYWORD)
 PG_KEYWORD("varchar", VARCHAR, COL_NAME_KEYWORD)
+PG_KEYWORD("variable", VARIABLE, UNRESERVED_KEYWORD)
 PG_KEYWORD("variadic", VARIADIC, RESERVED_KEYWORD)
 PG_KEYWORD("varying", VARYING, UNRESERVED_KEYWORD)
 PG_KEYWORD("verbose", VERBOSE, TYPE_FUNC_NAME_KEYWORD)
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index fda75bb..41c98a9 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -158,6 +158,7 @@ typedef ArrayType Acl;
 #define ACL_ALL_RIGHTS_NAMESPACE	(ACL_USAGE|ACL_CREATE)
 #define ACL_ALL_RIGHTS_TABLESPACE	(ACL_CREATE)
 #define ACL_ALL_RIGHTS_TYPE			(ACL_USAGE)
+#define ACL_ALL_RIGHTS_VARIABLE		(ACL_SELECT|ACL_UPDATE)
 
 /* operation codes for pg_*_aclmask */
 typedef enum
@@ -199,6 +200,7 @@ typedef enum AclObjectKind
 	ACL_KIND_FOREIGN_SERVER,	/* pg_foreign_server */
 	ACL_KIND_EVENT_TRIGGER,		/* pg_event_trigger */
 	ACL_KIND_EXTENSION,			/* pg_extension */
+	ACL_KIND_VARIABLE,			/* session variable */
 	MAX_ACL_KIND				/* MUST BE LAST */
 } AclObjectKind;
 
diff --git a/src/test/regress/expected/variables.out b/src/test/regress/expected/variables.out
new file mode 100644
index 0000000..20db9d5
--- /dev/null
+++ b/src/test/regress/expected/variables.out
@@ -0,0 +1,256 @@
+create type composite_type as (x integer, y integer, z integer);
+create variable var1 as integer;
+create variable var2 as numeric;
+create variable var3 as text;
+create variable var4 as composite_type;
+create variable var5 as integer[];
+create variable var6 as composite_type;
+select getvar('var1'), getvar('var2'), getvar('var3'), getvar('var4'), getvar('var5'), getvar('var6');
+ var1 | var2 | var3 | var4 | var5 | var6 
+------+------+------+------+------+------
+      |      |      |      |      | 
+(1 row)
+
+select setvar('var1', 1);
+ var1 
+------
+    1
+(1 row)
+
+select setvar('var2', 12222222222.1234);
+       var2       
+------------------
+ 12222222222.1234
+(1 row)
+
+select setvar('var3', 'today is a pretty day');
+         var3          
+-----------------------
+ today is a pretty day
+(1 row)
+
+select setvar('var4', '(1,2,3)');
+  var4   
+---------
+ (1,2,3)
+(1 row)
+
+select setvar('var5', ARRAY[9,8,7,6,5,4,3,2,1]);
+        var5         
+---------------------
+ {9,8,7,6,5,4,3,2,1}
+(1 row)
+
+select setvar('var6', ROW(10,20,30));
+    var6    
+------------
+ (10,20,30)
+(1 row)
+
+select getvar('var1'), pg_typeof(getvar('var1'));
+ var1 | pg_typeof 
+------+-----------
+    1 | integer
+(1 row)
+
+select getvar('var2'), pg_typeof(getvar('var2'));
+       var2       | pg_typeof 
+------------------+-----------
+ 12222222222.1234 | numeric
+(1 row)
+
+select getvar('var3'), pg_typeof(getvar('var3'));
+         var3          | pg_typeof 
+-----------------------+-----------
+ today is a pretty day | text
+(1 row)
+
+select getvar('var4'), pg_typeof(getvar('var4'));
+  var4   |   pg_typeof    
+---------+----------------
+ (1,2,3) | composite_type
+(1 row)
+
+select getvar('var5'), pg_typeof(getvar('var5'));
+        var5         | pg_typeof 
+---------------------+-----------
+ {9,8,7,6,5,4,3,2,1} | integer[]
+(1 row)
+
+select getvar('var6'), pg_typeof(getvar('var6'));
+    var6    |   pg_typeof    
+------------+----------------
+ (10,20,30) | composite_type
+(1 row)
+
+-- second setting should to work too
+select setvar('var3', 'today is a super pretty day');
+            var3             
+-----------------------------
+ today is a super pretty day
+(1 row)
+
+select getvar('var3'), pg_typeof(getvar('var3'));
+            var3             | pg_typeof 
+-----------------------------+-----------
+ today is a super pretty day | text
+(1 row)
+
+explain (verbose, cost off) select getvar('var6'), pg_typeof(getvar('var6'));
+ERROR:  unrecognized EXPLAIN option "cost"
+LINE 1: explain (verbose, cost off) select getvar('var6'), pg_typeof...
+                          ^
+select (getvar('var6')).y;
+ y  
+----
+ 20
+(1 row)
+
+select (getvar('var5'))[2];
+ var5 
+------
+    8
+(1 row)
+
+-- access to attributes of composite fields
+create type composite_type_2 as (a int, b text);
+create variable var7 composite_type_2;
+select getvar('var7');
+ var7 
+------
+ 
+(1 row)
+
+select * from getvar('var7');
+ a | b 
+---+---
+   | 
+(1 row)
+
+select getattr('var7', 'b');
+ b 
+---
+ 
+(1 row)
+
+select setvar('var7','(10,Hello world\, Hello world\, Hello world)');
+                     var7                     
+----------------------------------------------
+ (10,"Hello world, Hello world, Hello world")
+(1 row)
+
+select * from getvar('var7');
+ a  |                   b                   
+----+---------------------------------------
+ 10 | Hello world, Hello world, Hello world
+(1 row)
+
+select getattr('var7','a');
+ a  
+----
+ 10
+(1 row)
+
+select getattr('var7','b');
+                   b                   
+---------------------------------------
+ Hello world, Hello world, Hello world
+(1 row)
+
+select setattr('var7', 'a', 1000);
+  a   
+------
+ 1000
+(1 row)
+
+select * from getvar('var7');
+  a   |                   b                   
+------+---------------------------------------
+ 1000 | Hello world, Hello world, Hello world
+(1 row)
+
+select setattr('var7', 'b', 'Hola, hola!');
+      b      
+-------------
+ Hola, hola!
+(1 row)
+
+select * from getvar('var7');
+  a   |      b      
+------+-------------
+ 1000 | Hola, hola!
+(1 row)
+
+select getattr('var7','b');
+      b      
+-------------
+ Hola, hola!
+(1 row)
+
+drop variable var7;
+drop type composite_type_2;
+-- getvar, setvar are volatile functions
+create variable _id as integer;
+select setvar('_id', coalesce(getvar('_id') + 1,1)) from generate_series(1,10);
+ _id 
+-----
+   1
+   2
+   3
+   4
+   5
+   6
+   7
+   8
+   9
+  10
+(10 rows)
+
+explain verbose select setvar('_id', coalesce(getvar('_id') + 1,1)) from generate_series(1,10);
+                                    QUERY PLAN                                     
+-----------------------------------------------------------------------------------
+ Function Scan on pg_catalog.generate_series  (cost=0.00..12.50 rows=1000 width=4)
+   Output: SETVAR('_id',COALESCE((GETVAR('_id') + 1), 1))
+   Function Call: generate_series(1, 10)
+(3 rows)
+
+drop variable _id;
+-- should fail
+drop variable var1;
+select getvar('var1'), pg_typeof(getvar('var1'));
+ERROR:  session variable "var1" does not exits
+LINE 1: select getvar('var1'), pg_typeof(getvar('var1'));
+               ^
+-- SQL access is not supported yet, should fail (but syntax is valid,
+-- and should be supported in future time)
+select value from var3;
+ERROR:  cannot read session variable "var3" as relation
+select x from var4;
+ERROR:  cannot read session variable "var4" as relation
+update var3 set value = 'Hello, Hello';
+ERROR:  cannot change session variable "var3"
+update var4 set x = 100;
+ERROR:  cannot change session variable "var4"
+-- access tests
+create role var_test_role login;
+grant select on variable var2 to var_test_role;
+grant update on variable var3 to var_test_role;
+set role var_test_role;
+-- should fail
+select setvar('var2', 3.3);
+ERROR:  permission denied for variable var2
+select getvar('var3');
+ERROR:  permission denied for variable var3
+-- should be ok
+select getvar('var2');
+       var2       
+------------------
+ 12222222222.1234
+(1 row)
+
+select setvar('var3', 'Hello, Hello, Hello');
+        var3         
+---------------------
+ Hello, Hello, Hello
+(1 row)
+
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 8641769..d6a5b9d 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -106,7 +106,7 @@ test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combo
 # NB: temp.sql does a reconnect which transiently uses 2 connections,
 # so keep this parallel group to at most 19 tests
 # ----------
-test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml
+test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml variables
 
 # event triggers cannot run concurrently with any test that runs DDL
 test: event_trigger
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 835cf35..90ce8c9 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -169,3 +169,4 @@ test: with
 test: xml
 test: event_trigger
 test: stats
+test: variables
diff --git a/src/test/regress/sql/variables.sql b/src/test/regress/sql/variables.sql
new file mode 100644
index 0000000..0d502aa
--- /dev/null
+++ b/src/test/regress/sql/variables.sql
@@ -0,0 +1,85 @@
+create type composite_type as (x integer, y integer, z integer);
+
+create variable var1 as integer;
+create variable var2 as numeric;
+create variable var3 as text;
+create variable var4 as composite_type;
+create variable var5 as integer[];
+create variable var6 as composite_type;
+
+select getvar('var1'), getvar('var2'), getvar('var3'), getvar('var4'), getvar('var5'), getvar('var6');
+
+select setvar('var1', 1);
+select setvar('var2', 12222222222.1234);
+select setvar('var3', 'today is a pretty day');
+select setvar('var4', '(1,2,3)');
+select setvar('var5', ARRAY[9,8,7,6,5,4,3,2,1]);
+select setvar('var6', ROW(10,20,30));
+
+select getvar('var1'), pg_typeof(getvar('var1'));
+select getvar('var2'), pg_typeof(getvar('var2'));
+select getvar('var3'), pg_typeof(getvar('var3'));
+select getvar('var4'), pg_typeof(getvar('var4'));
+select getvar('var5'), pg_typeof(getvar('var5'));
+select getvar('var6'), pg_typeof(getvar('var6'));
+
+-- second setting should to work too
+select setvar('var3', 'today is a super pretty day');
+select getvar('var3'), pg_typeof(getvar('var3'));
+
+explain (verbose, cost off) select getvar('var6'), pg_typeof(getvar('var6'));
+
+select (getvar('var6')).y;
+select (getvar('var5'))[2];
+
+-- access to attributes of composite fields
+create type composite_type_2 as (a int, b text);
+create variable var7 composite_type_2;
+select getvar('var7');
+select * from getvar('var7');
+select getattr('var7', 'b');
+select setvar('var7','(10,Hello world\, Hello world\, Hello world)');
+select * from getvar('var7');
+select getattr('var7','a');
+select getattr('var7','b');
+select setattr('var7', 'a', 1000);
+select * from getvar('var7');
+select setattr('var7', 'b', 'Hola, hola!');
+select * from getvar('var7');
+select getattr('var7','b');
+
+drop variable var7;
+drop type composite_type_2;
+
+-- getvar, setvar are volatile functions
+create variable _id as integer;
+select setvar('_id', coalesce(getvar('_id') + 1,1)) from generate_series(1,10);
+explain verbose select setvar('_id', coalesce(getvar('_id') + 1,1)) from generate_series(1,10);
+drop variable _id;
+
+-- should fail
+drop variable var1;
+select getvar('var1'), pg_typeof(getvar('var1'));
+
+-- SQL access is not supported yet, should fail (but syntax is valid,
+-- and should be supported in future time)
+select value from var3;
+select x from var4;
+update var3 set value = 'Hello, Hello';
+update var4 set x = 100;
+
+-- access tests
+create role var_test_role login;
+
+grant select on variable var2 to var_test_role;
+grant update on variable var3 to var_test_role;
+
+set role var_test_role;
+
+-- should fail
+select setvar('var2', 3.3);
+select getvar('var3');
+
+-- should be ok
+select getvar('var2');
+select setvar('var3', 'Hello, Hello, Hello');
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to