Thread: ALTER SESSION

ALTER SESSION

From
Kyotaro HORIGUCHI
Date:
Hello.

https://www.postgresql.org/message-id/20190128.133143.115303042.horiguchi.kyotaro@lab.ntt.co.jp

> C. Provide session-specific GUC variable (that overides the global one)
>    - Add new configuration file "postgresql.conf.<PID>" and
>      pg_reload_conf() let the session with the PID loads it as if
>      it is the last included file. All such files are removed at
>      startup or at the end of the coressponding session.
> 
>    - Add a new syntax like this:
>      ALTER SESSION WITH (pid=xxxx)
>         SET configuration_parameter {TO | =} {value | 'value' | DEFAULT}
>         RESET configuration_parameter
>         RESET ALL
> 
>    - Target variables are marked with GUC_REMOTE.
> 
> I'll consider the last choice and will come up with a patch.

This is that, with a small change in design.

ALTER SESSION WITH (pid <pid>) SET param {TO|=} value [ IMMEDIATE ]
ALTER SESSION WITH (pid <pid>) RESET param [ IMMEDIATE ]
ALTER SESSION WITH (pid <pid>) RESET ALL

The first form create an entry in
$PGDATA/pg_session_conf/postgresql.<beid>.conf.
The second form removes the entry.
The third form removes the file itself.

IMMEDIATE specifies that the change is applied immediately by
sending SIGHUP to the process. pg_reload_conf() works as well.

The session configuration is removed at session-end and the
directory is cleaned up at startup.

It can change varaibles of PGC_USERSET by non-superuser or
PGC_SUSET by superuser. The local session user should have the
same privileges with pg_signal_backend() on the target session.

This patch contains documentation but doesn't contain test yet.

I would appreciate any comments or suggestions on this.

regards.

-- 
Kyotaro Horiguchi
NTT Open Source Software Center
From 03c53ca5259c2cc4b66c6339ea494de9016d9263 Mon Sep 17 00:00:00 2001
From: Kyotaro Horiguchi <horiguchi.kyotaro@lab.ntt.co.jp>
Date: Tue, 29 Jan 2019 17:03:57 +0900
Subject: [PATCH] ALTER SESSION

Some tracking features are controled by GUC so they cannot be
activated from another session. The ALTER SESSION command allows
sessions to change some of GUC settings of another session.
---
 doc/src/sgml/config.sgml             |  26 ++-
 doc/src/sgml/ref/allfiles.sgml       |   1 +
 doc/src/sgml/reference.sgml          |   1 +
 src/backend/nodes/copyfuncs.c        |  14 ++
 src/backend/nodes/equalfuncs.c       |  12 ++
 src/backend/parser/gram.y            |  47 ++++-
 src/backend/postmaster/postmaster.c  |   6 +
 src/backend/replication/basebackup.c |   3 +
 src/backend/tcop/utility.c           |  13 ++
 src/backend/utils/init/postinit.c    |   3 +
 src/backend/utils/misc/guc-file.l    |  24 +++
 src/backend/utils/misc/guc.c         | 334 +++++++++++++++++++++++++++--------
 src/bin/initdb/initdb.c              |   3 +-
 src/include/nodes/nodes.h            |   1 +
 src/include/nodes/parsenodes.h       |  12 ++
 src/include/utils/guc.h              |  11 ++
 16 files changed, 433 insertions(+), 78 deletions(-)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index b6f5822b84..a52989342a 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -175,6 +175,14 @@ shared_buffers = 128MB
      read whenever <filename>postgresql.conf</filename> is, and its settings take
      effect in the same way.  Settings in <filename>postgresql.auto.conf</filename>
      override those in <filename>postgresql.conf</filename>.
+
+    </para>
+    <para>
+     Furthermore a directory <filename>pg_session_conf</filename> in the
+     PostgreSQL data directory contains per-session configuration files. Every
+     file holds settings provided through the
+     <xref linkend="sql-altersession"/> command. This is read by reloading
+     after the two above files and removed at the session-end.
     </para>
 
     <para>
@@ -195,8 +203,8 @@ shared_buffers = 128MB
       The already-mentioned <xref linkend="sql-altersystem"/> command
       provides a SQL-accessible means of changing global defaults; it is
       functionally equivalent to editing <filename>postgresql.conf</filename>.
-      In addition, there are two commands that allow setting of defaults
-      on a per-database or per-role basis:
+      In addition, there are three commands that allow setting of defaults
+      on a per-database, per-role or per-session basis:
      </para>
 
      <itemizedlist>
@@ -213,6 +221,13 @@ shared_buffers = 128MB
        per-database settings to be overridden with user-specific values.
       </para>
      </listitem>
+
+     <listitem>
+      <para>
+       The <xref linkend="sql-altersession"/> command allows other sessions to
+        override session-local settings with user-specific values.
+      </para>
+     </listitem>
     </itemizedlist>
 
      <para>
@@ -223,6 +238,13 @@ shared_buffers = 128MB
       Note that some settings cannot be changed after server start, and
       so cannot be set with these commands (or the ones listed below).
     </para>
+     <para>
+      Values set with <command>ALTER SESSION</command> are applied only when
+      reloading.  They override values obtained from the configuration files
+      or server command line, and constitute defaults for the rest of the
+      session.  Note that it can change only settings that are changeable
+      on-session.
+    </para>
 
      <para>
       Once a client is connected to the database, <productname>PostgreSQL</productname>
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index c81c87ef41..650ae49a24 100644
--- a/doc/src/sgml/ref/allfiles.sgml
+++ b/doc/src/sgml/ref/allfiles.sgml
@@ -34,6 +34,7 @@ Complete list of usable sgml source files in this directory.
 <!ENTITY alterSchema        SYSTEM "alter_schema.sgml">
 <!ENTITY alterServer        SYSTEM "alter_server.sgml">
 <!ENTITY alterSequence      SYSTEM "alter_sequence.sgml">
+<!ENTITY alterSession       SYSTEM "alter_session.sgml">
 <!ENTITY alterSubscription  SYSTEM "alter_subscription.sgml">
 <!ENTITY alterSystem        SYSTEM "alter_system.sgml">
 <!ENTITY alterStatistics    SYSTEM "alter_statistics.sgml">
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
index db4f4167e3..85f515e2b2 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -62,6 +62,7 @@
    &alterSchema;
    &alterSequence;
    &alterServer;
+   &alterSession;
    &alterStatistics;
    &alterSubscription;
    &alterSystem;
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 3eb7e95d64..108bf6976b 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3923,6 +3923,17 @@ _copyAlterSystemStmt(const AlterSystemStmt *from)
     return newnode;
 }
 
+static AlterSessionStmt *
+_copyAlterSessionStmt(const AlterSessionStmt *from)
+{
+    AlterSessionStmt *newnode = makeNode(AlterSessionStmt);
+
+    COPY_NODE_FIELD(sessionopt);
+    COPY_NODE_FIELD(setstmt);
+    COPY_SCALAR_FIELD(immediate);
+    return newnode;
+}
+
 static CreateSeqStmt *
 _copyCreateSeqStmt(const CreateSeqStmt *from)
 {
@@ -5341,6 +5352,9 @@ copyObjectImpl(const void *from)
         case T_AlterSystemStmt:
             retval = _copyAlterSystemStmt(from);
             break;
+        case T_AlterSessionStmt:
+            retval = _copyAlterSessionStmt(from);
+            break;
         case T_CreateSeqStmt:
             retval = _copyCreateSeqStmt(from);
             break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 5c4fa7d077..34e13412a0 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1732,6 +1732,15 @@ _equalAlterSystemStmt(const AlterSystemStmt *a, const AlterSystemStmt *b)
     return true;
 }
 
+static bool
+_equalAlterSessionStmt(const AlterSessionStmt *a, const AlterSessionStmt *b)
+{
+    COMPARE_NODE_FIELD(sessionopt);
+    COMPARE_NODE_FIELD(setstmt);
+    COMPARE_SCALAR_FIELD(immediate);
+
+    return true;
+}
 
 static bool
 _equalCreateSeqStmt(const CreateSeqStmt *a, const CreateSeqStmt *b)
@@ -3406,6 +3415,9 @@ equal(const void *a, const void *b)
         case T_AlterSystemStmt:
             retval = _equalAlterSystemStmt(a, b);
             break;
+        case T_AlterSessionStmt:
+            retval = _equalAlterSessionStmt(a, b);
+            break;
         case T_CreateSeqStmt:
             retval = _equalCreateSeqStmt(a, b);
             break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index c1faf4152c..33d4922bfb 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -248,8 +248,9 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
         AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterEnumStmt
         AlterFdwStmt AlterForeignServerStmt AlterGroupStmt
         AlterObjectDependsStmt AlterObjectSchemaStmt AlterOwnerStmt
-        AlterOperatorStmt AlterSeqStmt AlterSystemStmt AlterTableStmt
-        AlterTblSpcStmt AlterExtensionStmt AlterExtensionContentsStmt AlterForeignTableStmt
+        AlterOperatorStmt AlterSeqStmt AlterSystemStmt AlterSessionStmt
+        AlterTableStmt AlterTblSpcStmt AlterExtensionStmt
+        AlterExtensionContentsStmt AlterForeignTableStmt
         AlterCompositeTypeStmt AlterUserMappingStmt
         AlterRoleStmt AlterRoleSetStmt AlterPolicyStmt
         AlterDefaultPrivilegesStmt DefACLAction
@@ -303,13 +304,14 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 %type <defelt>    createdb_opt_item copy_opt_item
                 transaction_mode_item
                 create_extension_opt_item alter_extension_opt_item
+                altersess_option_item
 
 %type <ival>    opt_lock lock_type cast_context
 %type <ival>    vacuum_option_list vacuum_option_elem
                 analyze_option_list analyze_option_elem
 %type <boolean>    opt_or_replace
                 opt_grant_grant_option opt_grant_admin_option
-                opt_nowait opt_if_exists opt_with_data
+                opt_nowait opt_if_exists opt_with_data opt_immediate
 %type <ival>    opt_nowait_or_skip
 
 %type <list>    OptRoleList AlterOptRoleList
@@ -839,6 +841,7 @@ stmt :
             | AlterPolicyStmt
             | AlterSeqStmt
             | AlterSystemStmt
+            | AlterSessionStmt
             | AlterTableStmt
             | AlterTblSpcStmt
             | AlterCompositeTypeStmt
@@ -10169,6 +10172,44 @@ AlterSystemStmt:
         ;
 
 
+/*****************************************************************************
+ *
+ *        ALTER SESSION
+ *
+ * This is used to change configuration parameters persistently.
+ *****************************************************************************/
+
+AlterSessionStmt:
+            ALTER SESSION WITH '(' altersess_option_item ')' SET generic_set opt_immediate
+                {
+                    AlterSessionStmt *n = makeNode(AlterSessionStmt);
+                    n->sessionopt = $5;
+                    n->setstmt = $8;
+                    n->immediate = $9;
+                    $$ = (Node *)n;
+                }
+            | ALTER SESSION WITH '(' altersess_option_item ')' RESET generic_reset opt_immediate
+                {
+                    AlterSessionStmt *n = makeNode(AlterSessionStmt);
+                    n->sessionopt = $5;
+                    n->setstmt = $8;
+                    n->immediate = $9;
+                    $$ = (Node *)n;
+                }
+        ;
+
+altersess_option_item:
+        ColLabel Iconst
+        {
+            $$ = makeDefElem($1, (Node *)makeInteger($2), @1);
+        }
+    ;
+
+opt_immediate:
+        IMMEDIATE    { $$ = true; }
+        | /*EMPTY*/ { $$ = false; }
+    ;
+
 /*****************************************************************************
  *
  * Manipulate a domain
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 3052bbbc21..1a32bb5f52 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -125,6 +125,7 @@
 #include "tcop/tcopprot.h"
 #include "utils/builtins.h"
 #include "utils/datetime.h"
+#include "utils/guc.h"
 #include "utils/memutils.h"
 #include "utils/pidfile.h"
 #include "utils/ps_status.h"
@@ -1242,6 +1243,11 @@ PostmasterMain(int argc, char *argv[])
      */
     RemovePgTempFiles();
 
+    /*
+     * Remove old session configuration files.
+     */
+    CleanupSessionGUCfiles();
+    
     /*
      * Forcibly remove the files signaling a standby promotion request.
      * Otherwise, the existence of those files triggers a promotion too early,
diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c
index def6c03dd0..6631c8498b 100644
--- a/src/backend/replication/basebackup.c
+++ b/src/backend/replication/basebackup.c
@@ -153,6 +153,9 @@ static const char *excludeDirContents[] =
     /* Contents zeroed on startup, see StartupSUBTRANS(). */
     "pg_subtrans",
 
+    /* Contents removed on startup, see CleanupSessionGUCfiles(). */
+    "pg_session_conf",
+
     /* end of list */
     NULL
 };
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 6ec795f1b4..3b78d6457d 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -681,6 +681,11 @@ standard_ProcessUtility(PlannedStmt *pstmt,
             AlterSystemSetConfigFile((AlterSystemStmt *) parsetree);
             break;
 
+        case T_AlterSessionStmt:
+            PreventInTransactionBlock(isTopLevel, "ALTER SESSION");
+            AlterSessionSetConfigFile((AlterSessionStmt *) parsetree);
+            break;
+
         case T_VariableSetStmt:
             ExecSetVariableStmt((VariableSetStmt *) parsetree, isTopLevel);
             break;
@@ -2605,6 +2610,10 @@ CreateCommandTag(Node *parsetree)
             tag = "ALTER SYSTEM";
             break;
 
+        case T_AlterSessionStmt:
+            tag = "ALTER SESSION";
+            break;
+
         case T_VariableSetStmt:
             switch (((VariableSetStmt *) parsetree)->kind)
             {
@@ -3235,6 +3244,10 @@ GetCommandLogLevel(Node *parsetree)
             lev = LOGSTMT_DDL;
             break;
 
+        case T_AlterSessionStmt:
+            lev = LOGSTMT_DDL;
+            break;
+
         case T_VariableSetStmt:
             lev = LOGSTMT_ALL;
             break;
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index c0b6231458..45c005a109 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -703,6 +703,9 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
      */
     before_shmem_exit(ShutdownPostgres, 0);
 
+    /* Session initialization for GUC system */
+    InitializeSessionGUC();
+
     /* The autovacuum launcher is done here */
     if (IsAutoVacuumLauncherProcess())
     {
diff --git a/src/backend/utils/misc/guc-file.l b/src/backend/utils/misc/guc-file.l
index 1c8b5f7d84..a577ed2def 100644
--- a/src/backend/utils/misc/guc-file.l
+++ b/src/backend/utils/misc/guc-file.l
@@ -208,6 +208,30 @@ ProcessConfigFileInternal(GucContext context, bool applySettings, int elevel)
             ConfFileWithError = PG_AUTOCONF_FILENAME;
             goto bail_out;
         }
+
+        /* load session configuration if any */
+        if (MyBackendId != InvalidBackendId)
+        {
+            static char SessionConfFileName[MAXPGPATH];
+            struct stat st;
+
+            snprintf(SessionConfFileName, sizeof(SessionConfFileName),
+                     PG_SESSIONCONF_FILENAME, MyBackendId);
+
+            /* be silent on absence */
+            if (stat(SessionConfFileName, &st) == 0)
+            {
+                if (!ParseConfigFile(SessionConfFileName, true,
+                                     NULL, 0, 0, elevel,
+                                     &head, &tail))
+                {
+                    /* Syntax error(s) detected in the file, so bail out */
+                    error = true;
+                    ConfFileWithError = SessionConfFileName;
+                    goto bail_out;
+                }
+            }
+        }
     }
     else
     {
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index c216ed0922..4b8fef5b00 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -36,6 +36,7 @@
 #include "catalog/namespace.h"
 #include "catalog/pg_authid.h"
 #include "commands/async.h"
+#include "commands/defrem.h"
 #include "commands/prepare.h"
 #include "commands/user.h"
 #include "commands/vacuum.h"
@@ -72,9 +73,11 @@
 #include "storage/dsm_impl.h"
 #include "storage/standby.h"
 #include "storage/fd.h"
+#include "storage/ipc.h"
 #include "storage/large_object.h"
 #include "storage/pg_shmem.h"
 #include "storage/proc.h"
+#include "storage/procarray.h"
 #include "storage/predicate.h"
 #include "tcop/tcopprot.h"
 #include "tsearch/ts_cache.h"
@@ -4482,7 +4485,8 @@ static void ShowAllGUCConfig(DestReceiver *dest);
 static char *_ShowOption(struct config_generic *record, bool use_units);
 static bool validate_option_array_item(const char *name, const char *value,
                            bool skipIfNoPermissions);
-static void write_auto_conf_file(int fd, const char *filename, ConfigVariable *head_p);
+static void write_auto_conf_file(int fd, const char *filename, bool is_session,
+                                 ConfigVariable *head_p);
 static void replace_auto_config_value(ConfigVariable **head_p, ConfigVariable **tail_p,
                           const char *name, const char *value);
 
@@ -4974,6 +4978,59 @@ guc_name_compare(const char *namea, const char *nameb)
     return 0;
 }
 
+void
+CleanupSessionGUCfiles(void)
+{
+    DIR           *dir;
+    struct dirent *entry;
+    char        fname[MAXPGPATH];
+
+    dir = AllocateDir(PG_SESSIONCONF_DIRNAME);
+    while ((entry = ReadDir(dir, PG_SESSIONCONF_DIRNAME)) != NULL)
+    {
+        if (strcmp(entry->d_name, ".") == 0 ||
+            strcmp(entry->d_name, "..") == 0)
+            continue;
+
+        snprintf(fname, sizeof(fname), "%s/%s", PG_SESSIONCONF_DIRNAME,
+                 entry->d_name);
+
+        /*
+         * no need for durable_unlnk here since this is done again in the next
+         * startup after a crash
+         */
+        if (unlink(fname) != 0)
+            ereport(NOTICE,
+                    (errcode_for_file_access(),
+                     errmsg("could not remove file \"%s\" during clean up of session config files: %m",
+                            fname)));
+    }
+    FreeDir(dir);
+}
+
+static void
+RemoveSessionGUCFile(int code, Datum arg)
+{
+    char SessionConfFileName[MAXPGPATH];
+
+    if (MyBackendId != InvalidBackendId)
+    {
+        snprintf(SessionConfFileName, sizeof(SessionConfFileName),
+                 PG_SESSIONCONF_FILENAME, MyBackendId);
+        unlink(SessionConfFileName);
+    }
+}
+
+void
+InitializeSessionGUC(void)
+{
+    if (!IsUnderPostmaster || MyBackendId == InvalidBackendId)
+        return;
+
+    RemoveSessionGUCFile(0, 0);
+    on_shmem_exit(RemoveSessionGUCFile, 0);
+}
+
 
 /*
  * Initialize GUC options during program startup.
@@ -7470,16 +7527,19 @@ flatten_set_variable_args(const char *name, List *args)
  * values before writing them.
  */
 static void
-write_auto_conf_file(int fd, const char *filename, ConfigVariable *head)
+write_auto_conf_file(int fd, const char *filename, bool is_session,
+                     ConfigVariable *head)
 {
     StringInfoData buf;
     ConfigVariable *item;
+    char *command_name = is_session ? "ALTER SESSION" : "ALTER_SYSTEM";
 
     initStringInfo(&buf);
 
     /* Emit file header containing warning comment */
     appendStringInfoString(&buf, "# Do not edit this file manually!\n");
-    appendStringInfoString(&buf, "# It will be overwritten by the ALTER SYSTEM command.\n");
+    appendStringInfo(&buf, "# It will be overwritten by the %s command.\n",
+        command_name);
 
     errno = 0;
     if (write(fd, buf.data, buf.len) != buf.len)
@@ -7599,103 +7659,101 @@ replace_auto_config_value(ConfigVariable **head_p, ConfigVariable **tail_p,
 
 
 /*
- * Execute ALTER SYSTEM statement.
- *
- * Read the old PG_AUTOCONF_FILENAME file, merge in the new variable value,
- * and write out an updated file.  If the command is ALTER SYSTEM RESET ALL,
- * we can skip reading the old file and just write an empty file.
- *
- * An LWLock is used to serialize updates of the configuration file.
- *
- * In case of an error, we leave the original automatic
- * configuration file (PG_AUTOCONF_FILENAME) intact.
+  Check and process the given set statment of ALTER SYSTEM and ALTER SESSION.
  */
-void
-AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
+static void
+AlterConfigProcessSetCommand(VariableSetStmt *setstmt,
+                             bool is_session, bool is_superuser,
+                             char **name, char **value, bool *resetall)
 {
-    char       *name;
-    char       *value;
-    bool        resetall = false;
-    ConfigVariable *head = NULL;
-    ConfigVariable *tail = NULL;
-    volatile int Tmpfd;
-    char        AutoConfFileName[MAXPGPATH];
-    char        AutoConfTmpFileName[MAXPGPATH];
+    bool disallow_by_context = false;
 
-    if (!superuser())
-        ereport(ERROR,
-                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-                 (errmsg("must be superuser to execute ALTER SYSTEM command"))));
+    *name = setstmt->name;
 
-    /*
-     * Extract statement arguments
-     */
-    name = altersysstmt->setstmt->name;
-
-    switch (altersysstmt->setstmt->kind)
+    switch (setstmt->kind)
     {
         case VAR_SET_VALUE:
-            value = ExtractSetVariableArgs(altersysstmt->setstmt);
+            *value = ExtractSetVariableArgs(setstmt);
             break;
 
         case VAR_SET_DEFAULT:
         case VAR_RESET:
-            value = NULL;
+            *value = NULL;
             break;
 
         case VAR_RESET_ALL:
-            value = NULL;
-            resetall = true;
+            *value = NULL;
+            *resetall = true;
             break;
 
         default:
-            elog(ERROR, "unrecognized alter system stmt type: %d",
-                 altersysstmt->setstmt->kind);
+            elog(ERROR, "unrecognized set type: %d",
+                 setstmt->kind);
             break;
     }
 
     /*
      * Unless it's RESET_ALL, validate the target variable and value
      */
-    if (!resetall)
+    if (!*resetall)
     {
         struct config_generic *record;
 
-        record = find_option(name, false, ERROR);
+        record = find_option(*name, false, ERROR);
         if (record == NULL)
             ereport(ERROR,
                     (errcode(ERRCODE_UNDEFINED_OBJECT),
                      errmsg("unrecognized configuration parameter \"%s\"",
-                            name)));
+                            *name)));
 
         /*
          * Don't allow parameters that can't be set in configuration files to
          * be set in PG_AUTOCONF_FILENAME file.
          */
-        if ((record->context == PGC_INTERNAL) ||
+
+        if (record->context == PGC_INTERNAL)
+            disallow_by_context = true;
+        else if (is_session)
+        {
+            /* parameters that cannot be changed on-session */
+            if (record->context == PGC_SIGHUP ||
+                record->context == PGC_POSTMASTER ||
+                record->context == PGC_SU_BACKEND ||
+                record->context == PGC_BACKEND)
+                disallow_by_context = true;
+
+            /* parameters that can be changed only by superuser */
+            if (!is_superuser && record->context == PGC_SUSET)
+                ereport(ERROR,
+                        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                         errmsg("permission denied to set parameter \"%s\"",
+                                *name)));
+        }
+
+        if (disallow_by_context ||
             (record->flags & GUC_DISALLOW_IN_FILE) ||
             (record->flags & GUC_DISALLOW_IN_AUTO_FILE))
             ereport(ERROR,
                     (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
                      errmsg("parameter \"%s\" cannot be changed",
-                            name)));
+                            *name)));
 
         /*
          * If a value is specified, verify that it's sane.
          */
-        if (value)
+        if (*value)
         {
             union config_var_val newval;
             void       *newextra = NULL;
 
             /* Check that it's acceptable for the indicated parameter */
-            if (!parse_and_validate_value(record, name, value,
+            if (!parse_and_validate_value(record, *name, *value,
                                           PGC_S_FILE, ERROR,
                                           &newval, &newextra))
                 ereport(ERROR,
                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                          errmsg("invalid value for parameter \"%s\": \"%s\"",
-                                name, value)));
+                                *name, *value)));
 
             if (record->vartype == PGC_STRING && newval.stringval != NULL)
                 free(newval.stringval);
@@ -7707,22 +7765,25 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
              * grammar for config files doesn't support embedded newlines in
              * string literals.
              */
-            if (strchr(value, '\n'))
+            if (strchr(*value, '\n'))
                 ereport(ERROR,
                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                         errmsg("parameter value for ALTER SYSTEM must not contain a newline")));
+                         errmsg("parameter value for SET must not contain a newline")));
         }
     }
+}
 
-    /*
-     * PG_AUTOCONF_FILENAME and its corresponding temporary file are always in
-     * the data directory, so we can reference them by simple relative paths.
-     */
-    snprintf(AutoConfFileName, sizeof(AutoConfFileName), "%s",
-             PG_AUTOCONF_FILENAME);
-    snprintf(AutoConfTmpFileName, sizeof(AutoConfTmpFileName), "%s.%s",
-             AutoConfFileName,
-             "tmp");
+/*
+ * Edit or create auto configuration files.
+ */
+static void
+EditAutoConfigFile(char *confname, char *tmpname, bool is_session,
+                   char *name, char *value, bool resetall)
+{
+    struct stat st;
+    volatile int Tmpfd;
+    ConfigVariable *head = NULL;
+    ConfigVariable *tail = NULL;
 
     /*
      * Only one backend is allowed to operate on PG_AUTOCONF_FILENAME at a
@@ -7733,30 +7794,43 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
 
     /*
      * If we're going to reset everything, then no need to open or parse the
-     * old file.  We'll just write out an empty list.
+     * old file.
+     */
+    if (is_session && resetall)
+    {
+        /* RESET ALL just remove files for session configuratrion */
+        if (stat(confname, &st) == 0)
+            durable_unlink(confname, ERROR);
+
+        unlink(tmpname);    /* ignore error */
+        LWLockRelease(AutoFileLock);
+        return;
+    }
+
+    /*
+     * We'll just write out an empty list for auto configration for RESET
+     * ALL
      */
     if (!resetall)
     {
-        struct stat st;
-
-        if (stat(AutoConfFileName, &st) == 0)
+        if (stat(confname, &st) == 0)
         {
-            /* open old file PG_AUTOCONF_FILENAME */
+            /* open old auto config file  */
             FILE       *infile;
 
-            infile = AllocateFile(AutoConfFileName, "r");
+            infile = AllocateFile(confname, "r");
             if (infile == NULL)
                 ereport(ERROR,
                         (errcode_for_file_access(),
                          errmsg("could not open file \"%s\": %m",
-                                AutoConfFileName)));
+                                confname)));
 
             /* parse it */
-            if (!ParseConfigFp(infile, AutoConfFileName, 0, LOG, &head, &tail))
+            if (!ParseConfigFp(infile, confname, 0, LOG, &head, &tail))
                 ereport(ERROR,
                         (errcode(ERRCODE_CONFIG_FILE_ERROR),
                          errmsg("could not parse contents of file \"%s\"",
-                                AutoConfFileName)));
+                                confname)));
 
             FreeFile(infile);
         }
@@ -7775,13 +7849,13 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
      * If there is a temp file left over due to a previous crash, it's okay to
      * truncate and reuse it.
      */
-    Tmpfd = BasicOpenFile(AutoConfTmpFileName,
+    Tmpfd = BasicOpenFile(tmpname,
                           O_CREAT | O_RDWR | O_TRUNC);
     if (Tmpfd < 0)
         ereport(ERROR,
                 (errcode_for_file_access(),
                  errmsg("could not open file \"%s\": %m",
-                        AutoConfTmpFileName)));
+                        tmpname)));
 
     /*
      * Use a TRY block to clean up the file if we fail.  Since we need a TRY
@@ -7790,7 +7864,7 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
     PG_TRY();
     {
         /* Write and sync the new contents to the temporary file */
-        write_auto_conf_file(Tmpfd, AutoConfTmpFileName, head);
+        write_auto_conf_file(Tmpfd, tmpname, is_session, head);
 
         /* Close before renaming; may be required on some platforms */
         close(Tmpfd);
@@ -7801,7 +7875,7 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
          * at worst it can lose the parameters set by last ALTER SYSTEM
          * command.
          */
-        durable_rename(AutoConfTmpFileName, AutoConfFileName, ERROR);
+        durable_rename(tmpname, confname, ERROR);
     }
     PG_CATCH();
     {
@@ -7810,7 +7884,7 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
             close(Tmpfd);
 
         /* Unlink, but ignore any error */
-        (void) unlink(AutoConfTmpFileName);
+        (void) unlink(tmpname);
 
         PG_RE_THROW();
     }
@@ -7821,6 +7895,122 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
     LWLockRelease(AutoFileLock);
 }
 
+/*
+ * Execute ALTER SYSTEM statement.
+ *
+ * Read the old PG_AUTOCONF_FILENAME file, merge in the new variable value,
+ * and write out an updated file.  If the command is ALTER SYSTEM RESET ALL,
+ * we can skip reading the old file and just write an empty file.
+ *
+ * An LWLock is used to serialize updates of the configuration file.
+ *
+ * In case of an error, we leave the original automatic
+ * configuration file (PG_AUTOCONF_FILENAME) intact.
+ */
+void
+AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
+{
+    char       *name;
+    char       *value;
+    bool        resetall = false;
+    char        AutoConfFileName[MAXPGPATH];
+    char        AutoConfTmpFileName[MAXPGPATH];
+
+    if (!superuser())
+        ereport(ERROR,
+                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                 (errmsg("must be superuser to execute ALTER SYSTEM command"))));
+
+    /*
+     * Extract statement arguments
+     */
+
+    AlterConfigProcessSetCommand(altersysstmt->setstmt, false, false,
+                                 &name, &value, &resetall);
+    /*
+     * PG_AUTOCONF_FILENAME and its corresponding temporary file are always in
+     * the data directory, so we can reference them by simple relative paths.
+     */
+    snprintf(AutoConfFileName, sizeof(AutoConfFileName), "%s",
+             PG_AUTOCONF_FILENAME);
+    snprintf(AutoConfTmpFileName, sizeof(AutoConfTmpFileName), "%s.%s",
+             AutoConfFileName,
+             "tmp");
+
+    EditAutoConfigFile(AutoConfFileName, AutoConfTmpFileName, false,
+                       name, value, resetall);
+}
+
+/*
+ * Execute ALTER SESSION statement.
+ *
+ * Peforms the similar thing with ALTER SYSTEM to PG_SESSIONCONF_FILENAME.
+ */
+void
+AlterSessionSetConfigFile(AlterSessionStmt *altersesstmt)
+{
+    int            target_pid;
+    PGPROC       *target_proc;
+    DefElem       *def;
+    char       *name;
+    char       *value;
+    bool        resetall = false;
+    char        SessionConfigFile[MAXPGPATH];
+    char        SessionConfigTmpFile[MAXPGPATH];
+
+    def = altersesstmt->sessionopt;
+
+    /* ignoring namespace */
+    if (strcmp(def->defname, "pid") != 0)
+        ereport(ERROR,
+                (errmsg ("only pid is allowed here")));
+    target_pid = defGetInt32(def);
+
+    target_proc = BackendPidGetProc(target_pid);
+
+    if (target_proc == NULL)
+        ereport(ERROR,
+                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                 errmsg("PID %d is not a PostgreSQL server process",
+                        target_pid)));
+
+    /* The same condition to pg_signal_backend() */
+    if ((superuser_arg(target_proc->roleId) && !superuser()) ||
+        (!has_privs_of_role(GetUserId(), target_proc->roleId) &&
+         !has_privs_of_role(GetUserId(), DEFAULT_ROLE_SIGNAL_BACKENDID)))
+        ereport(ERROR,
+                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                 errmsg("insufficient privileges for the session")));
+
+    if (target_proc->backendId == InvalidBackendId)
+        elog(ERROR, "not a backend");
+
+    AlterConfigProcessSetCommand(altersesstmt->setstmt, true, superuser(),
+                                 &name, &value, &resetall);
+
+    /* don't allow RESET ALL for non-superusers */
+    if (resetall && !superuser())
+        ereport(ERROR,
+                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                 errmsg("RESET ALL requres superuser privileges")));
+
+    /* Ok, edit or create session config file */
+    snprintf(SessionConfigFile, sizeof(SessionConfigFile),
+             PG_SESSIONCONF_FILENAME, target_proc->backendId);
+    snprintf(SessionConfigTmpFile, sizeof(SessionConfigTmpFile), "%s.%s",
+             SessionConfigFile, "tmp");
+
+    EditAutoConfigFile(SessionConfigFile, SessionConfigTmpFile, true,
+                       name, value, resetall);
+
+    if (altersesstmt->immediate)
+    {
+        if (kill(target_pid, SIGHUP))
+            ereport(WARNING,
+                    (errmsg("failed to send signal to session: %m")));
+    }
+}
+
 /*
  * SET command
  */
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index fd50a809ea..138190e9dc 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -221,7 +221,8 @@ static const char *const subdirs[] = {
     "pg_xact",
     "pg_logical",
     "pg_logical/snapshots",
-    "pg_logical/mappings"
+    "pg_logical/mappings",
+    "pg_session_conf"
 };
 
 
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 10dac60cd3..48ed5ab753 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -407,6 +407,7 @@ typedef enum NodeTag
     T_RefreshMatViewStmt,
     T_ReplicaIdentityStmt,
     T_AlterSystemStmt,
+    T_AlterSessionStmt,
     T_CreatePolicyStmt,
     T_AlterPolicyStmt,
     T_CreateTransformStmt,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index addc2c2ec7..251a52b0cc 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -3118,6 +3118,18 @@ typedef struct AlterSystemStmt
     VariableSetStmt *setstmt;    /* SET subcommand */
 } AlterSystemStmt;
 
+/* ----------------------
+ *        Alter Session Statement
+ * ----------------------
+ */
+typedef struct AlterSessionStmt
+{
+    NodeTag        type;
+    DefElem       *sessionopt;        /* session property */
+    VariableSetStmt *setstmt;    /* SET subcommand */
+    bool        immediate;        /* reload immediately */
+} AlterSessionStmt;
+
 /* ----------------------
  *        Cluster Statement (support pbrown's cluster index implementation)
  * ----------------------
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index c07e7b945e..d8c779752b 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -33,6 +33,14 @@
  */
 #define PG_AUTOCONF_FILENAME        "postgresql.auto.conf"
 
+/*
+ * Session configuration file name for ALTER SESSION.
+ * This file will be used to store values of configuration parameters
+ * set by ALTER SESSION command.
+ */
+#define PG_SESSIONCONF_DIRNAME    "pg_session_conf"
+#define PG_SESSIONCONF_FILENAME    PG_SESSIONCONF_DIRNAME"/postgresql.%d.conf"
+
 /*
  * Certain options can only be set at certain times. The rules are
  * like this:
@@ -351,6 +359,8 @@ extern const char *GetConfigOption(const char *name, bool missing_ok,
 extern const char *GetConfigOptionResetString(const char *name);
 extern int    GetConfigOptionFlags(const char *name, bool missing_ok);
 extern void ProcessConfigFile(GucContext context);
+extern void CleanupSessionGUCfiles(void);
+extern void InitializeSessionGUC(void);
 extern void InitializeGUCOptions(void);
 extern bool SelectConfigFiles(const char *userDoption, const char *progname);
 extern void ResetAllOptions(void);
@@ -367,6 +377,7 @@ extern int set_config_option(const char *name, const char *value,
                   GucAction action, bool changeVal, int elevel,
                   bool is_reload);
 extern void AlterSystemSetConfigFile(AlterSystemStmt *setstmt);
+extern void AlterSessionSetConfigFile(AlterSessionStmt *altersesstmt);
 extern char *GetConfigOptionByName(const char *name, const char **varname,
                       bool missing_ok);
 extern void GetConfigOptionByNum(int varnum, const char **values, bool *noshow);
-- 
2.16.3


Re: ALTER SESSION

From
Kyotaro HORIGUCHI
Date:
At Tue, 29 Jan 2019 20:32:54 +0900 (Tokyo Standard Time), Kyotaro HORIGUCHI <horiguchi.kyotaro@lab.ntt.co.jp> wrote in
<20190129.203254.115361483.horiguchi.kyotaro@lab.ntt.co.jp>
> Hello.
> 
> https://www.postgresql.org/message-id/20190128.133143.115303042.horiguchi.kyotaro@lab.ntt.co.jp
> 
> > C. Provide session-specific GUC variable (that overides the global one)
> >    - Add new configuration file "postgresql.conf.<PID>" and
> >      pg_reload_conf() let the session with the PID loads it as if
> >      it is the last included file. All such files are removed at
> >      startup or at the end of the coressponding session.
> > 
> >    - Add a new syntax like this:
> >      ALTER SESSION WITH (pid=xxxx)
> >         SET configuration_parameter {TO | =} {value | 'value' | DEFAULT}
> >         RESET configuration_parameter
> >         RESET ALL
> > 
> >    - Target variables are marked with GUC_REMOTE.
> > 
> > I'll consider the last choice and will come up with a patch.
> 
> This is that, with a small change in design.
> 
> ALTER SESSION WITH (pid <pid>) SET param {TO|=} value [ IMMEDIATE ]
> ALTER SESSION WITH (pid <pid>) RESET param [ IMMEDIATE ]
> ALTER SESSION WITH (pid <pid>) RESET ALL
> 
> The first form create an entry in
> $PGDATA/pg_session_conf/postgresql.<beid>.conf.
> The second form removes the entry.
> The third form removes the file itself.
> 
> IMMEDIATE specifies that the change is applied immediately by
> sending SIGHUP to the process. pg_reload_conf() works as well.
> 
> The session configuration is removed at session-end and the
> directory is cleaned up at startup.
> 
> It can change varaibles of PGC_USERSET by non-superuser or
> PGC_SUSET by superuser. The local session user should have the
> same privileges with pg_signal_backend() on the target session.
> 
> This patch contains documentation but doesn't contain test yet.
> 
> I would appreciate any comments or suggestions on this.

Minor updates and rebased.

regards.

-- 
Kyotaro Horiguchi
NTT Open Source Software Center
From f4cbeae278a31ea91b4c82f3e8fa8cd4d45ad580 Mon Sep 17 00:00:00 2001
From: Kyotaro Horiguchi <horiguchi.kyotaro@lab.ntt.co.jp>
Date: Tue, 29 Jan 2019 17:03:57 +0900
Subject: [PATCH] ALTER SESSION

Some tracking features are controled by GUC so they cannot be
activated from another session. The ALTER SESSION command allows
sessions to change some of GUC settings of another session.
---
 doc/src/sgml/config.sgml             |  26 ++-
 doc/src/sgml/ref/allfiles.sgml       |   1 +
 doc/src/sgml/reference.sgml          |   1 +
 src/backend/nodes/copyfuncs.c        |  14 ++
 src/backend/nodes/equalfuncs.c       |  12 ++
 src/backend/parser/gram.y            |  47 ++++-
 src/backend/postmaster/postmaster.c  |   6 +
 src/backend/replication/basebackup.c |   3 +
 src/backend/tcop/utility.c           |  13 ++
 src/backend/utils/init/postinit.c    |   3 +
 src/backend/utils/misc/guc-file.l    |  23 +++
 src/backend/utils/misc/guc.c         | 323 +++++++++++++++++++++++++++--------
 src/bin/initdb/initdb.c              |   3 +-
 src/include/nodes/nodes.h            |   1 +
 src/include/nodes/parsenodes.h       |  12 ++
 src/include/utils/guc.h              |  13 ++
 16 files changed, 422 insertions(+), 79 deletions(-)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index b6f5822b84..a52989342a 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -175,6 +175,14 @@ shared_buffers = 128MB
      read whenever <filename>postgresql.conf</filename> is, and its settings take
      effect in the same way.  Settings in <filename>postgresql.auto.conf</filename>
      override those in <filename>postgresql.conf</filename>.
+
+    </para>
+    <para>
+     Furthermore a directory <filename>pg_session_conf</filename> in the
+     PostgreSQL data directory contains per-session configuration files. Every
+     file holds settings provided through the
+     <xref linkend="sql-altersession"/> command. This is read by reloading
+     after the two above files and removed at the session-end.
     </para>
 
     <para>
@@ -195,8 +203,8 @@ shared_buffers = 128MB
       The already-mentioned <xref linkend="sql-altersystem"/> command
       provides a SQL-accessible means of changing global defaults; it is
       functionally equivalent to editing <filename>postgresql.conf</filename>.
-      In addition, there are two commands that allow setting of defaults
-      on a per-database or per-role basis:
+      In addition, there are three commands that allow setting of defaults
+      on a per-database, per-role or per-session basis:
      </para>
 
      <itemizedlist>
@@ -213,6 +221,13 @@ shared_buffers = 128MB
        per-database settings to be overridden with user-specific values.
       </para>
      </listitem>
+
+     <listitem>
+      <para>
+       The <xref linkend="sql-altersession"/> command allows other sessions to
+        override session-local settings with user-specific values.
+      </para>
+     </listitem>
     </itemizedlist>
 
      <para>
@@ -223,6 +238,13 @@ shared_buffers = 128MB
       Note that some settings cannot be changed after server start, and
       so cannot be set with these commands (or the ones listed below).
     </para>
+     <para>
+      Values set with <command>ALTER SESSION</command> are applied only when
+      reloading.  They override values obtained from the configuration files
+      or server command line, and constitute defaults for the rest of the
+      session.  Note that it can change only settings that are changeable
+      on-session.
+    </para>
 
      <para>
       Once a client is connected to the database, <productname>PostgreSQL</productname>
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index c81c87ef41..650ae49a24 100644
--- a/doc/src/sgml/ref/allfiles.sgml
+++ b/doc/src/sgml/ref/allfiles.sgml
@@ -34,6 +34,7 @@ Complete list of usable sgml source files in this directory.
 <!ENTITY alterSchema        SYSTEM "alter_schema.sgml">
 <!ENTITY alterServer        SYSTEM "alter_server.sgml">
 <!ENTITY alterSequence      SYSTEM "alter_sequence.sgml">
+<!ENTITY alterSession       SYSTEM "alter_session.sgml">
 <!ENTITY alterSubscription  SYSTEM "alter_subscription.sgml">
 <!ENTITY alterSystem        SYSTEM "alter_system.sgml">
 <!ENTITY alterStatistics    SYSTEM "alter_statistics.sgml">
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
index db4f4167e3..85f515e2b2 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -62,6 +62,7 @@
    &alterSchema;
    &alterSequence;
    &alterServer;
+   &alterSession;
    &alterStatistics;
    &alterSubscription;
    &alterSystem;
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 807393dfaa..7719b475ae 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3923,6 +3923,17 @@ _copyAlterSystemStmt(const AlterSystemStmt *from)
     return newnode;
 }
 
+static AlterSessionStmt *
+_copyAlterSessionStmt(const AlterSessionStmt *from)
+{
+    AlterSessionStmt *newnode = makeNode(AlterSessionStmt);
+
+    COPY_NODE_FIELD(sessionopt);
+    COPY_NODE_FIELD(setstmt);
+    COPY_SCALAR_FIELD(immediate);
+    return newnode;
+}
+
 static CreateSeqStmt *
 _copyCreateSeqStmt(const CreateSeqStmt *from)
 {
@@ -5341,6 +5352,9 @@ copyObjectImpl(const void *from)
         case T_AlterSystemStmt:
             retval = _copyAlterSystemStmt(from);
             break;
+        case T_AlterSessionStmt:
+            retval = _copyAlterSessionStmt(from);
+            break;
         case T_CreateSeqStmt:
             retval = _copyCreateSeqStmt(from);
             break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index a397de155e..b542d060f8 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1732,6 +1732,15 @@ _equalAlterSystemStmt(const AlterSystemStmt *a, const AlterSystemStmt *b)
     return true;
 }
 
+static bool
+_equalAlterSessionStmt(const AlterSessionStmt *a, const AlterSessionStmt *b)
+{
+    COMPARE_NODE_FIELD(sessionopt);
+    COMPARE_NODE_FIELD(setstmt);
+    COMPARE_SCALAR_FIELD(immediate);
+
+    return true;
+}
 
 static bool
 _equalCreateSeqStmt(const CreateSeqStmt *a, const CreateSeqStmt *b)
@@ -3406,6 +3415,9 @@ equal(const void *a, const void *b)
         case T_AlterSystemStmt:
             retval = _equalAlterSystemStmt(a, b);
             break;
+        case T_AlterSessionStmt:
+            retval = _equalAlterSessionStmt(a, b);
+            break;
         case T_CreateSeqStmt:
             retval = _equalCreateSeqStmt(a, b);
             break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index c1faf4152c..33d4922bfb 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -248,8 +248,9 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
         AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterEnumStmt
         AlterFdwStmt AlterForeignServerStmt AlterGroupStmt
         AlterObjectDependsStmt AlterObjectSchemaStmt AlterOwnerStmt
-        AlterOperatorStmt AlterSeqStmt AlterSystemStmt AlterTableStmt
-        AlterTblSpcStmt AlterExtensionStmt AlterExtensionContentsStmt AlterForeignTableStmt
+        AlterOperatorStmt AlterSeqStmt AlterSystemStmt AlterSessionStmt
+        AlterTableStmt AlterTblSpcStmt AlterExtensionStmt
+        AlterExtensionContentsStmt AlterForeignTableStmt
         AlterCompositeTypeStmt AlterUserMappingStmt
         AlterRoleStmt AlterRoleSetStmt AlterPolicyStmt
         AlterDefaultPrivilegesStmt DefACLAction
@@ -303,13 +304,14 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 %type <defelt>    createdb_opt_item copy_opt_item
                 transaction_mode_item
                 create_extension_opt_item alter_extension_opt_item
+                altersess_option_item
 
 %type <ival>    opt_lock lock_type cast_context
 %type <ival>    vacuum_option_list vacuum_option_elem
                 analyze_option_list analyze_option_elem
 %type <boolean>    opt_or_replace
                 opt_grant_grant_option opt_grant_admin_option
-                opt_nowait opt_if_exists opt_with_data
+                opt_nowait opt_if_exists opt_with_data opt_immediate
 %type <ival>    opt_nowait_or_skip
 
 %type <list>    OptRoleList AlterOptRoleList
@@ -839,6 +841,7 @@ stmt :
             | AlterPolicyStmt
             | AlterSeqStmt
             | AlterSystemStmt
+            | AlterSessionStmt
             | AlterTableStmt
             | AlterTblSpcStmt
             | AlterCompositeTypeStmt
@@ -10169,6 +10172,44 @@ AlterSystemStmt:
         ;
 
 
+/*****************************************************************************
+ *
+ *        ALTER SESSION
+ *
+ * This is used to change configuration parameters persistently.
+ *****************************************************************************/
+
+AlterSessionStmt:
+            ALTER SESSION WITH '(' altersess_option_item ')' SET generic_set opt_immediate
+                {
+                    AlterSessionStmt *n = makeNode(AlterSessionStmt);
+                    n->sessionopt = $5;
+                    n->setstmt = $8;
+                    n->immediate = $9;
+                    $$ = (Node *)n;
+                }
+            | ALTER SESSION WITH '(' altersess_option_item ')' RESET generic_reset opt_immediate
+                {
+                    AlterSessionStmt *n = makeNode(AlterSessionStmt);
+                    n->sessionopt = $5;
+                    n->setstmt = $8;
+                    n->immediate = $9;
+                    $$ = (Node *)n;
+                }
+        ;
+
+altersess_option_item:
+        ColLabel Iconst
+        {
+            $$ = makeDefElem($1, (Node *)makeInteger($2), @1);
+        }
+    ;
+
+opt_immediate:
+        IMMEDIATE    { $$ = true; }
+        | /*EMPTY*/ { $$ = false; }
+    ;
+
 /*****************************************************************************
  *
  * Manipulate a domain
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 3052bbbc21..1a32bb5f52 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -125,6 +125,7 @@
 #include "tcop/tcopprot.h"
 #include "utils/builtins.h"
 #include "utils/datetime.h"
+#include "utils/guc.h"
 #include "utils/memutils.h"
 #include "utils/pidfile.h"
 #include "utils/ps_status.h"
@@ -1242,6 +1243,11 @@ PostmasterMain(int argc, char *argv[])
      */
     RemovePgTempFiles();
 
+    /*
+     * Remove old session configuration files.
+     */
+    CleanupSessionGUCfiles();
+    
     /*
      * Forcibly remove the files signaling a standby promotion request.
      * Otherwise, the existence of those files triggers a promotion too early,
diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c
index def6c03dd0..6631c8498b 100644
--- a/src/backend/replication/basebackup.c
+++ b/src/backend/replication/basebackup.c
@@ -153,6 +153,9 @@ static const char *excludeDirContents[] =
     /* Contents zeroed on startup, see StartupSUBTRANS(). */
     "pg_subtrans",
 
+    /* Contents removed on startup, see CleanupSessionGUCfiles(). */
+    "pg_session_conf",
+
     /* end of list */
     NULL
 };
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 6ec795f1b4..daa3af725c 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -681,6 +681,11 @@ standard_ProcessUtility(PlannedStmt *pstmt,
             AlterSystemSetConfigFile((AlterSystemStmt *) parsetree);
             break;
 
+        case T_AlterSessionStmt:
+            PreventInTransactionBlock(isTopLevel, "ALTER SESSION");
+            AlterSessionSetConfigFile(pstate, (AlterSessionStmt *) parsetree);
+            break;
+
         case T_VariableSetStmt:
             ExecSetVariableStmt((VariableSetStmt *) parsetree, isTopLevel);
             break;
@@ -2605,6 +2610,10 @@ CreateCommandTag(Node *parsetree)
             tag = "ALTER SYSTEM";
             break;
 
+        case T_AlterSessionStmt:
+            tag = "ALTER SESSION";
+            break;
+
         case T_VariableSetStmt:
             switch (((VariableSetStmt *) parsetree)->kind)
             {
@@ -3235,6 +3244,10 @@ GetCommandLogLevel(Node *parsetree)
             lev = LOGSTMT_DDL;
             break;
 
+        case T_AlterSessionStmt:
+            lev = LOGSTMT_DDL;
+            break;
+
         case T_VariableSetStmt:
             lev = LOGSTMT_ALL;
             break;
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index c0b6231458..45c005a109 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -703,6 +703,9 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
      */
     before_shmem_exit(ShutdownPostgres, 0);
 
+    /* Session initialization for GUC system */
+    InitializeSessionGUC();
+
     /* The autovacuum launcher is done here */
     if (IsAutoVacuumLauncherProcess())
     {
diff --git a/src/backend/utils/misc/guc-file.l b/src/backend/utils/misc/guc-file.l
index 1c8b5f7d84..dbb131613b 100644
--- a/src/backend/utils/misc/guc-file.l
+++ b/src/backend/utils/misc/guc-file.l
@@ -208,6 +208,29 @@ ProcessConfigFileInternal(GucContext context, bool applySettings, int elevel)
             ConfFileWithError = PG_AUTOCONF_FILENAME;
             goto bail_out;
         }
+
+        /* load session configuration if any */
+        if (MyBackendId != InvalidBackendId)
+        {
+            /* Use this name for error reporting */
+            static char SessionConfFileName[MAXPGPATH];
+            struct stat st;
+
+            snprintf(SessionConfFileName, sizeof(SessionConfFileName),
+                     PG_SESSIONCONF_FILENAME, MyBackendId);
+
+            /* be silent on absence */
+            if (stat(SessionConfFileName, &st) == 0 &&
+                !ParseConfigFile(SessionConfFileName, true,
+                                 NULL, 0, 0, elevel,
+                                 &head, &tail))
+            {
+                /* Syntax error(s) detected in the file, so bail out */
+                error = true;
+                ConfFileWithError = SessionConfFileName;
+                goto bail_out;
+            }
+        }
     }
     else
     {
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 98d75be292..45eec19392 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -36,6 +36,7 @@
 #include "catalog/namespace.h"
 #include "catalog/pg_authid.h"
 #include "commands/async.h"
+#include "commands/defrem.h"
 #include "commands/prepare.h"
 #include "commands/user.h"
 #include "commands/vacuum.h"
@@ -73,9 +74,11 @@
 #include "storage/dsm_impl.h"
 #include "storage/standby.h"
 #include "storage/fd.h"
+#include "storage/ipc.h"
 #include "storage/large_object.h"
 #include "storage/pg_shmem.h"
 #include "storage/proc.h"
+#include "storage/procarray.h"
 #include "storage/predicate.h"
 #include "tcop/tcopprot.h"
 #include "tsearch/ts_cache.h"
@@ -4483,7 +4486,8 @@ static void ShowAllGUCConfig(DestReceiver *dest);
 static char *_ShowOption(struct config_generic *record, bool use_units);
 static bool validate_option_array_item(const char *name, const char *value,
                            bool skipIfNoPermissions);
-static void write_auto_conf_file(int fd, const char *filename, ConfigVariable *head_p);
+static void write_auto_conf_file(int fd, const char *filename, bool is_session,
+                                 ConfigVariable *head_p);
 static void replace_auto_config_value(ConfigVariable **head_p, ConfigVariable **tail_p,
                           const char *name, const char *value);
 
@@ -4975,6 +4979,59 @@ guc_name_compare(const char *namea, const char *nameb)
     return 0;
 }
 
+void
+CleanupSessionGUCfiles(void)
+{
+    DIR           *dir;
+    struct dirent *entry;
+    char        fname[MAXPGPATH];
+
+    dir = AllocateDir(PG_SESSIONCONF_DIRNAME);
+    while ((entry = ReadDir(dir, PG_SESSIONCONF_DIRNAME)) != NULL)
+    {
+        if (strcmp(entry->d_name, ".") == 0 ||
+            strcmp(entry->d_name, "..") == 0)
+            continue;
+
+        snprintf(fname, sizeof(fname), "%s/%s", PG_SESSIONCONF_DIRNAME,
+                 entry->d_name);
+
+        /*
+         * no need for durable_unlnk here since this is done again in the next
+         * startup after a crash
+         */
+        if (unlink(fname) != 0)
+            ereport(NOTICE,
+                    (errcode_for_file_access(),
+                     errmsg("could not remove file \"%s\" during clean up of session config files: %m",
+                            fname)));
+    }
+    FreeDir(dir);
+}
+
+static void
+RemoveSessionGUCFile(int code, Datum arg)
+{
+    char SessionConfFileName[MAXPGPATH];
+
+    if (MyBackendId != InvalidBackendId)
+    {
+        snprintf(SessionConfFileName, sizeof(SessionConfFileName),
+                 PG_SESSIONCONF_FILENAME, MyBackendId);
+        unlink(SessionConfFileName);
+    }
+}
+
+void
+InitializeSessionGUC(void)
+{
+    if (!IsUnderPostmaster || MyBackendId == InvalidBackendId)
+        return;
+
+    RemoveSessionGUCFile(0, 0);
+    on_shmem_exit(RemoveSessionGUCFile, 0);
+}
+
 
 /*
  * Initialize GUC options during program startup.
@@ -7471,16 +7528,19 @@ flatten_set_variable_args(const char *name, List *args)
  * values before writing them.
  */
 static void
-write_auto_conf_file(int fd, const char *filename, ConfigVariable *head)
+write_auto_conf_file(int fd, const char *filename, bool is_session,
+                     ConfigVariable *head)
 {
     StringInfoData buf;
     ConfigVariable *item;
+    char *command_name = is_session ? "ALTER SESSION" : "ALTER_SYSTEM";
 
     initStringInfo(&buf);
 
     /* Emit file header containing warning comment */
     appendStringInfoString(&buf, "# Do not edit this file manually!\n");
-    appendStringInfoString(&buf, "# It will be overwritten by the ALTER SYSTEM command.\n");
+    appendStringInfo(&buf, "# It will be overwritten by the %s command.\n",
+        command_name);
 
     errno = 0;
     if (write(fd, buf.data, buf.len) != buf.len)
@@ -7600,103 +7660,91 @@ replace_auto_config_value(ConfigVariable **head_p, ConfigVariable **tail_p,
 
 
 /*
- * Execute ALTER SYSTEM statement.
- *
- * Read the old PG_AUTOCONF_FILENAME file, merge in the new variable value,
- * and write out an updated file.  If the command is ALTER SYSTEM RESET ALL,
- * we can skip reading the old file and just write an empty file.
- *
- * An LWLock is used to serialize updates of the configuration file.
- *
- * In case of an error, we leave the original automatic
- * configuration file (PG_AUTOCONF_FILENAME) intact.
+  Check and process the given set statment of ALTER SYSTEM and ALTER SESSION.
  */
-void
-AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
+static void
+AlterConfigProcessSetCommand(VariableSetStmt *setstmt,
+                             bool is_session, bool is_superuser,
+                             char **name, char **value, bool *resetall)
 {
-    char       *name;
-    char       *value;
-    bool        resetall = false;
-    ConfigVariable *head = NULL;
-    ConfigVariable *tail = NULL;
-    volatile int Tmpfd;
-    char        AutoConfFileName[MAXPGPATH];
-    char        AutoConfTmpFileName[MAXPGPATH];
+    *name = setstmt->name;
 
-    if (!superuser())
-        ereport(ERROR,
-                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-                 (errmsg("must be superuser to execute ALTER SYSTEM command"))));
-
-    /*
-     * Extract statement arguments
-     */
-    name = altersysstmt->setstmt->name;
-
-    switch (altersysstmt->setstmt->kind)
+    switch (setstmt->kind)
     {
         case VAR_SET_VALUE:
-            value = ExtractSetVariableArgs(altersysstmt->setstmt);
+            *value = ExtractSetVariableArgs(setstmt);
             break;
 
         case VAR_SET_DEFAULT:
         case VAR_RESET:
-            value = NULL;
+            *value = NULL;
             break;
 
         case VAR_RESET_ALL:
-            value = NULL;
-            resetall = true;
+            *value = NULL;
+            *resetall = true;
             break;
 
         default:
-            elog(ERROR, "unrecognized alter system stmt type: %d",
-                 altersysstmt->setstmt->kind);
+            elog(ERROR, "unrecognized set type: %d",
+                 setstmt->kind);
             break;
     }
 
     /*
      * Unless it's RESET_ALL, validate the target variable and value
      */
-    if (!resetall)
+    if (!*resetall)
     {
         struct config_generic *record;
 
-        record = find_option(name, false, ERROR);
+        record = find_option(*name, false, ERROR);
         if (record == NULL)
             ereport(ERROR,
                     (errcode(ERRCODE_UNDEFINED_OBJECT),
                      errmsg("unrecognized configuration parameter \"%s\"",
-                            name)));
+                            *name)));
 
         /*
          * Don't allow parameters that can't be set in configuration files to
          * be set in PG_AUTOCONF_FILENAME file.
          */
-        if ((record->context == PGC_INTERNAL) ||
+
+        /* PG_SUSET is changebale but only by superusers  */
+        if (is_session && !is_superuser && record->context == PGC_SUSET)
+                ereport(ERROR,
+                        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                         errmsg("permission denied to set parameter \"%s\"",
+                                *name)));
+
+        /* ALTER SESSION allows only parameters changeabe on-session */
+        if ((is_session &&
+             (record->context != PGC_USERSET &&
+              record->context != PGC_SUSET)) ||
+            record->context == PGC_INTERNAL ||
             (record->flags & GUC_DISALLOW_IN_FILE) ||
             (record->flags & GUC_DISALLOW_IN_AUTO_FILE))
             ereport(ERROR,
                     (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
                      errmsg("parameter \"%s\" cannot be changed",
-                            name)));
+                            *name)));
 
         /*
          * If a value is specified, verify that it's sane.
          */
-        if (value)
+        if (*value)
         {
             union config_var_val newval;
             void       *newextra = NULL;
 
             /* Check that it's acceptable for the indicated parameter */
-            if (!parse_and_validate_value(record, name, value,
+            if (!parse_and_validate_value(record, *name, *value,
                                           PGC_S_FILE, ERROR,
                                           &newval, &newextra))
                 ereport(ERROR,
                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                          errmsg("invalid value for parameter \"%s\": \"%s\"",
-                                name, value)));
+                                *name, *value)));
 
             if (record->vartype == PGC_STRING && newval.stringval != NULL)
                 free(newval.stringval);
@@ -7708,23 +7756,30 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
              * grammar for config files doesn't support embedded newlines in
              * string literals.
              */
-            if (strchr(value, '\n'))
+            if (strchr(*value, '\n'))
                 ereport(ERROR,
                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                         errmsg("parameter value for ALTER SYSTEM must not contain a newline")));
+                         errmsg("parameter value for SET must not contain a newline")));
         }
     }
+}
 
-    /*
-     * PG_AUTOCONF_FILENAME and its corresponding temporary file are always in
-     * the data directory, so we can reference them by simple relative paths.
-     */
-    snprintf(AutoConfFileName, sizeof(AutoConfFileName), "%s",
-             PG_AUTOCONF_FILENAME);
-    snprintf(AutoConfTmpFileName, sizeof(AutoConfTmpFileName), "%s.%s",
-             AutoConfFileName,
-             "tmp");
+/*
+ * Edit or create auto configuration files.
+ */
+static void
+EditAutoConfigFile(char *confname, bool is_session,
+                   char *name, char *value, bool resetall)
+{
+    struct stat st;
+    char    tmpname[MAXPGPATH];
+    volatile int Tmpfd;
+    ConfigVariable *head = NULL;
+    ConfigVariable *tail = NULL;
 
+    /* We create a temporary file then rename it to the persistent name */
+    snprintf(tmpname, sizeof(tmpname), "%s.%s", confname, "tmp");
+    
     /*
      * Only one backend is allowed to operate on PG_AUTOCONF_FILENAME at a
      * time.  Use AutoFileLock to ensure that.  We must hold the lock while
@@ -7734,30 +7789,43 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
 
     /*
      * If we're going to reset everything, then no need to open or parse the
-     * old file.  We'll just write out an empty list.
+     * old file.
+     */
+    if (is_session && resetall)
+    {
+        /* RESET ALL just remove files for session configuratrion */
+        if (stat(confname, &st) == 0)
+            durable_unlink(confname, ERROR);
+
+        unlink(tmpname);    /* ignore error */
+        LWLockRelease(AutoFileLock);
+        return;
+    }
+
+    /*
+     * We'll just write out an empty list for auto configration for RESET
+     * ALL
      */
     if (!resetall)
     {
-        struct stat st;
-
-        if (stat(AutoConfFileName, &st) == 0)
+        if (stat(confname, &st) == 0)
         {
-            /* open old file PG_AUTOCONF_FILENAME */
+            /* open old auto config file  */
             FILE       *infile;
 
-            infile = AllocateFile(AutoConfFileName, "r");
+            infile = AllocateFile(confname, "r");
             if (infile == NULL)
                 ereport(ERROR,
                         (errcode_for_file_access(),
                          errmsg("could not open file \"%s\": %m",
-                                AutoConfFileName)));
+                                confname)));
 
             /* parse it */
-            if (!ParseConfigFp(infile, AutoConfFileName, 0, LOG, &head, &tail))
+            if (!ParseConfigFp(infile, confname, 0, LOG, &head, &tail))
                 ereport(ERROR,
                         (errcode(ERRCODE_CONFIG_FILE_ERROR),
                          errmsg("could not parse contents of file \"%s\"",
-                                AutoConfFileName)));
+                                confname)));
 
             FreeFile(infile);
         }
@@ -7776,13 +7844,13 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
      * If there is a temp file left over due to a previous crash, it's okay to
      * truncate and reuse it.
      */
-    Tmpfd = BasicOpenFile(AutoConfTmpFileName,
+    Tmpfd = BasicOpenFile(tmpname,
                           O_CREAT | O_RDWR | O_TRUNC);
     if (Tmpfd < 0)
         ereport(ERROR,
                 (errcode_for_file_access(),
                  errmsg("could not open file \"%s\": %m",
-                        AutoConfTmpFileName)));
+                        tmpname)));
 
     /*
      * Use a TRY block to clean up the file if we fail.  Since we need a TRY
@@ -7791,7 +7859,7 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
     PG_TRY();
     {
         /* Write and sync the new contents to the temporary file */
-        write_auto_conf_file(Tmpfd, AutoConfTmpFileName, head);
+        write_auto_conf_file(Tmpfd, tmpname, is_session, head);
 
         /* Close before renaming; may be required on some platforms */
         close(Tmpfd);
@@ -7802,7 +7870,7 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
          * at worst it can lose the parameters set by last ALTER SYSTEM
          * command.
          */
-        durable_rename(AutoConfTmpFileName, AutoConfFileName, ERROR);
+        durable_rename(tmpname, confname, ERROR);
     }
     PG_CATCH();
     {
@@ -7811,7 +7879,7 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
             close(Tmpfd);
 
         /* Unlink, but ignore any error */
-        (void) unlink(AutoConfTmpFileName);
+        (void) unlink(tmpname);
 
         PG_RE_THROW();
     }
@@ -7822,6 +7890,115 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
     LWLockRelease(AutoFileLock);
 }
 
+/*
+ * Execute ALTER SYSTEM statement.
+ *
+ * Read the old PG_AUTOCONF_FILENAME file, merge in the new variable value,
+ * and write out an updated file.  If the command is ALTER SYSTEM RESET ALL,
+ * we can skip reading the old file and just write an empty file.
+ *
+ * An LWLock is used to serialize updates of the configuration file.
+ *
+ * In case of an error, we leave the original automatic
+ * configuration file (PG_AUTOCONF_FILENAME) intact.
+ */
+void
+AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
+{
+    char       *name;
+    char       *value;
+    bool        resetall = false;
+    char        AutoConfFileName[MAXPGPATH];
+
+    if (!superuser())
+        ereport(ERROR,
+                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                 (errmsg("must be superuser to execute ALTER SYSTEM command"))));
+
+    /*
+     * Extract statement arguments
+     */
+
+    AlterConfigProcessSetCommand(altersysstmt->setstmt, false, false,
+                                 &name, &value, &resetall);
+    /*
+     * PG_AUTOCONF_FILENAME and its corresponding temporary file are always in
+     * the data directory, so we can reference them by simple relative paths.
+     */
+    snprintf(AutoConfFileName, sizeof(AutoConfFileName), "%s",
+             PG_AUTOCONF_FILENAME);
+
+    EditAutoConfigFile(AutoConfFileName, false, name, value, resetall);
+}
+
+/*
+ * Execute ALTER SESSION statement.
+ *
+ * Peforms the similar thing with ALTER SYSTEM to PG_SESSIONCONF_FILENAME.
+ */
+void
+AlterSessionSetConfigFile(ParseState *pstate, AlterSessionStmt *altersesstmt)
+{
+    int            target_pid;
+    PGPROC       *target_proc;
+    DefElem       *def;
+    char       *name;
+    char       *value;
+    bool        resetall = false;
+    char        SessionConfigFile[MAXPGPATH];
+
+    def = altersesstmt->sessionopt;
+
+    /* ignoring namespace */
+    if (strcmp(def->defname, "pid") != 0)
+        ereport(ERROR,
+                (errmsg ("only pid is allowed here"),
+                 parser_errposition(pstate, def->location)));
+
+    target_pid = defGetInt32(def);
+
+    target_proc = BackendPidGetProc(target_pid);
+
+    if (target_proc == NULL)
+        ereport(ERROR,
+                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                 errmsg("PID %d is not a PostgreSQL server process",
+                        target_pid)));
+
+    /* The same condition to pg_signal_backend() */
+    if ((superuser_arg(target_proc->roleId) && !superuser()) ||
+        (!has_privs_of_role(GetUserId(), target_proc->roleId) &&
+         !has_privs_of_role(GetUserId(), DEFAULT_ROLE_SIGNAL_BACKENDID)))
+        ereport(ERROR,
+                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                 errmsg("insufficient privileges for the session")));
+
+    if (target_proc->backendId == InvalidBackendId)
+        elog(ERROR, "not a backend");
+
+    AlterConfigProcessSetCommand(altersesstmt->setstmt, true, superuser(),
+                                 &name, &value, &resetall);
+
+    /* don't allow RESET ALL for non-superusers */
+    if (resetall && !superuser())
+        ereport(ERROR,
+                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                 errmsg("RESET ALL requres superuser privileges")));
+
+    /* Ok, edit or create session config file */
+    snprintf(SessionConfigFile, sizeof(SessionConfigFile),
+             PG_SESSIONCONF_FILENAME, target_proc->backendId);
+
+    EditAutoConfigFile(SessionConfigFile, true, name, value, resetall);
+
+    if (altersesstmt->immediate)
+    {
+        if (kill(target_pid, SIGHUP))
+            ereport(WARNING,
+                    (errmsg("failed to send signal to session: %m")));
+    }
+}
+
 /*
  * SET command
  */
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index fd50a809ea..138190e9dc 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -221,7 +221,8 @@ static const char *const subdirs[] = {
     "pg_xact",
     "pg_logical",
     "pg_logical/snapshots",
-    "pg_logical/mappings"
+    "pg_logical/mappings",
+    "pg_session_conf"
 };
 
 
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index fbe2dc14a7..74e4fe2f0d 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -407,6 +407,7 @@ typedef enum NodeTag
     T_RefreshMatViewStmt,
     T_ReplicaIdentityStmt,
     T_AlterSystemStmt,
+    T_AlterSessionStmt,
     T_CreatePolicyStmt,
     T_AlterPolicyStmt,
     T_CreateTransformStmt,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 4ec8a83541..98658ce714 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -3121,6 +3121,18 @@ typedef struct AlterSystemStmt
     VariableSetStmt *setstmt;    /* SET subcommand */
 } AlterSystemStmt;
 
+/* ----------------------
+ *        Alter Session Statement
+ * ----------------------
+ */
+typedef struct AlterSessionStmt
+{
+    NodeTag        type;
+    DefElem       *sessionopt;        /* session property */
+    VariableSetStmt *setstmt;    /* SET subcommand */
+    bool        immediate;        /* reload immediately */
+} AlterSessionStmt;
+
 /* ----------------------
  *        Cluster Statement (support pbrown's cluster index implementation)
  * ----------------------
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index c07e7b945e..e02b8908c8 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -14,6 +14,7 @@
 #define GUC_H
 
 #include "nodes/parsenodes.h"
+#include "parser/parse_node.h"
 #include "tcop/dest.h"
 #include "utils/array.h"
 
@@ -33,6 +34,14 @@
  */
 #define PG_AUTOCONF_FILENAME        "postgresql.auto.conf"
 
+/*
+ * Session configuration file name for ALTER SESSION.
+ * This file will be used to store values of configuration parameters
+ * set by ALTER SESSION command.
+ */
+#define PG_SESSIONCONF_DIRNAME    "pg_session_conf"
+#define PG_SESSIONCONF_FILENAME    PG_SESSIONCONF_DIRNAME"/postgresql.%d.conf"
+
 /*
  * Certain options can only be set at certain times. The rules are
  * like this:
@@ -351,6 +360,8 @@ extern const char *GetConfigOption(const char *name, bool missing_ok,
 extern const char *GetConfigOptionResetString(const char *name);
 extern int    GetConfigOptionFlags(const char *name, bool missing_ok);
 extern void ProcessConfigFile(GucContext context);
+extern void CleanupSessionGUCfiles(void);
+extern void InitializeSessionGUC(void);
 extern void InitializeGUCOptions(void);
 extern bool SelectConfigFiles(const char *userDoption, const char *progname);
 extern void ResetAllOptions(void);
@@ -367,6 +378,8 @@ extern int set_config_option(const char *name, const char *value,
                   GucAction action, bool changeVal, int elevel,
                   bool is_reload);
 extern void AlterSystemSetConfigFile(AlterSystemStmt *setstmt);
+extern void AlterSessionSetConfigFile(ParseState *pstate,
+                                      AlterSessionStmt *altersesstmt);
 extern char *GetConfigOptionByName(const char *name, const char **varname,
                       bool missing_ok);
 extern void GetConfigOptionByNum(int varnum, const char **values, bool *noshow);
-- 
2.16.3


Re: ALTER SESSION

From
Andres Freund
Date:
Hi,

On 2019-01-29 20:32:54 +0900, Kyotaro HORIGUCHI wrote:
> Hello.
> 
> https://www.postgresql.org/message-id/20190128.133143.115303042.horiguchi.kyotaro@lab.ntt.co.jp
> 
> > C. Provide session-specific GUC variable (that overides the global one)
> >    - Add new configuration file "postgresql.conf.<PID>" and
> >      pg_reload_conf() let the session with the PID loads it as if
> >      it is the last included file. All such files are removed at
> >      startup or at the end of the coressponding session.
> > 
> >    - Add a new syntax like this:
> >      ALTER SESSION WITH (pid=xxxx)
> >         SET configuration_parameter {TO | =} {value | 'value' | DEFAULT}
> >         RESET configuration_parameter
> >         RESET ALL
> > 
> >    - Target variables are marked with GUC_REMOTE.
> > 
> > I'll consider the last choice and will come up with a patch.
> 
> This is that, with a small change in design.
> 
> ALTER SESSION WITH (pid <pid>) SET param {TO|=} value [ IMMEDIATE ]
> ALTER SESSION WITH (pid <pid>) RESET param [ IMMEDIATE ]
> ALTER SESSION WITH (pid <pid>) RESET ALL
> 
> The first form create an entry in
> $PGDATA/pg_session_conf/postgresql.<beid>.conf.
> The second form removes the entry.
> The third form removes the file itself.
> 
> IMMEDIATE specifies that the change is applied immediately by
> sending SIGHUP to the process. pg_reload_conf() works as well.
> 
> The session configuration is removed at session-end and the
> directory is cleaned up at startup.
> 
> It can change varaibles of PGC_USERSET by non-superuser or
> PGC_SUSET by superuser. The local session user should have the
> same privileges with pg_signal_backend() on the target session.
> 
> This patch contains documentation but doesn't contain test yet.
> 
> I would appreciate any comments or suggestions on this.

Leaving the desirability of the feature aside, isn't this racy as hell?
I.e. it seems entirely possible that backends stop/start between
determining the PID, and the ALTER SESSION creating the file, and it
actually being processed. By the time that happens an entirely different
session might be using that pid.

And IMMEDIATE wouldn't be very immediate, considering e.g. longrunning
queries / VACUUM etc, which'll only process new config in the mainloop.

- Andres


Re: ALTER SESSION

From
Stephen Frost
Date:
Greetings,

* Andres Freund (andres@anarazel.de) wrote:
> On 2019-01-29 20:32:54 +0900, Kyotaro HORIGUCHI wrote:
> > Hello.
> >
> > https://www.postgresql.org/message-id/20190128.133143.115303042.horiguchi.kyotaro@lab.ntt.co.jp
> >
> > > C. Provide session-specific GUC variable (that overides the global one)
> > >    - Add new configuration file "postgresql.conf.<PID>" and
> > >      pg_reload_conf() let the session with the PID loads it as if
> > >      it is the last included file. All such files are removed at
> > >      startup or at the end of the coressponding session.
> > >
> > >    - Add a new syntax like this:
> > >      ALTER SESSION WITH (pid=xxxx)
> > >         SET configuration_parameter {TO | =} {value | 'value' | DEFAULT}
> > >         RESET configuration_parameter
> > >         RESET ALL
> > >
> > >    - Target variables are marked with GUC_REMOTE.
> > >
> > > I'll consider the last choice and will come up with a patch.
> >
> > This is that, with a small change in design.
> >
> > ALTER SESSION WITH (pid <pid>) SET param {TO|=} value [ IMMEDIATE ]
> > ALTER SESSION WITH (pid <pid>) RESET param [ IMMEDIATE ]
> > ALTER SESSION WITH (pid <pid>) RESET ALL
> >
> > The first form create an entry in
> > $PGDATA/pg_session_conf/postgresql.<beid>.conf.
> > The second form removes the entry.
> > The third form removes the file itself.
> >
> > IMMEDIATE specifies that the change is applied immediately by
> > sending SIGHUP to the process. pg_reload_conf() works as well.
> >
> > The session configuration is removed at session-end and the
> > directory is cleaned up at startup.
> >
> > It can change varaibles of PGC_USERSET by non-superuser or
> > PGC_SUSET by superuser. The local session user should have the
> > same privileges with pg_signal_backend() on the target session.
> >
> > This patch contains documentation but doesn't contain test yet.
> >
> > I would appreciate any comments or suggestions on this.
>
> Leaving the desirability of the feature aside, isn't this racy as hell?
> I.e. it seems entirely possible that backends stop/start between
> determining the PID, and the ALTER SESSION creating the file, and it
> actually being processed. By the time that happens an entirely different
> session might be using that pid.

That seems like something that could possibly be fixed, by adding in
other things to make it more likely to be the 'right' backend, but my
complaint here is that we are, again, using files to pass data between
backend processes and that seems like a pretty terrible direction to be
going in.

Isn't there a whole system for passing information between different
backend processes that we could and probably should be using here
instead..?  I get that it wasn't quite intended for this originally, but
hopefully it would be possible to make it work...

> And IMMEDIATE wouldn't be very immediate, considering e.g. longrunning
> queries / VACUUM etc, which'll only process new config in the mainloop.

That's certainly a good point.

Thanks!

Stephen

Attachment

Re: ALTER SESSION

From
Andres Freund
Date:
On 2019-01-29 20:52:08 -0500, Stephen Frost wrote:
> * Andres Freund (andres@anarazel.de) wrote:
> > Leaving the desirability of the feature aside, isn't this racy as hell?
> > I.e. it seems entirely possible that backends stop/start between
> > determining the PID, and the ALTER SESSION creating the file, and it
> > actually being processed. By the time that happens an entirely different
> > session might be using that pid.
> 
> That seems like something that could possibly be fixed, by adding in
> other things to make it more likely to be the 'right' backend, but my
> complaint here is that we are, again, using files to pass data between
> backend processes and that seems like a pretty terrible direction to be
> going in.

I think pid would be wholly unsuitable for this, and if so we'd have to
use something entirely independent.


> Isn't there a whole system for passing information between different
> backend processes that we could and probably should be using here
> instead..?  I get that it wasn't quite intended for this originally, but
> hopefully it would be possible to make it work...

I'm not sure which system you're referring to? Procsignals? Those rely
on the fact that it's harmless to send such signals even when the pid
has been recycled, so that doesn't really address the issue.  And
realistically, you're going to need somtehing to persist such settings
to - they're not fixed size, and using DSM here would complicate things
to a significant degree. I don't think files would necessarily be wrong
here, if we actually want this; alternatively we could go with some
magic catalog, but that'd be a lot of infrastructure for not
particularly much gain.

Greetings,

Andres Freund


Re: ALTER SESSION

From
Stephen Frost
Date:
Greetings,

* Andres Freund (andres@anarazel.de) wrote:
> On 2019-01-29 20:52:08 -0500, Stephen Frost wrote:
> > * Andres Freund (andres@anarazel.de) wrote:
> > > Leaving the desirability of the feature aside, isn't this racy as hell?
> > > I.e. it seems entirely possible that backends stop/start between
> > > determining the PID, and the ALTER SESSION creating the file, and it
> > > actually being processed. By the time that happens an entirely different
> > > session might be using that pid.
> >
> > That seems like something that could possibly be fixed, by adding in
> > other things to make it more likely to be the 'right' backend, but my
> > complaint here is that we are, again, using files to pass data between
> > backend processes and that seems like a pretty terrible direction to be
> > going in.
>
> I think pid would be wholly unsuitable for this, and if so we'd have to
> use something entirely independent.

I would think you'd use pid + other stuff (user OID, backend proc entry
number, other things).  Basically, if you see a file there with your pid
on it, then you look and see if the other things match- if so, act on
it, if not, discard the file.  I still don't like this approach though,

> > Isn't there a whole system for passing information between different
> > backend processes that we could and probably should be using here
> > instead..?  I get that it wasn't quite intended for this originally, but
> > hopefully it would be possible to make it work...
>
> I'm not sure which system you're referring to? Procsignals? Those rely
> on the fact that it's harmless to send such signals even when the pid
> has been recycled, so that doesn't really address the issue.  And
> realistically, you're going to need somtehing to persist such settings
> to - they're not fixed size, and using DSM here would complicate things
> to a significant degree. I don't think files would necessarily be wrong
> here, if we actually want this; alternatively we could go with some
> magic catalog, but that'd be a lot of infrastructure for not
> particularly much gain.

I would think we'd use proc signals to say "hey, go check this when you
get a chance" or similar, but, no, I was thinking for actually passing
the data we'd use a DSM.  I can see how that would complicate things but
that seems like something we might be able to solve by making it easier
to use them for this simplified use-case.

I really don't think files are the right way to be going about this.

A magic catalog sounds like an interesting idea.  Another thought I had
was something around pipes but it seems like that would require we have
pipes between every pair of backends..  Instead, I'd think we'd have a
way for any backend to plop a message into some other backend's message
queue and then that backend processes it when it gets to it.

I don't think this is going to be the last time we want to do something
like this and so having a bunch of individually built file-based systems
for passing information between backends seems really grotty.

Thanks!

Stephen

Attachment

Re: ALTER SESSION

From
Andres Freund
Date:
On 2019-01-29 21:09:22 -0500, Stephen Frost wrote:
> Greetings,
> 
> * Andres Freund (andres@anarazel.de) wrote:
> > On 2019-01-29 20:52:08 -0500, Stephen Frost wrote:
> > > * Andres Freund (andres@anarazel.de) wrote:
> > > > Leaving the desirability of the feature aside, isn't this racy as hell?
> > > > I.e. it seems entirely possible that backends stop/start between
> > > > determining the PID, and the ALTER SESSION creating the file, and it
> > > > actually being processed. By the time that happens an entirely different
> > > > session might be using that pid.
> > > 
> > > That seems like something that could possibly be fixed, by adding in
> > > other things to make it more likely to be the 'right' backend, but my
> > > complaint here is that we are, again, using files to pass data between
> > > backend processes and that seems like a pretty terrible direction to be
> > > going in.
> > 
> > I think pid would be wholly unsuitable for this, and if so we'd have to
> > use something entirely independent.
> 
> I would think you'd use pid + other stuff (user OID, backend proc entry
> number, other things).  Basically, if you see a file there with your pid
> on it, then you look and see if the other things match- if so, act on
> it, if not, discard the file.  I still don't like this approach though,

What do we gain by including the pid here? Seems much more reasonable to
use a session id that's just unique over the life of a cluster.


> I really don't think files are the right way to be going about this.

Why? They persist and can be removed, they are introspectable, they
automatically are removed from memory when there's no demand...

Greetings,

Andres Freund


Re: ALTER SESSION

From
Stephen Frost
Date:
Greetings,

* Andres Freund (andres@anarazel.de) wrote:
> On 2019-01-29 21:09:22 -0500, Stephen Frost wrote:
> > * Andres Freund (andres@anarazel.de) wrote:
> > > On 2019-01-29 20:52:08 -0500, Stephen Frost wrote:
> > > > * Andres Freund (andres@anarazel.de) wrote:
> > > > > Leaving the desirability of the feature aside, isn't this racy as hell?
> > > > > I.e. it seems entirely possible that backends stop/start between
> > > > > determining the PID, and the ALTER SESSION creating the file, and it
> > > > > actually being processed. By the time that happens an entirely different
> > > > > session might be using that pid.
> > > >
> > > > That seems like something that could possibly be fixed, by adding in
> > > > other things to make it more likely to be the 'right' backend, but my
> > > > complaint here is that we are, again, using files to pass data between
> > > > backend processes and that seems like a pretty terrible direction to be
> > > > going in.
> > >
> > > I think pid would be wholly unsuitable for this, and if so we'd have to
> > > use something entirely independent.
> >
> > I would think you'd use pid + other stuff (user OID, backend proc entry
> > number, other things).  Basically, if you see a file there with your pid
> > on it, then you look and see if the other things match- if so, act on
> > it, if not, discard the file.  I still don't like this approach though,
>
> What do we gain by including the pid here? Seems much more reasonable to
> use a session id that's just unique over the life of a cluster.

Are you suggesting we have one of those already, or is the idea that
we'd add a cluster-lifetime session id for this?

> > I really don't think files are the right way to be going about this.
>
> Why? They persist and can be removed, they are introspectable, they
> automatically are removed from memory when there's no demand...

Well, we don't actually want these to persist, and it's because they do
that we have to deal with removing them, and I don't see a whole lot of
gain from them being introspectable; indeed, that seems like more of a
drawback than anything since it will invite people to whack those files
around and abuse them as if they were some externally documented
interface.

They also cost disk space, they require inodes, they have to be cleaned
up and managed on shutdown/restart, backup tools need to understand what
to do with them, potentially, we have to consider if we should have a
checksum for them, we have to handle out-of-disk space cases with them,
they could cause us to run out of disk space...

These same arguments could have been made about how we could have
implemented parallel query too.  I agree that the use-case is somewhat
different there but there's also a lot of similarity when it comes to
managing this passing of information to that use-case.

Thanks!

Stephen

Attachment

Re: ALTER SESSION

From
Kyotaro HORIGUCHI
Date:
At Tue, 29 Jan 2019 21:46:28 -0500, Stephen Frost <sfrost@snowman.net> wrote in
<20190130024628.GE5118@tamriel.snowman.net>
> Greetings,
> 
> * Andres Freund (andres@anarazel.de) wrote:
> > On 2019-01-29 21:09:22 -0500, Stephen Frost wrote:
> > > * Andres Freund (andres@anarazel.de) wrote:
> > > > On 2019-01-29 20:52:08 -0500, Stephen Frost wrote:
> > > > > * Andres Freund (andres@anarazel.de) wrote:
> > > > > > Leaving the desirability of the feature aside, isn't this racy as hell?
> > > > > > I.e. it seems entirely possible that backends stop/start between
> > > > > > determining the PID, and the ALTER SESSION creating the file, and it
> > > > > > actually being processed. By the time that happens an entirely different
> > > > > > session might be using that pid.
> > > > > 
> > > > > That seems like something that could possibly be fixed, by adding in
> > > > > other things to make it more likely to be the 'right' backend, but my
> > > > > complaint here is that we are, again, using files to pass data between
> > > > > backend processes and that seems like a pretty terrible direction to be
> > > > > going in.
> > > > 
> > > > I think pid would be wholly unsuitable for this, and if so we'd have to
> > > > use something entirely independent.
> > > 
> > > I would think you'd use pid + other stuff (user OID, backend proc entry
> > > number, other things).  Basically, if you see a file there with your pid
> > > on it, then you look and see if the other things match- if so, act on
> > > it, if not, discard the file.  I still don't like this approach though,
> > 
> > What do we gain by including the pid here? Seems much more reasonable to
> > use a session id that's just unique over the life of a cluster.
> 
> Are you suggesting we have one of those already, or is the idea that
> we'd add a cluster-lifetime session id for this?

Just a 32 bit counter would suffice for such a period. But in the
attached the worst thing to happen is that the new session reads
the only one config line written by the last command, which don't
seem harmful.. (Of couse not the best, though.)

> > > I really don't think files are the right way to be going about this.
> > 
> > Why? They persist and can be removed, they are introspectable, they
> > automatically are removed from memory when there's no demand...
> 
> Well, we don't actually want these to persist, and it's because they do
> that we have to deal with removing them, and I don't see a whole lot of
> gain from them being introspectable; indeed, that seems like more of a
> drawback than anything since it will invite people to whack those files
> around and abuse them as if they were some externally documented
> interface.

.auto.conf is already a kind of such.. My first version signals
the change via shared memory (in a largely-improvable way) and
add the GUC system the complex "nontransactional SET" feature,
which lets a change persists beyond transaction end if
any. Holding changes until the session becomes idle seems also
complex.

https://www.postgresql.org/message-id/20181127.193622.252197705.horiguchi.kyotaro@lab.ntt.co.jp

The most significant reason for passing-by-file is the affinity
with the current GUC system.

> They also cost disk space, they require inodes, they have to be cleaned
> up and managed on shutdown/restart, backup tools need to understand what
> to do with them, potentially, we have to consider if we should have a
> checksum for them, we have to handle out-of-disk space cases with them,
> they could cause us to run out of disk space...

The files are so small to cause such problems easily, but I agree
that file handling is bothersome and fragile. A weak point of
signalling via shared memory is incompatibility with the current
GUC system as I mentioned above.

> These same arguments could have been made about how we could have
> implemented parallel query too.  I agree that the use-case is somewhat
> different there but there's also a lot of similarity when it comes to
> managing this passing of information to that use-case.

Parallel query passes data via DSM?

regards.

-- 
Kyotaro Horiguchi
NTT Open Source Software Center
From ceabf24d061a15ed39db544a0972ba199ce2c4bf Mon Sep 17 00:00:00 2001
From: Kyotaro Horiguchi <horiguchi.kyotaro@lab.ntt.co.jp>
Date: Tue, 29 Jan 2019 17:03:57 +0900
Subject: [PATCH] ALTER SESSION

Some tracking features are controled by GUC so they cannot be
activated from another session. The ALTER SESSION command allows
sessions to change some of GUC settings of another session.
---
 doc/src/sgml/config.sgml             |  26 ++-
 doc/src/sgml/ref/allfiles.sgml       |   1 +
 doc/src/sgml/ref/alter_session.sgml  | 160 ++++++++++++++++
 doc/src/sgml/reference.sgml          |   1 +
 src/backend/nodes/copyfuncs.c        |  14 ++
 src/backend/nodes/equalfuncs.c       |  12 ++
 src/backend/parser/gram.y            |  51 ++++-
 src/backend/postmaster/postmaster.c  |   6 +
 src/backend/replication/basebackup.c |   3 +
 src/backend/tcop/utility.c           |  13 ++
 src/backend/utils/init/postinit.c    |   3 +
 src/backend/utils/misc/guc-file.l    |  23 +++
 src/backend/utils/misc/guc.c         | 349 +++++++++++++++++++++++++++--------
 src/bin/initdb/initdb.c              |   3 +-
 src/include/nodes/nodes.h            |   1 +
 src/include/nodes/parsenodes.h       |  12 ++
 src/include/parser/kwlist.h          |   1 +
 src/include/utils/guc.h              |  13 ++
 18 files changed, 612 insertions(+), 80 deletions(-)
 create mode 100644 doc/src/sgml/ref/alter_session.sgml

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index b6f5822b84..a52989342a 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -175,6 +175,14 @@ shared_buffers = 128MB
      read whenever <filename>postgresql.conf</filename> is, and its settings take
      effect in the same way.  Settings in <filename>postgresql.auto.conf</filename>
      override those in <filename>postgresql.conf</filename>.
+
+    </para>
+    <para>
+     Furthermore a directory <filename>pg_session_conf</filename> in the
+     PostgreSQL data directory contains per-session configuration files. Every
+     file holds settings provided through the
+     <xref linkend="sql-altersession"/> command. This is read by reloading
+     after the two above files and removed at the session-end.
     </para>
 
     <para>
@@ -195,8 +203,8 @@ shared_buffers = 128MB
       The already-mentioned <xref linkend="sql-altersystem"/> command
       provides a SQL-accessible means of changing global defaults; it is
       functionally equivalent to editing <filename>postgresql.conf</filename>.
-      In addition, there are two commands that allow setting of defaults
-      on a per-database or per-role basis:
+      In addition, there are three commands that allow setting of defaults
+      on a per-database, per-role or per-session basis:
      </para>
 
      <itemizedlist>
@@ -213,6 +221,13 @@ shared_buffers = 128MB
        per-database settings to be overridden with user-specific values.
       </para>
      </listitem>
+
+     <listitem>
+      <para>
+       The <xref linkend="sql-altersession"/> command allows other sessions to
+        override session-local settings with user-specific values.
+      </para>
+     </listitem>
     </itemizedlist>
 
      <para>
@@ -223,6 +238,13 @@ shared_buffers = 128MB
       Note that some settings cannot be changed after server start, and
       so cannot be set with these commands (or the ones listed below).
     </para>
+     <para>
+      Values set with <command>ALTER SESSION</command> are applied only when
+      reloading.  They override values obtained from the configuration files
+      or server command line, and constitute defaults for the rest of the
+      session.  Note that it can change only settings that are changeable
+      on-session.
+    </para>
 
      <para>
       Once a client is connected to the database, <productname>PostgreSQL</productname>
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index c81c87ef41..650ae49a24 100644
--- a/doc/src/sgml/ref/allfiles.sgml
+++ b/doc/src/sgml/ref/allfiles.sgml
@@ -34,6 +34,7 @@ Complete list of usable sgml source files in this directory.
 <!ENTITY alterSchema        SYSTEM "alter_schema.sgml">
 <!ENTITY alterServer        SYSTEM "alter_server.sgml">
 <!ENTITY alterSequence      SYSTEM "alter_sequence.sgml">
+<!ENTITY alterSession       SYSTEM "alter_session.sgml">
 <!ENTITY alterSubscription  SYSTEM "alter_subscription.sgml">
 <!ENTITY alterSystem        SYSTEM "alter_system.sgml">
 <!ENTITY alterStatistics    SYSTEM "alter_statistics.sgml">
diff --git a/doc/src/sgml/ref/alter_session.sgml b/doc/src/sgml/ref/alter_session.sgml
new file mode 100644
index 0000000000..803d964091
--- /dev/null
+++ b/doc/src/sgml/ref/alter_session.sgml
@@ -0,0 +1,160 @@
+<!--
+doc/src/sgml/ref/alter_session.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-altersession">
+ <indexterm zone="sql-altersystem">
+  <primary>ALTER SESSION</primary>
+ </indexterm>
+
+ <refmeta>
+  <refentrytitle>ALTER SESSION</refentrytitle>
+  <manvolnum>7</manvolnum>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>ALTER SESSION</refname>
+  <refpurpose>change a server configuration parameter of a remote session</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+ALTER SESSION WITH (pid <replaceable class="parameter">session_pid</replaceable>) SET <replaceable
class="parameter">configuration_parameter</replaceable>{ TO | = } { <replaceable class="parameter">value</replaceable>
|'<replaceable class="parameter">value</replaceable>' | DEFAULT } [ [ WITH ] RELOAD ]
 
+
+ALTER SESSION WITH (pid <replaceable class="parameter">session_pid</replaceable>) RESET <replaceable
class="parameter">configuration_parameter</replaceable>[ [ WITH ] RELOAD ]
 
+ALTER SESSION WITH (pid <replaceable class="parameter">session_pid</replaceable>) RESET ALL [ [ WITH ] RELOAD ]
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+   <command>ALTER SESSION</command> is used for changing server configuration
+   parameters on the specified session. 
+   <command>ALTER SESSION</command> writes the given parameter setting to the
+   file <filename>pg_session_conf/postgresql.<replaceable>beid</replaceable>.conf</filename>
+   file, which is read in addition to <filename>postgresql.conf</filename>
+   and <filename>postgresql.auto.conf</filename>.  Setting a parameter
+   to <literal>DEFAULT</literal>, or using the
+   <command>RESET</command> variant, removes that configuration entry from the
+   session config file. Use <literal>RESET ALL</literal> to remove the
+   parameter file. The session config file lasts until the end of the session.
+  </para>
+
+  <para>
+   Values set with <command>ALTER SESSION</command> will be effective after
+   the next server configuration reload.  A server configuration reload can be
+   commanded by calling the SQL
+   function <function>pg_reload_conf()</function>, running <literal>pg_ctl
+   reload</literal>, sending a <systemitem>SIGHUP</systemitem> signal to the
+   main server process, or specifying <literal>WITH RELOAD</literal>
+   in <command>ALTER SESSION</command>. As <command>ALTER SYSTEM</command>
+   does, <command>ALTER SESSION</command> doesn't override settings changed
+   in-session.
+  </para>
+
+  <para>
+   Only superusers and users who are granted the session user or
+   pg_signal_backend can use <command>ALTER SESSION</command>.  Also, since
+   this command acts directly on the file system and cannot be rolled back, it
+   is not allowed inside a transaction block or function.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Parameters</title>
+
+  <variablelist>
+   <varlistentry>
+    <term><replaceable class="parameter">session_pid</replaceable></term>
+    <listitem>
+     <para>
+      Process ID of the target session.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">configuration_parameter</replaceable></term>
+    <listitem>
+     <para>
+      Name of a settable configuration parameter.  Available parameters are
+      documented in <xref linkend="runtime-config"/>.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">value</replaceable></term>
+    <listitem>
+     <para>
+      New value of the parameter.  Values can be specified as string
+      constants, identifiers, numbers, or comma-separated lists of
+      these, as appropriate for the particular parameter.
+      <literal>DEFAULT</literal> can be written to specify removing the
+      parameter and its value
+      from <filename>pg_session_conf/postgresql.<replaceable>beid</replaceable>.conf</filename>.
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+ </refsect1>
+
+ <refsect1>
+  <title>Notes</title>
+
+  <para>
+   This command can't be used to set <xref linkend="guc-data-directory"/>,
+   parameters that are not allowed in <filename>postgresql.conf</filename>
+   (e.g., <link linkend="runtime-config-preset">preset options</link>), nor
+   parameters that cannot be changed on-session. Only superuser can perform
+   <literal>RESET ALL</literal>.
+  </para>
+
+  <para>
+   See <xref linkend="config-setting"/> for other ways to set the parameters.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Examples</title>
+
+  <para>
+   Set the <literal>wal_level</literal>:
+<programlisting>
+ALTER SESSION WITH (pid 27162) SET track_functions = pl IMMEDIATE;
+</programlisting>
+  </para>
+
+  <para>
+   Undo that, restoring whatever setting was effective
+   in <filename>postgresql.conf</filename>
+   and <filename>postgresql.auto.conf</filename>:
+<programlisting>
+ALTER SESSION WITH (pid 27162) RESET track_functions IMMEDIATE;
+</programlisting></para>
+ </refsect1>
+
+ <refsect1>
+  <title>Compatibility</title>
+
+  <para>
+   The <command>ALTER SESSION</command> statement is a
+   <productname>PostgreSQL</productname> extension.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="sql-altersystem"/></member>
+   <member><xref linkend="sql-set"/></member>
+   <member><xref linkend="sql-show"/></member>
+  </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
index db4f4167e3..85f515e2b2 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -62,6 +62,7 @@
    &alterSchema;
    &alterSequence;
    &alterServer;
+   &alterSession;
    &alterStatistics;
    &alterSubscription;
    &alterSystem;
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 807393dfaa..7719b475ae 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3923,6 +3923,17 @@ _copyAlterSystemStmt(const AlterSystemStmt *from)
     return newnode;
 }
 
+static AlterSessionStmt *
+_copyAlterSessionStmt(const AlterSessionStmt *from)
+{
+    AlterSessionStmt *newnode = makeNode(AlterSessionStmt);
+
+    COPY_NODE_FIELD(sessionopt);
+    COPY_NODE_FIELD(setstmt);
+    COPY_SCALAR_FIELD(immediate);
+    return newnode;
+}
+
 static CreateSeqStmt *
 _copyCreateSeqStmt(const CreateSeqStmt *from)
 {
@@ -5341,6 +5352,9 @@ copyObjectImpl(const void *from)
         case T_AlterSystemStmt:
             retval = _copyAlterSystemStmt(from);
             break;
+        case T_AlterSessionStmt:
+            retval = _copyAlterSessionStmt(from);
+            break;
         case T_CreateSeqStmt:
             retval = _copyCreateSeqStmt(from);
             break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index a397de155e..b542d060f8 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1732,6 +1732,15 @@ _equalAlterSystemStmt(const AlterSystemStmt *a, const AlterSystemStmt *b)
     return true;
 }
 
+static bool
+_equalAlterSessionStmt(const AlterSessionStmt *a, const AlterSessionStmt *b)
+{
+    COMPARE_NODE_FIELD(sessionopt);
+    COMPARE_NODE_FIELD(setstmt);
+    COMPARE_SCALAR_FIELD(immediate);
+
+    return true;
+}
 
 static bool
 _equalCreateSeqStmt(const CreateSeqStmt *a, const CreateSeqStmt *b)
@@ -3406,6 +3415,9 @@ equal(const void *a, const void *b)
         case T_AlterSystemStmt:
             retval = _equalAlterSystemStmt(a, b);
             break;
+        case T_AlterSessionStmt:
+            retval = _equalAlterSessionStmt(a, b);
+            break;
         case T_CreateSeqStmt:
             retval = _equalCreateSeqStmt(a, b);
             break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index c1faf4152c..309e6da3ca 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -248,8 +248,9 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
         AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterEnumStmt
         AlterFdwStmt AlterForeignServerStmt AlterGroupStmt
         AlterObjectDependsStmt AlterObjectSchemaStmt AlterOwnerStmt
-        AlterOperatorStmt AlterSeqStmt AlterSystemStmt AlterTableStmt
-        AlterTblSpcStmt AlterExtensionStmt AlterExtensionContentsStmt AlterForeignTableStmt
+        AlterOperatorStmt AlterSeqStmt AlterSystemStmt AlterSessionStmt
+        AlterTableStmt AlterTblSpcStmt AlterExtensionStmt
+        AlterExtensionContentsStmt AlterForeignTableStmt
         AlterCompositeTypeStmt AlterUserMappingStmt
         AlterRoleStmt AlterRoleSetStmt AlterPolicyStmt
         AlterDefaultPrivilegesStmt DefACLAction
@@ -303,13 +304,14 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 %type <defelt>    createdb_opt_item copy_opt_item
                 transaction_mode_item
                 create_extension_opt_item alter_extension_opt_item
+                altersess_option_item
 
 %type <ival>    opt_lock lock_type cast_context
 %type <ival>    vacuum_option_list vacuum_option_elem
                 analyze_option_list analyze_option_elem
 %type <boolean>    opt_or_replace
                 opt_grant_grant_option opt_grant_admin_option
-                opt_nowait opt_if_exists opt_with_data
+                opt_nowait opt_if_exists opt_with_data opt_reload
 %type <ival>    opt_nowait_or_skip
 
 %type <list>    OptRoleList AlterOptRoleList
@@ -668,7 +670,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
     QUOTE
 
     RANGE READ REAL REASSIGN RECHECK RECURSIVE REF REFERENCES REFERENCING
-    REFRESH REINDEX RELATIVE_P RELEASE RENAME REPEATABLE REPLACE REPLICA
+    REFRESH REINDEX RELATIVE_P RELEASE RELOAD RENAME REPEATABLE REPLACE REPLICA
     RESET RESTART RESTRICT RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK ROLLUP
     ROUTINE ROUTINES ROW ROWS RULE
 
@@ -839,6 +841,7 @@ stmt :
             | AlterPolicyStmt
             | AlterSeqStmt
             | AlterSystemStmt
+            | AlterSessionStmt
             | AlterTableStmt
             | AlterTblSpcStmt
             | AlterCompositeTypeStmt
@@ -10169,6 +10172,45 @@ AlterSystemStmt:
         ;
 
 
+/*****************************************************************************
+ *
+ *        ALTER SESSION
+ *
+ * This is used to change configuration parameters persistently.
+ *****************************************************************************/
+
+AlterSessionStmt:
+            ALTER SESSION WITH '(' altersess_option_item ')' SET generic_set opt_reload
+                {
+                    AlterSessionStmt *n = makeNode(AlterSessionStmt);
+                    n->sessionopt = $5;
+                    n->setstmt = $8;
+                    n->immediate = $9;
+                    $$ = (Node *)n;
+                }
+            | ALTER SESSION WITH '(' altersess_option_item ')' RESET generic_reset opt_reload
+                {
+                    AlterSessionStmt *n = makeNode(AlterSessionStmt);
+                    n->sessionopt = $5;
+                    n->setstmt = $8;
+                    n->immediate = $9;
+                    $$ = (Node *)n;
+                }
+        ;
+
+altersess_option_item:
+        ColLabel Iconst
+        {
+            $$ = makeDefElem($1, (Node *)makeInteger($2), @1);
+        }
+    ;
+
+opt_reload:
+        WITH RELOAD    { $$ = true; }
+        | RELOAD    { $$ = true; }
+        | /*EMPTY*/ { $$ = false; }
+    ;
+
 /*****************************************************************************
  *
  * Manipulate a domain
@@ -15117,6 +15159,7 @@ unreserved_keyword:
             | REINDEX
             | RELATIVE_P
             | RELEASE
+            | RELOAD
             | RENAME
             | REPEATABLE
             | REPLACE
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 3052bbbc21..1a32bb5f52 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -125,6 +125,7 @@
 #include "tcop/tcopprot.h"
 #include "utils/builtins.h"
 #include "utils/datetime.h"
+#include "utils/guc.h"
 #include "utils/memutils.h"
 #include "utils/pidfile.h"
 #include "utils/ps_status.h"
@@ -1242,6 +1243,11 @@ PostmasterMain(int argc, char *argv[])
      */
     RemovePgTempFiles();
 
+    /*
+     * Remove old session configuration files.
+     */
+    CleanupSessionGUCfiles();
+    
     /*
      * Forcibly remove the files signaling a standby promotion request.
      * Otherwise, the existence of those files triggers a promotion too early,
diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c
index def6c03dd0..6631c8498b 100644
--- a/src/backend/replication/basebackup.c
+++ b/src/backend/replication/basebackup.c
@@ -153,6 +153,9 @@ static const char *excludeDirContents[] =
     /* Contents zeroed on startup, see StartupSUBTRANS(). */
     "pg_subtrans",
 
+    /* Contents removed on startup, see CleanupSessionGUCfiles(). */
+    "pg_session_conf",
+
     /* end of list */
     NULL
 };
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 6ec795f1b4..daa3af725c 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -681,6 +681,11 @@ standard_ProcessUtility(PlannedStmt *pstmt,
             AlterSystemSetConfigFile((AlterSystemStmt *) parsetree);
             break;
 
+        case T_AlterSessionStmt:
+            PreventInTransactionBlock(isTopLevel, "ALTER SESSION");
+            AlterSessionSetConfigFile(pstate, (AlterSessionStmt *) parsetree);
+            break;
+
         case T_VariableSetStmt:
             ExecSetVariableStmt((VariableSetStmt *) parsetree, isTopLevel);
             break;
@@ -2605,6 +2610,10 @@ CreateCommandTag(Node *parsetree)
             tag = "ALTER SYSTEM";
             break;
 
+        case T_AlterSessionStmt:
+            tag = "ALTER SESSION";
+            break;
+
         case T_VariableSetStmt:
             switch (((VariableSetStmt *) parsetree)->kind)
             {
@@ -3235,6 +3244,10 @@ GetCommandLogLevel(Node *parsetree)
             lev = LOGSTMT_DDL;
             break;
 
+        case T_AlterSessionStmt:
+            lev = LOGSTMT_DDL;
+            break;
+
         case T_VariableSetStmt:
             lev = LOGSTMT_ALL;
             break;
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index c0b6231458..45c005a109 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -703,6 +703,9 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
      */
     before_shmem_exit(ShutdownPostgres, 0);
 
+    /* Session initialization for GUC system */
+    InitializeSessionGUC();
+
     /* The autovacuum launcher is done here */
     if (IsAutoVacuumLauncherProcess())
     {
diff --git a/src/backend/utils/misc/guc-file.l b/src/backend/utils/misc/guc-file.l
index 1c8b5f7d84..dbb131613b 100644
--- a/src/backend/utils/misc/guc-file.l
+++ b/src/backend/utils/misc/guc-file.l
@@ -208,6 +208,29 @@ ProcessConfigFileInternal(GucContext context, bool applySettings, int elevel)
             ConfFileWithError = PG_AUTOCONF_FILENAME;
             goto bail_out;
         }
+
+        /* load session configuration if any */
+        if (MyBackendId != InvalidBackendId)
+        {
+            /* Use this name for error reporting */
+            static char SessionConfFileName[MAXPGPATH];
+            struct stat st;
+
+            snprintf(SessionConfFileName, sizeof(SessionConfFileName),
+                     PG_SESSIONCONF_FILENAME, MyBackendId);
+
+            /* be silent on absence */
+            if (stat(SessionConfFileName, &st) == 0 &&
+                !ParseConfigFile(SessionConfFileName, true,
+                                 NULL, 0, 0, elevel,
+                                 &head, &tail))
+            {
+                /* Syntax error(s) detected in the file, so bail out */
+                error = true;
+                ConfFileWithError = SessionConfFileName;
+                goto bail_out;
+            }
+        }
     }
     else
     {
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 98d75be292..12cc454537 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -36,6 +36,7 @@
 #include "catalog/namespace.h"
 #include "catalog/pg_authid.h"
 #include "commands/async.h"
+#include "commands/defrem.h"
 #include "commands/prepare.h"
 #include "commands/user.h"
 #include "commands/vacuum.h"
@@ -73,9 +74,11 @@
 #include "storage/dsm_impl.h"
 #include "storage/standby.h"
 #include "storage/fd.h"
+#include "storage/ipc.h"
 #include "storage/large_object.h"
 #include "storage/pg_shmem.h"
 #include "storage/proc.h"
+#include "storage/procarray.h"
 #include "storage/predicate.h"
 #include "tcop/tcopprot.h"
 #include "tsearch/ts_cache.h"
@@ -4483,7 +4486,8 @@ static void ShowAllGUCConfig(DestReceiver *dest);
 static char *_ShowOption(struct config_generic *record, bool use_units);
 static bool validate_option_array_item(const char *name, const char *value,
                            bool skipIfNoPermissions);
-static void write_auto_conf_file(int fd, const char *filename, ConfigVariable *head_p);
+static void write_auto_conf_file(int fd, const char *filename, bool is_session,
+                                 ConfigVariable *head_p);
 static void replace_auto_config_value(ConfigVariable **head_p, ConfigVariable **tail_p,
                           const char *name, const char *value);
 
@@ -4975,6 +4979,62 @@ guc_name_compare(const char *namea, const char *nameb)
     return 0;
 }
 
+void
+CleanupSessionGUCfiles(void)
+{
+    DIR           *dir;
+    struct dirent *entry;
+    char        fname[MAXPGPATH];
+
+    dir = AllocateDir(PG_SESSIONCONF_DIRNAME);
+    while ((entry = ReadDir(dir, PG_SESSIONCONF_DIRNAME)) != NULL)
+    {
+        if (strcmp(entry->d_name, ".") == 0 ||
+            strcmp(entry->d_name, "..") == 0)
+            continue;
+
+        snprintf(fname, sizeof(fname), "%s/%s", PG_SESSIONCONF_DIRNAME,
+                 entry->d_name);
+
+        /*
+         * no need for durable_unlnk here since this is done again in the next
+         * startup after a crash
+         */
+        if (unlink(fname) != 0)
+            ereport(NOTICE,
+                    (errcode_for_file_access(),
+                     errmsg("could not remove file \"%s\" during clean up of session config files: %m",
+                            fname)));
+    }
+    FreeDir(dir);
+}
+
+static void
+RemoveSessionGUCFile(int code, Datum arg)
+{
+    char SessionConfFileName[MAXPGPATH];
+    int     beid = DatumGetInt32(arg);
+
+    if (beid != InvalidBackendId)
+    {
+        LWLockAcquire(AutoFileLock, LW_EXCLUSIVE);
+        snprintf(SessionConfFileName, sizeof(SessionConfFileName),
+                 PG_SESSIONCONF_FILENAME, beid);
+        unlink(SessionConfFileName);
+        LWLockRelease(AutoFileLock);
+    }
+}
+
+void
+InitializeSessionGUC(void)
+{
+    if (!IsUnderPostmaster || MyBackendId == InvalidBackendId)
+        return;
+
+    RemoveSessionGUCFile(0, 0);
+    on_shmem_exit(RemoveSessionGUCFile, Int32GetDatum(MyBackendId));
+}
+
 
 /*
  * Initialize GUC options during program startup.
@@ -7471,16 +7531,19 @@ flatten_set_variable_args(const char *name, List *args)
  * values before writing them.
  */
 static void
-write_auto_conf_file(int fd, const char *filename, ConfigVariable *head)
+write_auto_conf_file(int fd, const char *filename, bool is_session,
+                     ConfigVariable *head)
 {
     StringInfoData buf;
     ConfigVariable *item;
+    char *command_name = is_session ? "ALTER SESSION" : "ALTER_SYSTEM";
 
     initStringInfo(&buf);
 
     /* Emit file header containing warning comment */
     appendStringInfoString(&buf, "# Do not edit this file manually!\n");
-    appendStringInfoString(&buf, "# It will be overwritten by the ALTER SYSTEM command.\n");
+    appendStringInfo(&buf, "# It will be overwritten by the %s command.\n",
+        command_name);
 
     errno = 0;
     if (write(fd, buf.data, buf.len) != buf.len)
@@ -7600,103 +7663,91 @@ replace_auto_config_value(ConfigVariable **head_p, ConfigVariable **tail_p,
 
 
 /*
- * Execute ALTER SYSTEM statement.
- *
- * Read the old PG_AUTOCONF_FILENAME file, merge in the new variable value,
- * and write out an updated file.  If the command is ALTER SYSTEM RESET ALL,
- * we can skip reading the old file and just write an empty file.
- *
- * An LWLock is used to serialize updates of the configuration file.
- *
- * In case of an error, we leave the original automatic
- * configuration file (PG_AUTOCONF_FILENAME) intact.
+  Check and process the given set statment of ALTER SYSTEM and ALTER SESSION.
  */
-void
-AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
+static void
+AlterConfigProcessSetCommand(VariableSetStmt *setstmt,
+                             bool is_session, bool is_superuser,
+                             char **name, char **value, bool *resetall)
 {
-    char       *name;
-    char       *value;
-    bool        resetall = false;
-    ConfigVariable *head = NULL;
-    ConfigVariable *tail = NULL;
-    volatile int Tmpfd;
-    char        AutoConfFileName[MAXPGPATH];
-    char        AutoConfTmpFileName[MAXPGPATH];
+    *name = setstmt->name;
 
-    if (!superuser())
-        ereport(ERROR,
-                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-                 (errmsg("must be superuser to execute ALTER SYSTEM command"))));
-
-    /*
-     * Extract statement arguments
-     */
-    name = altersysstmt->setstmt->name;
-
-    switch (altersysstmt->setstmt->kind)
+    switch (setstmt->kind)
     {
         case VAR_SET_VALUE:
-            value = ExtractSetVariableArgs(altersysstmt->setstmt);
+            *value = ExtractSetVariableArgs(setstmt);
             break;
 
         case VAR_SET_DEFAULT:
         case VAR_RESET:
-            value = NULL;
+            *value = NULL;
             break;
 
         case VAR_RESET_ALL:
-            value = NULL;
-            resetall = true;
+            *value = NULL;
+            *resetall = true;
             break;
 
         default:
-            elog(ERROR, "unrecognized alter system stmt type: %d",
-                 altersysstmt->setstmt->kind);
+            elog(ERROR, "unrecognized set type: %d",
+                 setstmt->kind);
             break;
     }
 
     /*
      * Unless it's RESET_ALL, validate the target variable and value
      */
-    if (!resetall)
+    if (!*resetall)
     {
         struct config_generic *record;
 
-        record = find_option(name, false, ERROR);
+        record = find_option(*name, false, ERROR);
         if (record == NULL)
             ereport(ERROR,
                     (errcode(ERRCODE_UNDEFINED_OBJECT),
                      errmsg("unrecognized configuration parameter \"%s\"",
-                            name)));
+                            *name)));
 
         /*
          * Don't allow parameters that can't be set in configuration files to
          * be set in PG_AUTOCONF_FILENAME file.
          */
-        if ((record->context == PGC_INTERNAL) ||
+
+        /* PG_SUSET is changebale but only by superusers  */
+        if (is_session && !is_superuser && record->context == PGC_SUSET)
+                ereport(ERROR,
+                        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                         errmsg("permission denied to set parameter \"%s\"",
+                                *name)));
+
+        /* ALTER SESSION allows only parameters changeabe on-session */
+        if ((is_session &&
+             (record->context != PGC_USERSET &&
+              record->context != PGC_SUSET)) ||
+            record->context == PGC_INTERNAL ||
             (record->flags & GUC_DISALLOW_IN_FILE) ||
             (record->flags & GUC_DISALLOW_IN_AUTO_FILE))
             ereport(ERROR,
                     (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
                      errmsg("parameter \"%s\" cannot be changed",
-                            name)));
+                            *name)));
 
         /*
          * If a value is specified, verify that it's sane.
          */
-        if (value)
+        if (*value)
         {
             union config_var_val newval;
             void       *newextra = NULL;
 
             /* Check that it's acceptable for the indicated parameter */
-            if (!parse_and_validate_value(record, name, value,
+            if (!parse_and_validate_value(record, *name, *value,
                                           PGC_S_FILE, ERROR,
                                           &newval, &newextra))
                 ereport(ERROR,
                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                          errmsg("invalid value for parameter \"%s\": \"%s\"",
-                                name, value)));
+                                *name, *value)));
 
             if (record->vartype == PGC_STRING && newval.stringval != NULL)
                 free(newval.stringval);
@@ -7708,23 +7759,30 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
              * grammar for config files doesn't support embedded newlines in
              * string literals.
              */
-            if (strchr(value, '\n'))
+            if (strchr(*value, '\n'))
                 ereport(ERROR,
                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                         errmsg("parameter value for ALTER SYSTEM must not contain a newline")));
+                         errmsg("parameter value for SET must not contain a newline")));
         }
     }
+}
 
-    /*
-     * PG_AUTOCONF_FILENAME and its corresponding temporary file are always in
-     * the data directory, so we can reference them by simple relative paths.
-     */
-    snprintf(AutoConfFileName, sizeof(AutoConfFileName), "%s",
-             PG_AUTOCONF_FILENAME);
-    snprintf(AutoConfTmpFileName, sizeof(AutoConfTmpFileName), "%s.%s",
-             AutoConfFileName,
-             "tmp");
+/*
+ * Edit or create auto configuration files.
+ */
+static void
+EditAutoConfigFile(char *confname, bool is_session,
+                   char *name, char *value, bool resetall)
+{
+    struct stat st;
+    char    tmpname[MAXPGPATH];
+    volatile int Tmpfd;
+    ConfigVariable *head = NULL;
+    ConfigVariable *tail = NULL;
 
+    /* We create a temporary file then rename it to the persistent name */
+    snprintf(tmpname, sizeof(tmpname), "%s.%s", confname, "tmp");
+    
     /*
      * Only one backend is allowed to operate on PG_AUTOCONF_FILENAME at a
      * time.  Use AutoFileLock to ensure that.  We must hold the lock while
@@ -7734,30 +7792,43 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
 
     /*
      * If we're going to reset everything, then no need to open or parse the
-     * old file.  We'll just write out an empty list.
+     * old file.
+     */
+    if (is_session && resetall)
+    {
+        /* RESET ALL just remove files for session configuratrion */
+        if (stat(confname, &st) == 0)
+            durable_unlink(confname, ERROR);
+
+        unlink(tmpname);    /* ignore error */
+        LWLockRelease(AutoFileLock);
+        return;
+    }
+
+    /*
+     * We'll just write out an empty list for auto configration for RESET
+     * ALL
      */
     if (!resetall)
     {
-        struct stat st;
-
-        if (stat(AutoConfFileName, &st) == 0)
+        if (stat(confname, &st) == 0)
         {
-            /* open old file PG_AUTOCONF_FILENAME */
+            /* open old auto config file  */
             FILE       *infile;
 
-            infile = AllocateFile(AutoConfFileName, "r");
+            infile = AllocateFile(confname, "r");
             if (infile == NULL)
                 ereport(ERROR,
                         (errcode_for_file_access(),
                          errmsg("could not open file \"%s\": %m",
-                                AutoConfFileName)));
+                                confname)));
 
             /* parse it */
-            if (!ParseConfigFp(infile, AutoConfFileName, 0, LOG, &head, &tail))
+            if (!ParseConfigFp(infile, confname, 0, LOG, &head, &tail))
                 ereport(ERROR,
                         (errcode(ERRCODE_CONFIG_FILE_ERROR),
                          errmsg("could not parse contents of file \"%s\"",
-                                AutoConfFileName)));
+                                confname)));
 
             FreeFile(infile);
         }
@@ -7776,13 +7847,13 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
      * If there is a temp file left over due to a previous crash, it's okay to
      * truncate and reuse it.
      */
-    Tmpfd = BasicOpenFile(AutoConfTmpFileName,
+    Tmpfd = BasicOpenFile(tmpname,
                           O_CREAT | O_RDWR | O_TRUNC);
     if (Tmpfd < 0)
         ereport(ERROR,
                 (errcode_for_file_access(),
                  errmsg("could not open file \"%s\": %m",
-                        AutoConfTmpFileName)));
+                        tmpname)));
 
     /*
      * Use a TRY block to clean up the file if we fail.  Since we need a TRY
@@ -7791,7 +7862,7 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
     PG_TRY();
     {
         /* Write and sync the new contents to the temporary file */
-        write_auto_conf_file(Tmpfd, AutoConfTmpFileName, head);
+        write_auto_conf_file(Tmpfd, tmpname, is_session, head);
 
         /* Close before renaming; may be required on some platforms */
         close(Tmpfd);
@@ -7802,7 +7873,7 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
          * at worst it can lose the parameters set by last ALTER SYSTEM
          * command.
          */
-        durable_rename(AutoConfTmpFileName, AutoConfFileName, ERROR);
+        durable_rename(tmpname, confname, ERROR);
     }
     PG_CATCH();
     {
@@ -7811,7 +7882,7 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
             close(Tmpfd);
 
         /* Unlink, but ignore any error */
-        (void) unlink(AutoConfTmpFileName);
+        (void) unlink(tmpname);
 
         PG_RE_THROW();
     }
@@ -7822,6 +7893,138 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
     LWLockRelease(AutoFileLock);
 }
 
+/*
+ * Execute ALTER SYSTEM statement.
+ *
+ * Read the old PG_AUTOCONF_FILENAME file, merge in the new variable value,
+ * and write out an updated file.  If the command is ALTER SYSTEM RESET ALL,
+ * we can skip reading the old file and just write an empty file.
+ *
+ * An LWLock is used to serialize updates of the configuration file.
+ *
+ * In case of an error, we leave the original automatic
+ * configuration file (PG_AUTOCONF_FILENAME) intact.
+ */
+void
+AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
+{
+    char       *name;
+    char       *value;
+    bool        resetall = false;
+    char        AutoConfFileName[MAXPGPATH];
+
+    if (!superuser())
+        ereport(ERROR,
+                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                 (errmsg("must be superuser to execute ALTER SYSTEM command"))));
+
+    /*
+     * Extract statement arguments
+     */
+
+    AlterConfigProcessSetCommand(altersysstmt->setstmt, false, false,
+                                 &name, &value, &resetall);
+    /*
+     * PG_AUTOCONF_FILENAME and its corresponding temporary file are always in
+     * the data directory, so we can reference them by simple relative paths.
+     */
+    snprintf(AutoConfFileName, sizeof(AutoConfFileName), "%s",
+             PG_AUTOCONF_FILENAME);
+
+    EditAutoConfigFile(AutoConfFileName, false, name, value, resetall);
+}
+
+/*
+ * Execute ALTER SESSION statement.
+ *
+ * Peforms the similar thing with ALTER SYSTEM to PG_SESSIONCONF_FILENAME.
+ */
+void
+AlterSessionSetConfigFile(ParseState *pstate, AlterSessionStmt *altersesstmt)
+{
+    int            target_pid;
+    PGPROC       *target_proc;
+    BackendId    target_beid;
+    DefElem       *def;
+    char       *name;
+    char       *value;
+    bool        resetall = false;
+    char        SessionConfigFile[MAXPGPATH];
+
+    def = altersesstmt->sessionopt;
+
+    /* ignoring namespace */
+    if (strcmp(def->defname, "pid") != 0)
+        ereport(ERROR,
+                (errmsg ("only pid is allowed here"),
+                 parser_errposition(pstate, def->location)));
+
+    target_pid = defGetInt32(def);
+
+    target_proc = BackendPidGetProc(target_pid);
+
+    if (target_proc == NULL)
+        ereport(ERROR,
+                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                 errmsg("PID %d is not a PostgreSQL server process",
+                        target_pid)));
+
+    target_beid = target_proc->backendId;
+
+    /* The same condition to pg_signal_backend() */
+    if ((superuser_arg(target_proc->roleId) && !superuser()) ||
+        (!has_privs_of_role(GetUserId(), target_proc->roleId) &&
+         !has_privs_of_role(GetUserId(), DEFAULT_ROLE_SIGNAL_BACKENDID)))
+        ereport(ERROR,
+                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                 errmsg("insufficient privileges for the session")));
+
+    if (target_beid == InvalidBackendId)
+        elog(ERROR, "not a backend");
+
+    AlterConfigProcessSetCommand(altersesstmt->setstmt, true, superuser(),
+                                 &name, &value, &resetall);
+
+    /* don't allow RESET ALL for non-superusers */
+    if (resetall && !superuser())
+        ereport(ERROR,
+                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                 errmsg("RESET ALL requres superuser privileges")));
+
+    /* Ok, edit or create session config file */
+    snprintf(SessionConfigFile, sizeof(SessionConfigFile),
+             PG_SESSIONCONF_FILENAME, target_beid);
+
+    EditAutoConfigFile(SessionConfigFile, true, name, value, resetall);
+
+    /*
+     * Some racy condition here.
+     *
+     * The session config file has been created or changed. Now check again if
+     * the target session is still living. A pid is supposed not to be
+     * assigned to another process shortly but check backend ID just in
+     * case. Still it's possible that the both match with a different
+     * session. In the case the config file has been removed by the
+     * session-end after editing, or recreated after the removal. Even in the
+     * latter case the new session receives the change commanded this time,
+     * which doesn't harm so much.
+     */
+    if (target_proc->pid != target_pid || target_proc->backendId != target_beid)
+    {
+        RemoveSessionGUCFile(0, Int32GetDatum(target_beid));
+        ereport(WARNING,
+                (errmsg ("target session has terminated")));
+        return;
+    }
+    
+    if (altersesstmt->immediate)
+    {
+        if (kill(target_pid, SIGHUP))
+            ereport(WARNING,
+                    (errmsg("failed to send signal to session: %m")));
+    }
+}
+
 /*
  * SET command
  */
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index fd50a809ea..138190e9dc 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -221,7 +221,8 @@ static const char *const subdirs[] = {
     "pg_xact",
     "pg_logical",
     "pg_logical/snapshots",
-    "pg_logical/mappings"
+    "pg_logical/mappings",
+    "pg_session_conf"
 };
 
 
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index fbe2dc14a7..74e4fe2f0d 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -407,6 +407,7 @@ typedef enum NodeTag
     T_RefreshMatViewStmt,
     T_ReplicaIdentityStmt,
     T_AlterSystemStmt,
+    T_AlterSessionStmt,
     T_CreatePolicyStmt,
     T_AlterPolicyStmt,
     T_CreateTransformStmt,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 4ec8a83541..98658ce714 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -3121,6 +3121,18 @@ typedef struct AlterSystemStmt
     VariableSetStmt *setstmt;    /* SET subcommand */
 } AlterSystemStmt;
 
+/* ----------------------
+ *        Alter Session Statement
+ * ----------------------
+ */
+typedef struct AlterSessionStmt
+{
+    NodeTag        type;
+    DefElem       *sessionopt;        /* session property */
+    VariableSetStmt *setstmt;    /* SET subcommand */
+    bool        immediate;        /* reload immediately */
+} AlterSessionStmt;
+
 /* ----------------------
  *        Cluster Statement (support pbrown's cluster index implementation)
  * ----------------------
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index adeb834ce8..aaae750c3f 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -330,6 +330,7 @@ PG_KEYWORD("refresh", REFRESH, UNRESERVED_KEYWORD)
 PG_KEYWORD("reindex", REINDEX, UNRESERVED_KEYWORD)
 PG_KEYWORD("relative", RELATIVE_P, UNRESERVED_KEYWORD)
 PG_KEYWORD("release", RELEASE, UNRESERVED_KEYWORD)
+PG_KEYWORD("reload", RELOAD, UNRESERVED_KEYWORD)
 PG_KEYWORD("rename", RENAME, UNRESERVED_KEYWORD)
 PG_KEYWORD("repeatable", REPEATABLE, UNRESERVED_KEYWORD)
 PG_KEYWORD("replace", REPLACE, UNRESERVED_KEYWORD)
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index c07e7b945e..e02b8908c8 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -14,6 +14,7 @@
 #define GUC_H
 
 #include "nodes/parsenodes.h"
+#include "parser/parse_node.h"
 #include "tcop/dest.h"
 #include "utils/array.h"
 
@@ -33,6 +34,14 @@
  */
 #define PG_AUTOCONF_FILENAME        "postgresql.auto.conf"
 
+/*
+ * Session configuration file name for ALTER SESSION.
+ * This file will be used to store values of configuration parameters
+ * set by ALTER SESSION command.
+ */
+#define PG_SESSIONCONF_DIRNAME    "pg_session_conf"
+#define PG_SESSIONCONF_FILENAME    PG_SESSIONCONF_DIRNAME"/postgresql.%d.conf"
+
 /*
  * Certain options can only be set at certain times. The rules are
  * like this:
@@ -351,6 +360,8 @@ extern const char *GetConfigOption(const char *name, bool missing_ok,
 extern const char *GetConfigOptionResetString(const char *name);
 extern int    GetConfigOptionFlags(const char *name, bool missing_ok);
 extern void ProcessConfigFile(GucContext context);
+extern void CleanupSessionGUCfiles(void);
+extern void InitializeSessionGUC(void);
 extern void InitializeGUCOptions(void);
 extern bool SelectConfigFiles(const char *userDoption, const char *progname);
 extern void ResetAllOptions(void);
@@ -367,6 +378,8 @@ extern int set_config_option(const char *name, const char *value,
                   GucAction action, bool changeVal, int elevel,
                   bool is_reload);
 extern void AlterSystemSetConfigFile(AlterSystemStmt *setstmt);
+extern void AlterSessionSetConfigFile(ParseState *pstate,
+                                      AlterSessionStmt *altersesstmt);
 extern char *GetConfigOptionByName(const char *name, const char **varname,
                       bool missing_ok);
 extern void GetConfigOptionByNum(int varnum, const char **values, bool *noshow);
-- 
2.16.3


RE: ALTER SESSION

From
"Tsunakawa, Takayuki"
Date:
Thanks for a cool feature with nice UI.  Can I expect it to work for background processes?  For troubleshooting, I
wantedto investigate how autovacuum launcher/worker behaves by having them emit DEBUG messages.
 

(My comments follow)


From: Kyotaro HORIGUCHI [mailto:horiguchi.kyotaro@lab.ntt.co.jp]
> .auto.conf is already a kind of such.. My first version signals the change
> via shared memory (in a largely-improvable way) and add the GUC system the
> complex "nontransactional SET" feature, which lets a change persists beyond
> transaction end if any. Holding changes until the session becomes idle seems
> also complex.
> 
> https://www.postgresql.org/message-id/20181127.193622.252197705.horigu
> chi.kyotaro@lab.ntt.co.jp
> 
> The most significant reason for passing-by-file is the affinity with the
> current GUC system.

Regarding the target session specification, do we want to use pid or a session ID like the following?

https://www.postgresql.org/docs/devel/runtime-config-logging.html
--------------------------------------------------
The %c escape prints a quasi-unique session identifier, consisting of two 4-byte hexadecimal numbers (without leading
zeros)separated by a dot. The numbers are the process start time and the process ID, so %c can also be used as a space
savingway of printing those items. For example, to generate the session identifier from pg_stat_activity, use this
query:

SELECT to_hex(trunc(EXTRACT(EPOCH FROM backend_start))::integer) || '.' ||
       to_hex(pid)
FROM pg_stat_activity;

  PRIMARY KEY (session_id, session_line_num)
--------------------------------------------------


pid is easier to type.  However, the session startup needs to try to delete the leftover file.  Is the file system
accessnegligible compared with the total heavy session startup processing?
 

If we choose session ID, the session startup doesn't have to care about the leftover file.  However, the background
processcrash could leave the file for a long time, since the crash may not lead to postmaster restart.  Also, we will
getinclined to add sessionid column in pg_stat_activity (the concept of session ID can be useful for other uses.)
 


I'm OK about passing parameter changes via a file.  But I'm not sure whether using DSM (DSA) is easier with less code.

And considering the multi-threaded server Konstantin is proposing, would it better to take pass-by-memory approach?  I
imaginethat if the server gets multi-threaded, the parameter change would be handled like:
 

1. Allocate memory for one parameter change.
2. Write the change to that memory.
3. Link the memory to a session-specific list.
4. The target session removes the entry from the list, applies the change, and frees the memory.

The code modification may be minimal when we migrate to the multi-threaded server -- only memory allocation and free
functions.


Regards
Takayuki Tsunakawa




Re: ALTER SESSION

From
Kyotaro HORIGUCHI
Date:
Hello.

At Fri, 1 Feb 2019 06:31:40 +0000, "Tsunakawa, Takayuki" <tsunakawa.takay@jp.fujitsu.com> wrote in
<0A3221C70F24FB45833433255569204D1FB927B1@G01JPEXMBYT05>
> Thanks for a cool feature with nice UI.  Can I expect it to work for background processes?  For troubleshooting, I
wantedto investigate how autovacuum launcher/worker behaves by having them emit DEBUG messages.
 
> 
> (My comments follow)

I haven't did actually, but it doesn't reject background
workers. But background worker seems to assume that no change in
variablres while working. I should consider that.

> From: Kyotaro HORIGUCHI [mailto:horiguchi.kyotaro@lab.ntt.co.jp]
> > .auto.conf is already a kind of such.. My first version signals the change
> > via shared memory (in a largely-improvable way) and add the GUC system the
> > complex "nontransactional SET" feature, which lets a change persists beyond
> > transaction end if any. Holding changes until the session becomes idle seems
> > also complex.
> > 
> > https://www.postgresql.org/message-id/20181127.193622.252197705.horigu
> > chi.kyotaro@lab.ntt.co.jp
> > 
> > The most significant reason for passing-by-file is the affinity with the
> > current GUC system.
> 
> Regarding the target session specification, do we want to use pid or a session ID like the following?
> 
> https://www.postgresql.org/docs/devel/runtime-config-logging.html
> --------------------------------------------------
> The %c escape prints a quasi-unique session identifier, consisting of two 4-byte hexadecimal numbers (without leading
zeros)separated by a dot. The numbers are the process start time and the process ID, so %c can also be used as a space
savingway of printing those items. For example, to generate the session identifier from pg_stat_activity, use this
query:
> 
> SELECT to_hex(trunc(EXTRACT(EPOCH FROM backend_start))::integer) || '.' ||
>        to_hex(pid)
> FROM pg_stat_activity;
> 
> pid is easier to type.  However, the session startup needs to try to delete the leftover file.  Is the file system
accessnegligible compared with the total heavy session startup processing?
 
> 
> If we choose session ID, the session startup doesn't have to care about the leftover file.  However, the background
processcrash could leave the file for a long time, since the crash may not lead to postmaster restart.  Also, we will
getinclined to add sessionid column in pg_stat_activity (the concept of session ID can be useful for other uses.)
 

Sounds reasonbale. 

The attached version happens to add backend startup time in
PGPROC and I added session id as a usable key. (Heavily WIP)

ALTER SESSION WITH (id '5c540141.b7f') SET work_mem to '128kB';

> I'm OK about passing parameter changes via a file.  But I'm not sure whether using DSM (DSA) is easier with less
code.

Perhaps DSA is not required. Currently it uses rather a large
area but I think we can do the same thing with smaller memory by
sending long strings part by part.

> And considering the multi-threaded server Konstantin is proposing, would it better to take pass-by-memory approach?
Iimagine that if the server gets multi-threaded, the parameter change would be handled like:
 
> 
> 1. Allocate memory for one parameter change.
> 2. Write the change to that memory.
> 3. Link the memory to a session-specific list.
> 4. The target session removes the entry from the list, applies the change, and frees the memory.
> 
> The code modification may be minimal when we migrate to the multi-threaded server -- only memory allocation and free
functions.

The attached is a WIP patch that:

 - Using the non-transactional SET (for my convenience).

 - based on not file, but static shared memory.
   Using a new signal 

 - It adds PGC_S_REMOTE with the same precedence with PGC_S_SESSION.
   (This causes arguable behavior..)

 - ALTER SESSION syntax. (key can be pid or session id)


(Sorry for the inconsistent name of the patch files..)

regards.

-- 
Kyotaro Horiguchi
NTT Open Source Software Center
From 82970b92d406d487f1bc88a64fb9e89dc9a6b039 Mon Sep 17 00:00:00 2001
From: Kyotaro Horiguchi <horiguchi.kyotaro@lab.ntt.co.jp>
Date: Fri, 1 Feb 2019 09:45:48 +0900
Subject: [PATCH 1/2] Add start timestamp to PGPROC

A ProcArray element may be allocated to another session while
investigating. Pid is not fully relialble for cheking that because of
the possibility of reusing. Add start timestamp to resolve the issue.
---
 src/backend/storage/lmgr/proc.c | 2 ++
 src/include/storage/proc.h      | 1 +
 2 files changed, 3 insertions(+)

diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 89c80fb687..a63309a432 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -373,6 +373,8 @@ InitProcess(void)
     MyProc->fpLocalTransactionId = InvalidLocalTransactionId;
     MyPgXact->xid = InvalidTransactionId;
     MyPgXact->xmin = InvalidTransactionId;
+    /* set this before pid to get rid of wrong pair with pid */
+    MyProc->starttimestamp = MyStartTimestamp;
     MyProc->pid = MyProcPid;
     /* backendId, databaseId and roleId will be filled in later */
     MyProc->backendId = InvalidBackendId;
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index d203acbb30..2cebdd2646 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -108,6 +108,7 @@ struct PGPROC
                                  * else InvalidLocalTransactionId */
     int            pid;            /* Backend's process ID; 0 if prepared xact */
     int            pgprocno;
+    uint64        starttimestamp;    /* Backend's start timestamp */
 
     /* These fields are zero while a backend is still starting up: */
     BackendId    backendId;        /* This backend's backend ID (if assigned) */
-- 
2.16.3

From 2e1fdd603948167aba0886036d9a2461bb051107 Mon Sep 17 00:00:00 2001
From: Kyotaro Horiguchi <horiguchi.kyotaro@lab.ntt.co.jp>
Date: Fri, 1 Feb 2019 17:23:09 +0900
Subject: [PATCH 2/2] Remote GUC (via shmem, ALTER SESSION, NoXact SET version)

---
 doc/src/sgml/config.sgml             |  26 +-
 doc/src/sgml/func.sgml               |  30 ++
 doc/src/sgml/ref/allfiles.sgml       |   1 +
 doc/src/sgml/ref/alter_session.sgml  | 160 +++++++
 doc/src/sgml/reference.sgml          |   1 +
 src/backend/catalog/system_views.sql |   7 +-
 src/backend/commands/discard.c       |   2 +-
 src/backend/nodes/copyfuncs.c        |  14 +
 src/backend/nodes/equalfuncs.c       |  12 +
 src/backend/parser/gram.y            |  44 +-
 src/backend/postmaster/pgstat.c      |   3 +
 src/backend/storage/ipc/ipci.c       |   2 +
 src/backend/storage/ipc/procsignal.c |   4 +
 src/backend/tcop/postgres.c          |   4 +
 src/backend/tcop/utility.c           |  13 +
 src/backend/utils/misc/README        |  26 +-
 src/backend/utils/misc/guc.c         | 869 ++++++++++++++++++++++++++++++-----
 src/include/catalog/pg_proc.dat      |  10 +-
 src/include/nodes/nodes.h            |   1 +
 src/include/nodes/parsenodes.h       |  12 +
 src/include/pgstat.h                 |   3 +-
 src/include/storage/procsignal.h     |   3 +
 src/include/utils/guc.h              |  21 +-
 src/include/utils/guc_tables.h       |   5 +-
 src/test/regress/expected/guc.out    | 223 +++++++++
 src/test/regress/sql/guc.sql         |  88 ++++
 26 files changed, 1439 insertions(+), 145 deletions(-)
 create mode 100644 doc/src/sgml/ref/alter_session.sgml

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index b6f5822b84..a52989342a 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -175,6 +175,14 @@ shared_buffers = 128MB
      read whenever <filename>postgresql.conf</filename> is, and its settings take
      effect in the same way.  Settings in <filename>postgresql.auto.conf</filename>
      override those in <filename>postgresql.conf</filename>.
+
+    </para>
+    <para>
+     Furthermore a directory <filename>pg_session_conf</filename> in the
+     PostgreSQL data directory contains per-session configuration files. Every
+     file holds settings provided through the
+     <xref linkend="sql-altersession"/> command. This is read by reloading
+     after the two above files and removed at the session-end.
     </para>
 
     <para>
@@ -195,8 +203,8 @@ shared_buffers = 128MB
       The already-mentioned <xref linkend="sql-altersystem"/> command
       provides a SQL-accessible means of changing global defaults; it is
       functionally equivalent to editing <filename>postgresql.conf</filename>.
-      In addition, there are two commands that allow setting of defaults
-      on a per-database or per-role basis:
+      In addition, there are three commands that allow setting of defaults
+      on a per-database, per-role or per-session basis:
      </para>
 
      <itemizedlist>
@@ -213,6 +221,13 @@ shared_buffers = 128MB
        per-database settings to be overridden with user-specific values.
       </para>
      </listitem>
+
+     <listitem>
+      <para>
+       The <xref linkend="sql-altersession"/> command allows other sessions to
+        override session-local settings with user-specific values.
+      </para>
+     </listitem>
     </itemizedlist>
 
      <para>
@@ -223,6 +238,13 @@ shared_buffers = 128MB
       Note that some settings cannot be changed after server start, and
       so cannot be set with these commands (or the ones listed below).
     </para>
+     <para>
+      Values set with <command>ALTER SESSION</command> are applied only when
+      reloading.  They override values obtained from the configuration files
+      or server command line, and constitute defaults for the rest of the
+      session.  Note that it can change only settings that are changeable
+      on-session.
+    </para>
 
      <para>
       Once a client is connected to the database, <productname>PostgreSQL</productname>
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 4930ec17f6..075e959786 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -18687,6 +18687,20 @@ SELECT collation for ('foo' COLLATE "de_DE");
        <entry><type>text</type></entry>
        <entry>set parameter and return new value</entry>
       </row>
+      <row>
+       <entry>
+        <indexterm>
+         <primary>pg_set_backend_config</primary>
+        </indexterm>
+        <literal><function>pg_set_backend_config(
+                            <parameter>process_id</parameter>,
+                            <parameter>setting_name</parameter>,
+                            <parameter>new_value</parameter>)
+                            </function></literal>
+       </entry>
+       <entry><type>bool</type></entry>
+       <entry>set parameter on another session</entry>
+      </row>
      </tbody>
     </tgroup>
    </table>
@@ -18741,6 +18755,22 @@ SELECT set_config('log_statement_stats', 'off', false);
 ------------
  off
 (1 row)
+</programlisting>
+   </para>
+
+   <para>
+    <function>pg_set_backend_config</function> sets the parameter
+    <parameter>setting_name</parameter> to
+    <parameter>new_value</parameter> on the other session with PID
+    <parameter>process_id</parameter>. The setting is always session-local and
+    returns true if succeeded.  An example:
+<programlisting>
+SELECT pg_set_backend_config(2134, 'work_mem', '16MB');
+
+pg_set_backend_config
+------------
+ t
+(1 row)
 </programlisting>
    </para>
 
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index c81c87ef41..650ae49a24 100644
--- a/doc/src/sgml/ref/allfiles.sgml
+++ b/doc/src/sgml/ref/allfiles.sgml
@@ -34,6 +34,7 @@ Complete list of usable sgml source files in this directory.
 <!ENTITY alterSchema        SYSTEM "alter_schema.sgml">
 <!ENTITY alterServer        SYSTEM "alter_server.sgml">
 <!ENTITY alterSequence      SYSTEM "alter_sequence.sgml">
+<!ENTITY alterSession       SYSTEM "alter_session.sgml">
 <!ENTITY alterSubscription  SYSTEM "alter_subscription.sgml">
 <!ENTITY alterSystem        SYSTEM "alter_system.sgml">
 <!ENTITY alterStatistics    SYSTEM "alter_statistics.sgml">
diff --git a/doc/src/sgml/ref/alter_session.sgml b/doc/src/sgml/ref/alter_session.sgml
new file mode 100644
index 0000000000..803d964091
--- /dev/null
+++ b/doc/src/sgml/ref/alter_session.sgml
@@ -0,0 +1,160 @@
+<!--
+doc/src/sgml/ref/alter_session.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-altersession">
+ <indexterm zone="sql-altersystem">
+  <primary>ALTER SESSION</primary>
+ </indexterm>
+
+ <refmeta>
+  <refentrytitle>ALTER SESSION</refentrytitle>
+  <manvolnum>7</manvolnum>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>ALTER SESSION</refname>
+  <refpurpose>change a server configuration parameter of a remote session</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+ALTER SESSION WITH (pid <replaceable class="parameter">session_pid</replaceable>) SET <replaceable
class="parameter">configuration_parameter</replaceable>{ TO | = } { <replaceable class="parameter">value</replaceable>
|'<replaceable class="parameter">value</replaceable>' | DEFAULT } [ [ WITH ] RELOAD ]
 
+
+ALTER SESSION WITH (pid <replaceable class="parameter">session_pid</replaceable>) RESET <replaceable
class="parameter">configuration_parameter</replaceable>[ [ WITH ] RELOAD ]
 
+ALTER SESSION WITH (pid <replaceable class="parameter">session_pid</replaceable>) RESET ALL [ [ WITH ] RELOAD ]
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+   <command>ALTER SESSION</command> is used for changing server configuration
+   parameters on the specified session. 
+   <command>ALTER SESSION</command> writes the given parameter setting to the
+   file <filename>pg_session_conf/postgresql.<replaceable>beid</replaceable>.conf</filename>
+   file, which is read in addition to <filename>postgresql.conf</filename>
+   and <filename>postgresql.auto.conf</filename>.  Setting a parameter
+   to <literal>DEFAULT</literal>, or using the
+   <command>RESET</command> variant, removes that configuration entry from the
+   session config file. Use <literal>RESET ALL</literal> to remove the
+   parameter file. The session config file lasts until the end of the session.
+  </para>
+
+  <para>
+   Values set with <command>ALTER SESSION</command> will be effective after
+   the next server configuration reload.  A server configuration reload can be
+   commanded by calling the SQL
+   function <function>pg_reload_conf()</function>, running <literal>pg_ctl
+   reload</literal>, sending a <systemitem>SIGHUP</systemitem> signal to the
+   main server process, or specifying <literal>WITH RELOAD</literal>
+   in <command>ALTER SESSION</command>. As <command>ALTER SYSTEM</command>
+   does, <command>ALTER SESSION</command> doesn't override settings changed
+   in-session.
+  </para>
+
+  <para>
+   Only superusers and users who are granted the session user or
+   pg_signal_backend can use <command>ALTER SESSION</command>.  Also, since
+   this command acts directly on the file system and cannot be rolled back, it
+   is not allowed inside a transaction block or function.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Parameters</title>
+
+  <variablelist>
+   <varlistentry>
+    <term><replaceable class="parameter">session_pid</replaceable></term>
+    <listitem>
+     <para>
+      Process ID of the target session.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">configuration_parameter</replaceable></term>
+    <listitem>
+     <para>
+      Name of a settable configuration parameter.  Available parameters are
+      documented in <xref linkend="runtime-config"/>.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">value</replaceable></term>
+    <listitem>
+     <para>
+      New value of the parameter.  Values can be specified as string
+      constants, identifiers, numbers, or comma-separated lists of
+      these, as appropriate for the particular parameter.
+      <literal>DEFAULT</literal> can be written to specify removing the
+      parameter and its value
+      from <filename>pg_session_conf/postgresql.<replaceable>beid</replaceable>.conf</filename>.
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+ </refsect1>
+
+ <refsect1>
+  <title>Notes</title>
+
+  <para>
+   This command can't be used to set <xref linkend="guc-data-directory"/>,
+   parameters that are not allowed in <filename>postgresql.conf</filename>
+   (e.g., <link linkend="runtime-config-preset">preset options</link>), nor
+   parameters that cannot be changed on-session. Only superuser can perform
+   <literal>RESET ALL</literal>.
+  </para>
+
+  <para>
+   See <xref linkend="config-setting"/> for other ways to set the parameters.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Examples</title>
+
+  <para>
+   Set the <literal>wal_level</literal>:
+<programlisting>
+ALTER SESSION WITH (pid 27162) SET track_functions = pl IMMEDIATE;
+</programlisting>
+  </para>
+
+  <para>
+   Undo that, restoring whatever setting was effective
+   in <filename>postgresql.conf</filename>
+   and <filename>postgresql.auto.conf</filename>:
+<programlisting>
+ALTER SESSION WITH (pid 27162) RESET track_functions IMMEDIATE;
+</programlisting></para>
+ </refsect1>
+
+ <refsect1>
+  <title>Compatibility</title>
+
+  <para>
+   The <command>ALTER SESSION</command> statement is a
+   <productname>PostgreSQL</productname> extension.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="sql-altersystem"/></member>
+   <member><xref linkend="sql-set"/></member>
+   <member><xref linkend="sql-show"/></member>
+  </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
index db4f4167e3..85f515e2b2 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -62,6 +62,7 @@
    &alterSchema;
    &alterSequence;
    &alterServer;
+   &alterSession;
    &alterStatistics;
    &alterSubscription;
    &alterSystem;
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 3e229c693c..88ad8ded40 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -474,7 +474,7 @@ CREATE VIEW pg_settings AS
 CREATE RULE pg_settings_u AS
     ON UPDATE TO pg_settings
     WHERE new.name = old.name DO
-    SELECT set_config(old.name, new.setting, 'f');
+    SELECT set_config(old.name, new.setting, 'f', 'f');
 
 CREATE RULE pg_settings_n AS
     ON UPDATE TO pg_settings
@@ -1035,6 +1035,11 @@ CREATE OR REPLACE FUNCTION
   RETURNS boolean STRICT VOLATILE LANGUAGE INTERNAL AS 'pg_promote'
   PARALLEL SAFE;
 
+CREATE OR REPLACE FUNCTION set_config (
+        setting_name text, new_value text, is_local boolean, is_nonxact boolean DEFAULT false)
+        RETURNS text STRICT VOLATILE LANGUAGE internal AS 'set_config_by_name'
+        PARALLEL UNSAFE;
+
 -- legacy definition for compatibility with 9.3
 CREATE OR REPLACE FUNCTION
   json_populate_record(base anyelement, from_json json, use_json_as_text boolean DEFAULT false)
diff --git a/src/backend/commands/discard.c b/src/backend/commands/discard.c
index 23a14586ba..948c0a70f3 100644
--- a/src/backend/commands/discard.c
+++ b/src/backend/commands/discard.c
@@ -68,7 +68,7 @@ DiscardAll(bool isTopLevel)
     /* Closing portals might run user-defined code, so do that first. */
     PortalHashTableDeleteAll();
     SetPGVariable("session_authorization", NIL, false);
-    ResetAllOptions();
+    ResetAllOptions(PGC_S_SESSION);
     DropAllPreparedStatements();
     Async_UnlistenAll();
     LockReleaseAll(USER_LOCKMETHOD, true);
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 807393dfaa..7719b475ae 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3923,6 +3923,17 @@ _copyAlterSystemStmt(const AlterSystemStmt *from)
     return newnode;
 }
 
+static AlterSessionStmt *
+_copyAlterSessionStmt(const AlterSessionStmt *from)
+{
+    AlterSessionStmt *newnode = makeNode(AlterSessionStmt);
+
+    COPY_NODE_FIELD(sessionopt);
+    COPY_NODE_FIELD(setstmt);
+    COPY_SCALAR_FIELD(immediate);
+    return newnode;
+}
+
 static CreateSeqStmt *
 _copyCreateSeqStmt(const CreateSeqStmt *from)
 {
@@ -5341,6 +5352,9 @@ copyObjectImpl(const void *from)
         case T_AlterSystemStmt:
             retval = _copyAlterSystemStmt(from);
             break;
+        case T_AlterSessionStmt:
+            retval = _copyAlterSessionStmt(from);
+            break;
         case T_CreateSeqStmt:
             retval = _copyCreateSeqStmt(from);
             break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index a397de155e..b542d060f8 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1732,6 +1732,15 @@ _equalAlterSystemStmt(const AlterSystemStmt *a, const AlterSystemStmt *b)
     return true;
 }
 
+static bool
+_equalAlterSessionStmt(const AlterSessionStmt *a, const AlterSessionStmt *b)
+{
+    COMPARE_NODE_FIELD(sessionopt);
+    COMPARE_NODE_FIELD(setstmt);
+    COMPARE_SCALAR_FIELD(immediate);
+
+    return true;
+}
 
 static bool
 _equalCreateSeqStmt(const CreateSeqStmt *a, const CreateSeqStmt *b)
@@ -3406,6 +3415,9 @@ equal(const void *a, const void *b)
         case T_AlterSystemStmt:
             retval = _equalAlterSystemStmt(a, b);
             break;
+        case T_AlterSessionStmt:
+            retval = _equalAlterSessionStmt(a, b);
+            break;
         case T_CreateSeqStmt:
             retval = _equalCreateSeqStmt(a, b);
             break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index c1faf4152c..55793b1185 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -248,8 +248,9 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
         AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterEnumStmt
         AlterFdwStmt AlterForeignServerStmt AlterGroupStmt
         AlterObjectDependsStmt AlterObjectSchemaStmt AlterOwnerStmt
-        AlterOperatorStmt AlterSeqStmt AlterSystemStmt AlterTableStmt
-        AlterTblSpcStmt AlterExtensionStmt AlterExtensionContentsStmt AlterForeignTableStmt
+        AlterOperatorStmt AlterSeqStmt AlterSystemStmt AlterSessionStmt
+        AlterTableStmt AlterTblSpcStmt AlterExtensionStmt
+        AlterExtensionContentsStmt AlterForeignTableStmt
         AlterCompositeTypeStmt AlterUserMappingStmt
         AlterRoleStmt AlterRoleSetStmt AlterPolicyStmt
         AlterDefaultPrivilegesStmt DefACLAction
@@ -303,6 +304,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 %type <defelt>    createdb_opt_item copy_opt_item
                 transaction_mode_item
                 create_extension_opt_item alter_extension_opt_item
+                altersess_option_item
 
 %type <ival>    opt_lock lock_type cast_context
 %type <ival>    vacuum_option_list vacuum_option_elem
@@ -668,7 +670,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
     QUOTE
 
     RANGE READ REAL REASSIGN RECHECK RECURSIVE REF REFERENCES REFERENCING
-    REFRESH REINDEX RELATIVE_P RELEASE RENAME REPEATABLE REPLACE REPLICA
+    REFRESH REINDEX RELATIVE_P RELEASE RELOAD RENAME REPEATABLE REPLACE REPLICA
     RESET RESTART RESTRICT RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK ROLLUP
     ROUTINE ROUTINES ROW ROWS RULE
 
@@ -839,6 +841,7 @@ stmt :
             | AlterPolicyStmt
             | AlterSeqStmt
             | AlterSystemStmt
+            | AlterSessionStmt
             | AlterTableStmt
             | AlterTblSpcStmt
             | AlterCompositeTypeStmt
@@ -10169,6 +10172,41 @@ AlterSystemStmt:
         ;
 
 
+/*****************************************************************************
+ *
+ *        ALTER SESSION
+ *
+ * This is used to change configuration parameters persistently.
+ *****************************************************************************/
+
+AlterSessionStmt:
+            ALTER SESSION WITH '(' altersess_option_item ')' SET generic_set
+                {
+                    AlterSessionStmt *n = makeNode(AlterSessionStmt);
+                    n->sessionopt = $5;
+                    n->setstmt = $8;
+                    $$ = (Node *)n;
+                }
+            | ALTER SESSION WITH '(' altersess_option_item ')' RESET generic_reset
+                {
+                    AlterSessionStmt *n = makeNode(AlterSessionStmt);
+                    n->sessionopt = $5;
+                    n->setstmt = $8;
+                    $$ = (Node *)n;
+                }
+        ;
+
+altersess_option_item:
+        ColLabel Iconst
+            {
+                $$ = makeDefElem($1, (Node *)makeInteger($2), @1);
+            }
+        | ColLabel Sconst
+            {
+                $$ = makeDefElem($1, (Node *)makeString($2), @1);
+            }
+    ;
+
 /*****************************************************************************
  *
  * Manipulate a domain
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 81c6499251..c50ce50e67 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -3683,6 +3683,9 @@ pgstat_get_wait_ipc(WaitEventIPC w)
         case WAIT_EVENT_SYNC_REP:
             event_name = "SyncRep";
             break;
+        case WAIT_EVENT_REMOTE_GUC:
+            event_name = "RemoteGUC";
+            break;
             /* no default case, so that compiler will warn */
     }
 
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2849e47d99..044107b354 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -148,6 +148,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port)
         size = add_size(size, BTreeShmemSize());
         size = add_size(size, SyncScanShmemSize());
         size = add_size(size, AsyncShmemSize());
+        size = add_size(size, GucShmemSize());
 #ifdef EXEC_BACKEND
         size = add_size(size, ShmemBackendArraySize());
 #endif
@@ -267,6 +268,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port)
     BTreeShmemInit();
     SyncScanShmemInit();
     AsyncShmemInit();
+    GucShmemInit();
 
 #ifdef EXEC_BACKEND
 
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index 7605b2c367..98c0f84378 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -27,6 +27,7 @@
 #include "storage/shmem.h"
 #include "storage/sinval.h"
 #include "tcop/tcopprot.h"
+#include "utils/guc.h"
 
 
 /*
@@ -292,6 +293,9 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
     if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN))
         RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
 
+    if (CheckProcSignal(PROCSIG_REMOTE_GUC))
+        HandleRemoteGucSetInterrupt();
+
     SetLatch(MyLatch);
 
     latch_sigusr1_handler();
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 36cfd507b2..363275e302 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -4228,6 +4228,10 @@ PostgresMain(int argc, char *argv[],
             ProcessConfigFile(PGC_SIGHUP);
         }
 
+        if (RemoteGucChangePending)
+            HandleGucRemoteChanges();
+
+
         /*
          * (7) process the command.  But ignore it if we're skipping till
          * Sync.
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 6ec795f1b4..e0c1e293d1 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -681,6 +681,11 @@ standard_ProcessUtility(PlannedStmt *pstmt,
             AlterSystemSetConfigFile((AlterSystemStmt *) parsetree);
             break;
 
+        case T_AlterSessionStmt:
+            PreventInTransactionBlock(isTopLevel, "ALTER SESSION");
+            AlterSessionSetRemoteConfig(pstate, (AlterSessionStmt *) parsetree);
+            break;
+
         case T_VariableSetStmt:
             ExecSetVariableStmt((VariableSetStmt *) parsetree, isTopLevel);
             break;
@@ -2605,6 +2610,10 @@ CreateCommandTag(Node *parsetree)
             tag = "ALTER SYSTEM";
             break;
 
+        case T_AlterSessionStmt:
+            tag = "ALTER SESSION";
+            break;
+
         case T_VariableSetStmt:
             switch (((VariableSetStmt *) parsetree)->kind)
             {
@@ -3235,6 +3244,10 @@ GetCommandLogLevel(Node *parsetree)
             lev = LOGSTMT_DDL;
             break;
 
+        case T_AlterSessionStmt:
+            lev = LOGSTMT_DDL;
+            break;
+
         case T_VariableSetStmt:
             lev = LOGSTMT_ALL;
             break;
diff --git a/src/backend/utils/misc/README b/src/backend/utils/misc/README
index 6e294386f7..42ae6c1a8f 100644
--- a/src/backend/utils/misc/README
+++ b/src/backend/utils/misc/README
@@ -169,10 +169,14 @@ Entry to a function with a SET option:
 Plain SET command:
 
     If no stack entry of current level:
-        Push new stack entry w/prior value and state SET
+        Push new stack entry w/prior value and state SET or
+        push new stack entry w/o value and state NONXACT.
     else if stack entry's state is SAVE, SET, or LOCAL:
         change stack state to SET, don't change saved value
         (here we are forgetting effects of prior set action)
+    else if stack entry's state is NONXACT:
+        change stack state to NONXACT_SET, set the current value to
+        prior.
     else (entry must have state SET+LOCAL):
         discard its masked value, change state to SET
         (here we are forgetting effects of prior SET and SET LOCAL)
@@ -185,13 +189,20 @@ SET LOCAL command:
     else if stack entry's state is SAVE or LOCAL or SET+LOCAL:
         no change to stack entry
         (in SAVE case, SET LOCAL will be forgotten at func exit)
+    else if stack entry's state is NONXACT:
+        set current value to both prior and masked slots. set state
+        NONXACT+LOCAL.
     else (entry must have state SET):
         put current active into its masked slot, set state SET+LOCAL
     Now set new value.
 
+Setting by NONXACT action (no command exists):
+    Always blow away existing stack then create a new NONXACT entry.    
+
 Transaction or subtransaction abort:
 
-    Pop stack entries, restoring prior value, until top < subxact depth
+    Pop stack entries, restoring prior value unless the stack entry's
+    state is NONXACT, until top < subxact depth
 
 Transaction or subtransaction commit (incl. successful function exit):
 
@@ -199,9 +210,9 @@ Transaction or subtransaction commit (incl. successful function exit):
 
         if entry's state is SAVE:
             pop, restoring prior value
-        else if level is 1 and entry's state is SET+LOCAL:
+        else if level is 1 and entry's state is SET+LOCAL or NONXACT+LOCAL:
             pop, restoring *masked* value
-        else if level is 1 and entry's state is SET:
+        else if level is 1 and entry's state is SET or NONXACT+SET:
             pop, discarding old value
         else if level is 1 and entry's state is LOCAL:
             pop, restoring prior value
@@ -210,9 +221,9 @@ Transaction or subtransaction commit (incl. successful function exit):
         else
             merge entries of level N-1 and N as specified below
 
-The merged entry will have level N-1 and prior = older prior, so easiest
-to keep older entry and free newer.  There are 12 possibilities since
-we already handled level N state = SAVE:
+The merged entry will have level N-1 and prior = older prior, so
+easiest to keep older entry and free newer.  Disregarding to NONXACT,
+here are 12 possibilities since we already handled level N state = SAVE:
 
 N-1        N
 
@@ -232,6 +243,7 @@ SET+LOCAL    SET        discard top prior and second masked, state SET
 SET+LOCAL    LOCAL        discard top prior, no change to stack entry
 SET+LOCAL    SET+LOCAL    discard top prior, copy masked, state S+L
 
+(TODO: states involving NONXACT)
 
 RESET is executed like a SET, but using the reset_val as the desired new
 value.  (We do not provide a RESET LOCAL command, but SET LOCAL TO DEFAULT
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 98d75be292..e4b00b83e2 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -36,6 +36,7 @@
 #include "catalog/namespace.h"
 #include "catalog/pg_authid.h"
 #include "commands/async.h"
+#include "commands/defrem.h"
 #include "commands/prepare.h"
 #include "commands/user.h"
 #include "commands/vacuum.h"
@@ -70,12 +71,14 @@
 #include "replication/walreceiver.h"
 #include "replication/walsender.h"
 #include "storage/bufmgr.h"
+#include "storage/condition_variable.h"
 #include "storage/dsm_impl.h"
 #include "storage/standby.h"
 #include "storage/fd.h"
 #include "storage/large_object.h"
 #include "storage/pg_shmem.h"
 #include "storage/proc.h"
+#include "storage/procarray.h"
 #include "storage/predicate.h"
 #include "tcop/tcopprot.h"
 #include "tsearch/ts_cache.h"
@@ -211,12 +214,41 @@ static bool check_recovery_target_lsn(char **newval, void **extra, GucSource sou
 static void assign_recovery_target_lsn(const char *newval, void *extra);
 static bool check_primary_slot_name(char **newval, void **extra, GucSource source);
 static bool check_default_with_oids(bool *newval, void **extra, GucSource source);
-
+static bool set_backend_config_internal(int target_pid, uint64 target_ts, char *name, char *value);
 /* Private functions in guc-file.l that need to be called from guc.c */
 static ConfigVariable *ProcessConfigFileInternal(GucContext context,
                           bool applySettings, int elevel);
 
 
+#define GUC_REMOTE_MAX_VALUE_LEN  1024        /* an arbitrary value */
+
+typedef struct GucPendingSetting
+{
+    char        name[NAMEDATALEN];
+    GucContext    context;
+    bool        reset;
+    bool        reset_all;
+    char       *value;
+    int            source_pid;
+} GucPendingSetting;
+
+typedef struct
+{
+    bool        busy;
+    slock_t        mutex;
+    ConditionVariable    busy_cv;
+    int            target_pid;
+    uint64        target_start_timestamp;
+    char        value[GUC_REMOTE_MAX_VALUE_LEN];
+    GucPendingSetting setting;
+    volatile Latch *sender_latch;
+} GucRemoteSetting;
+
+static MemoryContext RemoteConfigMemoryContext = NULL;
+static GucRemoteSetting *remote_setting = NULL;
+static HTAB *pending_setting = NULL;
+volatile bool RemoteGucChangePending = false;
+
 /*
  * Options for enum values defined in this module.
  *
@@ -605,7 +637,8 @@ const char *const GucSource_Names[] =
      /* PGC_S_OVERRIDE */ "override",
      /* PGC_S_INTERACTIVE */ "interactive",
      /* PGC_S_TEST */ "test",
-     /* PGC_S_SESSION */ "session"
+     /* PGC_S_SESSION */ "session",
+     /* PGC_S_REMOTE */ "remote session"
 };
 
 /*
@@ -4452,10 +4485,8 @@ static const char *const map_old_guc_names[] = {
  * Actual lookup of variables is done through this single, sorted array.
  */
 static struct config_generic **guc_variables;
-
 /* Current number of variables contained in the vector */
 static int    num_guc_variables;
-
 /* Vector capacity */
 static int    size_guc_variables;
 
@@ -5380,7 +5411,7 @@ SelectConfigFiles(const char *userDoption, const char *progname)
  * Reset all options to their saved default values (implements RESET ALL)
  */
 void
-ResetAllOptions(void)
+ResetAllOptions(GucSource src)
 {
     int            i;
 
@@ -5396,7 +5427,8 @@ ResetAllOptions(void)
         if (gconf->flags & GUC_NO_RESET_ALL)
             continue;
         /* No need to reset if wasn't SET */
-        if (gconf->source <= PGC_S_OVERRIDE)
+        Assert(PGC_S_OVERRIDE < src);
+        if (gconf->source != src)
             continue;
 
         /* Save old value to support transaction abort */
@@ -5490,6 +5522,22 @@ push_old_value(struct config_generic *gconf, GucAction action)
 
     /* Do we already have a stack entry of the current nest level? */
     stack = gconf->stack;
+
+    /* NONXACT action make existing stack useles */
+    if (action == GUC_ACTION_NONXACT)
+    {
+        while (stack)
+        {
+            GucStack *prev = stack->prev;
+
+            discard_stack_value(gconf, &stack->prior);
+            discard_stack_value(gconf, &stack->masked);
+            pfree(stack);
+            stack = prev;
+        }
+        stack = gconf->stack = NULL;
+    }
+
     if (stack && stack->nest_level >= GUCNestLevel)
     {
         /* Yes, so adjust its state if necessary */
@@ -5497,28 +5545,63 @@ push_old_value(struct config_generic *gconf, GucAction action)
         switch (action)
         {
             case GUC_ACTION_SET:
-                /* SET overrides any prior action at same nest level */
-                if (stack->state == GUC_SET_LOCAL)
+                if (stack->state == GUC_NONXACT)
                 {
-                    /* must discard old masked value */
-                    discard_stack_value(gconf, &stack->masked);
+                    /* NONXACT rollbacks to the current value */
+                    stack->scontext = gconf->scontext;
+                    set_stack_value(gconf, &stack->prior);
+                    stack->state = GUC_NONXACT_SET;
                 }
-                stack->state = GUC_SET;
+                else 
+                {
+                    /* SET overrides other prior actions at same nest level */
+                    if (stack->state == GUC_SET_LOCAL)
+                    {
+                        /* must discard old masked value */
+                        discard_stack_value(gconf, &stack->masked);
+                    }
+                    stack->state = GUC_SET;
+                }
+
                 break;
+
             case GUC_ACTION_LOCAL:
                 if (stack->state == GUC_SET)
                 {
-                    /* SET followed by SET LOCAL, remember SET's value */
+                    /* SET followed by SET LOCAL, remember it's value */
                     stack->masked_scontext = gconf->scontext;
                     set_stack_value(gconf, &stack->masked);
                     stack->state = GUC_SET_LOCAL;
                 }
+                else if (stack->state == GUC_NONXACT)
+                {
+                    /*
+                     * NONXACT followed by SET LOCAL, both prior and masked
+                     * are set to the current value
+                     */
+                    stack->scontext = gconf->scontext;
+                    set_stack_value(gconf, &stack->prior);
+                    stack->masked_scontext = stack->scontext;
+                    stack->masked = stack->prior;
+                    stack->state = GUC_NONXACT_LOCAL;
+                }
+                else if (stack->state == GUC_NONXACT_SET)
+                {
+                    /* NONXACT_SET followed by SET LOCAL, set masked */
+                    stack->masked_scontext = gconf->scontext;
+                    set_stack_value(gconf, &stack->masked);
+                    stack->state = GUC_NONXACT_LOCAL;
+                }
                 /* in all other cases, no change to stack entry */
                 break;
             case GUC_ACTION_SAVE:
                 /* Could only have a prior SAVE of same variable */
                 Assert(stack->state == GUC_SAVE);
                 break;
+
+            case GUC_ACTION_NONXACT:
+                Assert(false);
+                break;
         }
         Assert(guc_dirty);        /* must be set already */
         return;
@@ -5534,6 +5617,7 @@ push_old_value(struct config_generic *gconf, GucAction action)
 
     stack->prev = gconf->stack;
     stack->nest_level = GUCNestLevel;
+        
     switch (action)
     {
         case GUC_ACTION_SET:
@@ -5545,10 +5629,15 @@ push_old_value(struct config_generic *gconf, GucAction action)
         case GUC_ACTION_SAVE:
             stack->state = GUC_SAVE;
             break;
+        case GUC_ACTION_NONXACT:
+            stack->state = GUC_NONXACT;
+            break;
     }
     stack->source = gconf->source;
     stack->scontext = gconf->scontext;
-    set_stack_value(gconf, &stack->prior);
+
+    if (action != GUC_ACTION_NONXACT)
+        set_stack_value(gconf, &stack->prior);
 
     gconf->stack = stack;
 
@@ -5643,22 +5732,31 @@ AtEOXact_GUC(bool isCommit, int nestLevel)
              * stack entries to avoid leaking memory.  If we do set one of
              * those flags, unused fields will be cleaned up after restoring.
              */
-            if (!isCommit)        /* if abort, always restore prior value */
-                restorePrior = true;
+            if (!isCommit)
+            {
+                /* GUC_NONXACT does't rollback */
+                if (stack->state != GUC_NONXACT)
+                    restorePrior = true;
+            }
             else if (stack->state == GUC_SAVE)
                 restorePrior = true;
             else if (stack->nest_level == 1)
             {
                 /* transaction commit */
-                if (stack->state == GUC_SET_LOCAL)
+                if (stack->state == GUC_SET_LOCAL ||
+                    stack->state == GUC_NONXACT_LOCAL)
                     restoreMasked = true;
-                else if (stack->state == GUC_SET)
+                else if (stack->state == GUC_SET ||
+                         stack->state == GUC_NONXACT_SET)
                 {
                     /* we keep the current active value */
                     discard_stack_value(gconf, &stack->prior);
                 }
-                else            /* must be GUC_LOCAL */
+                else if (stack->state != GUC_NONXACT)
+                {
+                    /* must be GUC_LOCAL */
                     restorePrior = true;
+                }
             }
             else if (prev == NULL ||
                      prev->nest_level < stack->nest_level - 1)
@@ -5680,11 +5778,27 @@ AtEOXact_GUC(bool isCommit, int nestLevel)
                         break;
 
                     case GUC_SET:
-                        /* next level always becomes SET */
-                        discard_stack_value(gconf, &stack->prior);
-                        if (prev->state == GUC_SET_LOCAL)
+                        if (prev->state == GUC_SET ||
+                            prev->state == GUC_NONXACT_SET)
+                        {
+                            discard_stack_value(gconf, &stack->prior);
+                        }
+                        else if (prev->state == GUC_NONXACT)
+                        {
+                            prev->scontext = stack->scontext;
+                            prev->prior = stack->prior;
+                            prev->state = GUC_NONXACT_SET;
+                        }
+                        else if (prev->state == GUC_SET_LOCAL ||
+                                 prev->state == GUC_NONXACT_LOCAL)
+                        {
+                            discard_stack_value(gconf, &stack->prior);
                             discard_stack_value(gconf, &prev->masked);
-                        prev->state = GUC_SET;
+                            if (prev->state == GUC_SET_LOCAL)
+                                prev->state = GUC_SET;
+                            else
+                                prev->state = GUC_NONXACT_SET;
+                        }
                         break;
 
                     case GUC_LOCAL:
@@ -5695,6 +5809,16 @@ AtEOXact_GUC(bool isCommit, int nestLevel)
                             prev->masked = stack->prior;
                             prev->state = GUC_SET_LOCAL;
                         }
+                        else if (prev->state == GUC_NONXACT)
+                        {
+                            prev->prior = stack->masked;
+                            prev->scontext = stack->masked_scontext;
+                            prev->masked = stack->masked;
+                            prev->masked_scontext = stack->masked_scontext;
+                            discard_stack_value(gconf, &stack->prior);
+                            discard_stack_value(gconf, &stack->masked);
+                            prev->state = GUC_NONXACT_SET;
+                        }
                         else
                         {
                             /* else just forget this stack level */
@@ -5703,15 +5827,32 @@ AtEOXact_GUC(bool isCommit, int nestLevel)
                         break;
 
                     case GUC_SET_LOCAL:
-                        /* prior state at this level no longer wanted */
-                        discard_stack_value(gconf, &stack->prior);
-                        /* copy down the masked state */
-                        prev->masked_scontext = stack->masked_scontext;
-                        if (prev->state == GUC_SET_LOCAL)
-                            discard_stack_value(gconf, &prev->masked);
-                        prev->masked = stack->masked;
-                        prev->state = GUC_SET_LOCAL;
+                        if (prev->state == GUC_NONXACT)
+                        {
+                            prev->prior = stack->prior;
+                            prev->masked = stack->prior;
+                            discard_stack_value(gconf, &stack->prior);
+                            discard_stack_value(gconf, &stack->masked);
+                            prev->state = GUC_NONXACT_SET;
+                        }
+                        else if (prev->state != GUC_NONXACT_SET)
+                        {
+                            /* prior state at this level no longer wanted */
+                            discard_stack_value(gconf, &stack->prior);
+                            /* copy down the masked state */
+                            prev->masked_scontext = stack->masked_scontext;
+                            if (prev->state == GUC_SET_LOCAL)
+                                discard_stack_value(gconf, &prev->masked);
+                            prev->masked = stack->masked;
+                            prev->state = GUC_SET_LOCAL;
+                        }
                         break;
+                    case GUC_NONXACT:
+                    case GUC_NONXACT_SET:
+                    case GUC_NONXACT_LOCAL:
+                        Assert(false);
+                        break;
+                        
                 }
             }
 
@@ -6451,6 +6592,8 @@ set_config_option(const char *name, const char *value,
                  source == PGC_S_USER ||
                  source == PGC_S_DATABASE_USER)
             elevel = WARNING;
+        else if (source == PGC_S_REMOTE)
+            elevel = NOTICE;
         else
             elevel = ERROR;
     }
@@ -6654,8 +6797,9 @@ set_config_option(const char *name, const char *value,
      * trying to find out if the value is potentially good, not actually use
      * it. Also keep going if makeDefault is true, since we may want to set
      * the reset/stacked values even if we can't set the variable itself.
+     * Interructive sources are mutually overwritable.
      */
-    if (record->source > source)
+    if (record->source > source && source < PGC_S_INTERACTIVE)
     {
         if (changeVal && !makeDefault)
         {
@@ -7598,6 +7742,110 @@ replace_auto_config_value(ConfigVariable **head_p, ConfigVariable **tail_p,
     *tail_p = item;
 }
 
+/*
+  Check and process the given set statment of ALTER SYSTEM and ALTER SESSION.
+ */
+static void
+AlterConfigProcessSetCommand(VariableSetStmt *setstmt,
+                             bool is_session, bool is_superuser,
+                             char **name, char **value, bool *resetall)
+{
+    *name = setstmt->name;
+
+    switch (setstmt->kind)
+    {
+        case VAR_SET_VALUE:
+            *value = ExtractSetVariableArgs(setstmt);
+            break;
+
+        case VAR_SET_DEFAULT:
+        case VAR_RESET:
+            *value = NULL;
+            break;
+
+        case VAR_RESET_ALL:
+            *value = NULL;
+            *resetall = true;
+            break;
+
+        default:
+            elog(ERROR, "unrecognized set type: %d",
+                 setstmt->kind);
+            break;
+    }
+
+    /*
+     * Unless it's RESET_ALL, validate the target variable and value
+     */
+    if (!*resetall)
+    {
+        struct config_generic *record;
+
+        record = find_option(*name, false, ERROR);
+        if (record == NULL)
+            ereport(ERROR,
+                    (errcode(ERRCODE_UNDEFINED_OBJECT),
+                     errmsg("unrecognized configuration parameter \"%s\"",
+                            *name)));
+
+        /*
+         * Don't allow parameters that can't be set in configuration files to
+         * be set in PG_AUTOCONF_FILENAME file.
+         */
+
+        /* PG_SUSET is changebale but only by superusers  */
+        if (is_session && !is_superuser && record->context == PGC_SUSET)
+                ereport(ERROR,
+                        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                         errmsg("permission denied to set parameter \"%s\"",
+                                *name)));
+
+        /* ALTER SESSION allows only parameters changeabe on-session */
+        if ((is_session &&
+             (record->context != PGC_USERSET &&
+              record->context != PGC_SUSET)) ||
+            record->context == PGC_INTERNAL ||
+            (record->flags & GUC_DISALLOW_IN_FILE) ||
+            (record->flags & GUC_DISALLOW_IN_AUTO_FILE))
+            ereport(ERROR,
+                    (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
+                     errmsg("parameter \"%s\" cannot be changed",
+                            *name)));
+
+        /*
+         * If a value is specified, verify that it's sane.
+         */
+        if (*value)
+        {
+            union config_var_val newval;
+            void       *newextra = NULL;
+
+            /* Check that it's acceptable for the indicated parameter */
+            if (!parse_and_validate_value(record, *name, *value,
+                                          PGC_S_FILE, ERROR,
+                                          &newval, &newextra))
+                ereport(ERROR,
+                        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                         errmsg("invalid value for parameter \"%s\": \"%s\"",
+                                *name, *value)));
+
+            if (record->vartype == PGC_STRING && newval.stringval != NULL)
+                free(newval.stringval);
+            if (newextra)
+                free(newextra);
+
+            /*
+             * We must also reject values containing newlines, because the
+             * grammar for config files doesn't support embedded newlines in
+             * string literals.
+             */
+            if (strchr(*value, '\n'))
+                ereport(ERROR,
+                        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                         errmsg("parameter value for SET must not contain a newline")));
+        }
+    }
+}
 
 /*
  * Execute ALTER SYSTEM statement.
@@ -7631,89 +7879,8 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
     /*
      * Extract statement arguments
      */
-    name = altersysstmt->setstmt->name;
-
-    switch (altersysstmt->setstmt->kind)
-    {
-        case VAR_SET_VALUE:
-            value = ExtractSetVariableArgs(altersysstmt->setstmt);
-            break;
-
-        case VAR_SET_DEFAULT:
-        case VAR_RESET:
-            value = NULL;
-            break;
-
-        case VAR_RESET_ALL:
-            value = NULL;
-            resetall = true;
-            break;
-
-        default:
-            elog(ERROR, "unrecognized alter system stmt type: %d",
-                 altersysstmt->setstmt->kind);
-            break;
-    }
-
-    /*
-     * Unless it's RESET_ALL, validate the target variable and value
-     */
-    if (!resetall)
-    {
-        struct config_generic *record;
-
-        record = find_option(name, false, ERROR);
-        if (record == NULL)
-            ereport(ERROR,
-                    (errcode(ERRCODE_UNDEFINED_OBJECT),
-                     errmsg("unrecognized configuration parameter \"%s\"",
-                            name)));
-
-        /*
-         * Don't allow parameters that can't be set in configuration files to
-         * be set in PG_AUTOCONF_FILENAME file.
-         */
-        if ((record->context == PGC_INTERNAL) ||
-            (record->flags & GUC_DISALLOW_IN_FILE) ||
-            (record->flags & GUC_DISALLOW_IN_AUTO_FILE))
-            ereport(ERROR,
-                    (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
-                     errmsg("parameter \"%s\" cannot be changed",
-                            name)));
-
-        /*
-         * If a value is specified, verify that it's sane.
-         */
-        if (value)
-        {
-            union config_var_val newval;
-            void       *newextra = NULL;
-
-            /* Check that it's acceptable for the indicated parameter */
-            if (!parse_and_validate_value(record, name, value,
-                                          PGC_S_FILE, ERROR,
-                                          &newval, &newextra))
-                ereport(ERROR,
-                        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                         errmsg("invalid value for parameter \"%s\": \"%s\"",
-                                name, value)));
-
-            if (record->vartype == PGC_STRING && newval.stringval != NULL)
-                free(newval.stringval);
-            if (newextra)
-                free(newextra);
-
-            /*
-             * We must also reject values containing newlines, because the
-             * grammar for config files doesn't support embedded newlines in
-             * string literals.
-             */
-            if (strchr(value, '\n'))
-                ereport(ERROR,
-                        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                         errmsg("parameter value for ALTER SYSTEM must not contain a newline")));
-        }
-    }
+    AlterConfigProcessSetCommand(altersysstmt->setstmt, false, false,
+                                 &name, &value, &resetall);
 
     /*
      * PG_AUTOCONF_FILENAME and its corresponding temporary file are always in
@@ -7822,6 +7989,53 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
     LWLockRelease(AutoFileLock);
 }
 
+void
+AlterSessionSetRemoteConfig(ParseState *pstate, AlterSessionStmt *altersesstmt)
+{
+    int        target_pid;
+    char   *name;
+    char   *value;
+    bool    resetall = false;
+    DefElem       *def;
+    uint64    target_ts = 0;
+
+    def = altersesstmt->sessionopt;
+
+    /* ignoring namespace */
+    if (strcmp(def->defname, "pid") == 0)
+        target_pid = defGetInt32(def);
+    else if (strcmp(def->defname, "id") == 0)
+    {
+        /* Heavily WIP!! */
+        char *id = pstrdup(defGetString(def));
+        unsigned long     start;
+        int     n;
+
+        n = sscanf(id, "%lx.%x", &start, &target_pid);
+        if (n != 2)
+            ereport(ERROR,
+                    (errmsg ("malformed session id"),
+                     parser_errposition(pstate, def->location)));
+        target_ts = time_t_to_timestamptz(start);
+    }
+    else
+        ereport(ERROR,
+                (errmsg ("only pid or id is allowed here"),
+                 parser_errposition(pstate, def->location)));
+
+    
+    /*
+     * Extract statement arguments
+     */
+    AlterConfigProcessSetCommand(altersesstmt->setstmt, false, false,
+                                 &name, &value, &resetall);
+
+    if (resetall)
+        name = NULL;
+
+    set_backend_config_internal(target_pid, target_ts, name, value);
+}
+
 /*
  * SET command
  */
@@ -7938,7 +8152,7 @@ ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
                                      action, true, 0, false);
             break;
         case VAR_RESET_ALL:
-            ResetAllOptions();
+            ResetAllOptions(PGC_S_SESSION);
             break;
     }
 }
@@ -7992,7 +8206,8 @@ set_config_by_name(PG_FUNCTION_ARGS)
     char       *name;
     char       *value;
     char       *new_value;
-    bool        is_local;
+    int            set_action = GUC_ACTION_SET;
+
 
     if (PG_ARGISNULL(0))
         ereport(ERROR,
@@ -8012,18 +8227,27 @@ set_config_by_name(PG_FUNCTION_ARGS)
      * Get the desired state of is_local. Default to false if provided value
      * is NULL
      */
-    if (PG_ARGISNULL(2))
-        is_local = false;
-    else
-        is_local = PG_GETARG_BOOL(2);
+    if (!PG_ARGISNULL(2) && PG_GETARG_BOOL(2))
+        set_action = GUC_ACTION_LOCAL;
+
+    /*
+     * Get the desired state of is_nonxact. Default to false if provided value
+     * is NULL
+     */
+    if (!PG_ARGISNULL(3) && PG_GETARG_BOOL(3))
+    {
+        if (set_action == GUC_ACTION_LOCAL)
+            ereport(ERROR,
+                    (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                     errmsg("Only one of is_local and is_nonxact can be true")));
+        set_action = GUC_ACTION_NONXACT;
+    }
 
     /* Note SET DEFAULT (argstring == NULL) is equivalent to RESET */
     (void) set_config_option(name,
                              value,
                              (superuser() ? PGC_SUSET : PGC_USERSET),
-                             PGC_S_SESSION,
-                             is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET,
-                             true, 0, false);
+                             PGC_S_SESSION, set_action, true, 0, false);
 
     /* get the new current value */
     new_value = GetConfigOptionByName(name, NULL, false);
@@ -8032,7 +8256,6 @@ set_config_by_name(PG_FUNCTION_ARGS)
     PG_RETURN_TEXT_P(cstring_to_text(new_value));
 }
 
-
 /*
  * Common code for DefineCustomXXXVariable subroutines: allocate the
  * new variable's config struct and fill in generic fields.
@@ -8231,6 +8454,13 @@ reapply_stacked_values(struct config_generic *variable,
                                          WARNING, false);
                 break;
 
+            case GUC_NONXACT:
+                (void) set_config_option(name, curvalue,
+                                         curscontext, cursource,
+                                         GUC_ACTION_NONXACT, true,
+                                         WARNING, false);
+                break;
+
             case GUC_LOCAL:
                 (void) set_config_option(name, curvalue,
                                          curscontext, cursource,
@@ -8250,6 +8480,33 @@ reapply_stacked_values(struct config_generic *variable,
                                          GUC_ACTION_LOCAL, true,
                                          WARNING, false);
                 break;
+
+            case GUC_NONXACT_SET:
+                /* first, apply the masked value as SET */
+                (void) set_config_option(name, stack->masked.val.stringval,
+                                         stack->masked_scontext, PGC_S_SESSION,
+                                         GUC_ACTION_NONXACT, true,
+                                         WARNING, false);
+                /* then apply the current value as LOCAL */
+                (void) set_config_option(name, curvalue,
+                                         curscontext, cursource,
+                                         GUC_ACTION_SET, true,
+                                         WARNING, false);
+                break;
+
+            case GUC_NONXACT_LOCAL:
+                /* first, apply the masked value as SET */
+                (void) set_config_option(name, stack->masked.val.stringval,
+                                         stack->masked_scontext, PGC_S_SESSION,
+                                         GUC_ACTION_NONXACT, true,
+                                         WARNING, false);
+                /* then apply the current value as LOCAL */
+                (void) set_config_option(name, curvalue,
+                                         curscontext, cursource,
+                                         GUC_ACTION_LOCAL, true,
+                                         WARNING, false);
+                break;
+
         }
 
         /* If we successfully made a stack entry, adjust its nest level */
@@ -10228,6 +10485,370 @@ GUCArrayReset(ArrayType *array)
     return newarray;
 }
 
+Size
+GucShmemSize(void)
+{
+    Size size;
+
+    size = sizeof(GucRemoteSetting);
+
+    return size;
+}
+
+void
+GucShmemInit(void)
+{
+    Size    size;
+    bool    found;
+
+    size = sizeof(GucRemoteSetting);
+    remote_setting = (GucRemoteSetting *)
+        ShmemInitStruct("GUC remote setting", size, &found);
+
+    if (!found)
+    {
+        MemSet(remote_setting, 0, size);
+        SpinLockInit(&remote_setting->mutex);
+        ConditionVariableInit(&remote_setting->busy_cv);
+    }
+}
+
+/*
+ * set_backend_config: SQL callable function to set GUC variable of remote
+ * session.
+ */
+Datum
+set_backend_config(PG_FUNCTION_ARGS)
+{
+    int        target_pid    = PG_GETARG_INT32(0);
+    char   *name;
+    char   *value = NULL;
+
+    if (PG_ARGISNULL(0) || PG_ARGISNULL(1))
+        ereport(ERROR,
+                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                 errmsg("invalid argument")));
+    name = text_to_cstring(PG_GETARG_TEXT_P(1));
+
+    if (!PG_ARGISNULL(2))
+        value = text_to_cstring(PG_GETARG_TEXT_P(2));
+
+    PG_RETURN_BOOL(set_backend_config_internal(target_pid, 0, name, value));
+}
+
+static bool
+set_backend_config_internal(int target_pid,    uint64 target_ts, char *name, char *value)
+{
+    PGPROC *target_proc;
+    PGPROC *proc;
+    uint64    target_timestamp;
+    bool    resetall = (name == NULL);
+    struct config_generic *record;
+
+    if (!resetall)
+    {
+        if (strlen(name) >= NAMEDATALEN)
+            ereport(ERROR,
+                    (errcode(ERRCODE_NAME_TOO_LONG),
+                     errmsg("name of GUC variable is too long")));
+        if (value && strlen(value) >= GUC_REMOTE_MAX_VALUE_LEN)
+            ereport(ERROR,
+                    (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                     errmsg("value is too long"),
+                     errdetail("Maximum acceptable length of value is %d",
+                               GUC_REMOTE_MAX_VALUE_LEN - 1)));
+    }
+
+    target_proc = BackendPidGetProc(target_pid);
+
+    /* target pid is coorect ? */
+    if (!target_proc)
+        ereport(ERROR,
+                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                 errmsg("PID %d is not a PostgreSQL server process",
+                        target_pid)));
+
+    /* XXXXX */
+    if (target_ts > 0 &&
+        target_proc->starttimestamp / 1000000 != target_ts / 1000000)
+        ereport(ERROR,
+                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                 errmsg("not a PostgreSQL server process")));
+
+    /* save timestamp for later consistency check */
+    target_timestamp = target_proc->starttimestamp;
+
+    /* The same condition to pg_signal_backend() */
+    if ((superuser_arg(target_proc->roleId) && !superuser()) ||
+        (!has_privs_of_role(GetUserId(), target_proc->roleId) &&
+         !has_privs_of_role(GetUserId(), DEFAULT_ROLE_SIGNAL_BACKENDID)))
+        ereport(ERROR,
+                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                 errmsg("insufficient privileges for the session")));
+
+    Assert (target_proc->backendId != InvalidBackendId);
+
+    if (!resetall)
+    {
+        record = find_option(name, false, ERROR);
+        if (record == NULL)
+            ereport(ERROR,
+                    (errcode(ERRCODE_UNDEFINED_OBJECT),
+                     errmsg("unrecognized configuration parameter \"%s\"",
+                            name)));
+
+        /* PG_SUSET is changebale but only by superusers  */
+        if (!superuser() && record->context == PGC_SUSET)
+            ereport(ERROR,
+                    (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                     errmsg("permission denied to set parameter \"%s\"",
+                            name)));
+
+        if ((record->context != PGC_USERSET && record->context != PGC_SUSET) ||
+            record->context == PGC_INTERNAL ||
+            (record->flags & GUC_DISALLOW_IN_FILE) ||
+            (record->flags & GUC_DISALLOW_IN_AUTO_FILE))
+            ereport(ERROR,
+                    (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
+                     errmsg("parameter \"%s\" cannot be changed",
+                            name)));
+
+        /*
+         * If a value is specified, verify that it's sane.
+         */
+        if (value)
+        {
+        union config_var_val newval;
+        void       *newextra = NULL;
+
+        /* Check that it's acceptable for the indicated parameter */
+        if (!parse_and_validate_value(record, name, value,
+                                      PGC_S_FILE, ERROR,
+                                      &newval, &newextra))
+            ereport(ERROR,
+                    (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                     errmsg("invalid value for parameter \"%s\": \"%s\"",
+                            name, value)));
+
+        if (record->vartype == PGC_STRING && newval.stringval != NULL)
+            free(newval.stringval);
+        if (newextra)
+            free(newextra);
+
+        /*
+         * We must also reject values containing newlines, because the
+         * grammar for config files doesn't support embedded newlines in
+         * string literals.
+         */
+        if (strchr(value, '\n'))
+            ereport(ERROR,
+                    (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                     errmsg("parameter value for SET must not contain a newline")));
+        }
+    }
+
+    /* Wait for another user to finish its work if any */
+    SpinLockAcquire(&remote_setting->mutex);
+    while (remote_setting->busy)
+    {
+        SpinLockRelease(&remote_setting->mutex);
+        ConditionVariableSleep(&remote_setting->busy_cv, WAIT_EVENT_REMOTE_GUC);
+        SpinLockAcquire(&remote_setting->mutex);
+    }
+
+    /* my turn, send a request */
+    Assert(!remote_setting->busy);
+
+    remote_setting->busy = true;
+    SpinLockRelease(&remote_setting->mutex);
+    remote_setting->target_pid = target_pid;
+    remote_setting->setting.source_pid = MyProcPid;
+    remote_setting->setting.context = superuser() ? PGC_SUSET : PGC_USERSET;
+
+    if (!resetall)
+    {
+        strncpy(remote_setting->setting.name, name, NAMEDATALEN);
+        remote_setting->setting.name[NAMEDATALEN - 1] = 0;
+        if (value)
+        {
+            strncpy(remote_setting->value, value, GUC_REMOTE_MAX_VALUE_LEN);
+            remote_setting->value[GUC_REMOTE_MAX_VALUE_LEN - 1] = 0;
+            remote_setting->setting.value = NULL;
+        }
+        remote_setting->setting.reset = (value == NULL);
+        remote_setting->setting.reset_all = false;
+    }
+    else
+    {
+        remote_setting->setting.reset_all = true;
+    }
+    remote_setting->sender_latch = MyLatch;
+
+    /* Check for the target is there yet */
+    proc = BackendPidGetProc(target_pid);
+    if (proc != target_proc ||
+        proc->pid != target_pid || proc->starttimestamp != target_timestamp)
+    {
+        /* let anybody work on the area */
+        SpinLockAcquire(&remote_setting->mutex);
+        remote_setting->busy = false;
+        SpinLockRelease(&remote_setting->mutex);
+        ConditionVariableBroadcast(&remote_setting->busy_cv);
+        
+        ereport(ERROR,
+                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                 errmsg("target session has gone")));
+    }
+    
+    
+    if (SendProcSignal(target_pid, PROCSIG_REMOTE_GUC, InvalidBackendId) < 0)
+    {
+        /* let anybody work on the area */
+        SpinLockAcquire(&remote_setting->mutex);
+        remote_setting->busy = false;
+        SpinLockRelease(&remote_setting->mutex);
+        ConditionVariableBroadcast(&remote_setting->busy_cv);
+        
+        ereport(ERROR,
+                (errmsg("could not signal backend with PID %d: %m", target_pid)));
+    }
+
+    return true;
+}
+
+
+void
+HandleRemoteGucSetInterrupt(void)
+{
+    Assert(remote_setting->busy);
+
+    /* check if any request is being sent to me */
+    if (remote_setting->target_pid == MyProcPid)
+    {
+        GucPendingSetting  *setting;
+        GucPendingSetting  *r = &remote_setting->setting;
+        bool                found;
+
+        if (r->reset_all)
+        {
+            if (pending_setting)
+            {
+                MemoryContextReset(CacheMemoryContext);
+                pending_setting = NULL;
+            }
+            ResetAllOptions(PGC_S_REMOTE);
+            goto finish;
+        }
+
+        if (!RemoteConfigMemoryContext)
+            RemoteConfigMemoryContext =
+                AllocSetContextCreate(CacheMemoryContext,
+                                      "Remote GUC setting context",
+                                      ALLOCSET_DEFAULT_SIZES);
+
+        if (!pending_setting)
+        {
+            HASHCTL hashctl;
+
+            MemSet(&hashctl, 0, sizeof(hashctl));
+            hashctl.keysize = NAMEDATALEN;
+            hashctl.entrysize = sizeof(GucPendingSetting);
+            hashctl.hcxt = RemoteConfigMemoryContext;
+            pending_setting = hash_create("Pending remote GUC Changes",
+                                          8, &hashctl,
+                                          HASH_ELEM | HASH_CONTEXT);
+        }
+
+        /* Just overwrite existing entry */
+        setting = hash_search(pending_setting, r->name, HASH_ENTER, &found);
+        setting->context    = r->context;
+        setting->reset        = r->reset;
+        setting->reset_all    = r->reset_all;
+        setting->source_pid    = r->source_pid;
+
+        if (!found)
+            setting->value = NULL;
+
+        /* free existing value if new value is given or to reset */
+        if (setting->value &&
+            (r->reset ||
+             strcmp(setting->value, remote_setting->value) != 0))
+        {
+            pfree(setting->value);
+            setting->value = NULL;
+        }
+
+        /* non-null value means no use copying the value */
+        if (!r->reset && setting->value == NULL)
+            setting->value = MemoryContextStrdup(RemoteConfigMemoryContext,
+                                                 remote_setting->value);
+    }
+
+finish:
+    InterruptPending = true;
+    RemoteGucChangePending = true;
+
+    /* release the communication area */
+    SpinLockAcquire(&remote_setting->mutex);
+    remote_setting->busy = false;
+    SpinLockRelease(&remote_setting->mutex);
+    ConditionVariableBroadcast(&remote_setting->busy_cv);
+}
+
+void
+HandleGucRemoteChanges(void)
+{
+    HASH_SEQ_STATUS        seq;
+    GucPendingSetting  *entry;
+
+    RemoteGucChangePending = false;
+
+    /* reset all discards the hash, must return in the case */
+    if (!pending_setting)
+        return;
+
+    hash_seq_init(&seq, pending_setting);
+
+    while ((entry = hash_seq_search(&seq)))
+    {
+        int scres;
+        char *preval;
+        const char *valtmp = GetConfigOption(entry->name, true, false);
+
+        if (!valtmp)
+            valtmp = "";
+
+        preval = pstrdup(valtmp);
+
+        scres = set_config_option(entry->name, entry->value, entry->context,
+                                  PGC_S_REMOTE, GUC_ACTION_NONXACT,
+                                  true, WARNING, false);
+        if (scres > 0)
+        {
+            if (preval)
+            {
+                const char *postval = GetConfigOption(entry->name, true, false);
+                if (!postval)
+                    postval = "";
+                if (strcmp(preval, postval) != 0)
+                    ereport(NOTICE,
+                            (errmsg("GUC variable \"%s\" is changed to \"%s\" by request from backend with PID %d",
+                                    entry->name, postval, entry->source_pid)));
+            }
+        }
+        else if (scres == 0)
+            ereport(LOG,
+                    (errmsg("GUC variable \"%s\" could not be changed by request from backend with PID %d",
+                            entry->name, entry->source_pid)));
+
+        if (preval)
+            pfree(preval);
+    }
+
+    MemoryContextReset(RemoteConfigMemoryContext);
+    pending_setting = NULL;
+}
+
 /*
  * Validate a proposed option setting for GUCArrayAdd/Delete/Reset.
  *
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index b8de13f03b..e93a7b9938 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5700,8 +5700,8 @@
   proargtypes => 'text bool', prosrc => 'show_config_by_name_missing_ok' },
 { oid => '2078', descr => 'SET X as a function',
   proname => 'set_config', proisstrict => 'f', provolatile => 'v',
-  proparallel => 'u', prorettype => 'text', proargtypes => 'text text bool',
-  prosrc => 'set_config_by_name' },
+  proparallel => 'u', prorettype => 'text',
+  proargtypes => 'text text bool bool', prosrc => 'set_config_by_name' },
 { oid => '2084', descr => 'SHOW ALL as a function',
   proname => 'pg_show_all_settings', prorows => '1000', proretset => 't',
   provolatile => 's', prorettype => 'record', proargtypes => '',
@@ -9669,6 +9669,12 @@
   proargmodes => '{o,o,o,o,o,o,o,o,o,o,o}',
   proargnames =>
'{slot_name,plugin,slot_type,datoid,temporary,active,active_pid,xmin,catalog_xmin,restart_lsn,confirmed_flush_lsn}',
   prosrc => 'pg_get_replication_slots' },
+{ oid => '3424',
+  descr => 'set config of another backend',
+  proname => 'pg_set_backend_config', proisstrict => 'f',
+  proretset => 'f', provolatile => 'v', proparallel => 'u',
+  prorettype => 'bool', proargtypes => 'int4 text text',
+  prosrc => 'set_backend_config' },
 { oid => '3786', descr => 'set up a logical replication slot',
   proname => 'pg_create_logical_replication_slot', provolatile => 'v',
   proparallel => 'u', prorettype => 'record', proargtypes => 'name name bool',
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index fbe2dc14a7..74e4fe2f0d 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -407,6 +407,7 @@ typedef enum NodeTag
     T_RefreshMatViewStmt,
     T_ReplicaIdentityStmt,
     T_AlterSystemStmt,
+    T_AlterSessionStmt,
     T_CreatePolicyStmt,
     T_AlterPolicyStmt,
     T_CreateTransformStmt,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 4ec8a83541..98658ce714 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -3121,6 +3121,18 @@ typedef struct AlterSystemStmt
     VariableSetStmt *setstmt;    /* SET subcommand */
 } AlterSystemStmt;
 
+/* ----------------------
+ *        Alter Session Statement
+ * ----------------------
+ */
+typedef struct AlterSessionStmt
+{
+    NodeTag        type;
+    DefElem       *sessionopt;        /* session property */
+    VariableSetStmt *setstmt;    /* SET subcommand */
+    bool        immediate;        /* reload immediately */
+} AlterSessionStmt;
+
 /* ----------------------
  *        Cluster Statement (support pbrown's cluster index implementation)
  * ----------------------
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 88a75fb798..071deae55b 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -833,7 +833,8 @@ typedef enum
     WAIT_EVENT_REPLICATION_ORIGIN_DROP,
     WAIT_EVENT_REPLICATION_SLOT_DROP,
     WAIT_EVENT_SAFE_SNAPSHOT,
-    WAIT_EVENT_SYNC_REP
+    WAIT_EVENT_SYNC_REP,
+    WAIT_EVENT_REMOTE_GUC
 } WaitEventIPC;
 
 /* ----------
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index 9f2f965d5c..040877f5eb 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -42,6 +42,9 @@ typedef enum
     PROCSIG_RECOVERY_CONFLICT_BUFFERPIN,
     PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK,
 
+    /* Remote GUC setting */
+    PROCSIG_REMOTE_GUC,
+
     NUM_PROCSIGNALS                /* Must be last! */
 } ProcSignalReason;
 
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index c07e7b945e..c53e0c85c4 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -14,6 +14,7 @@
 #define GUC_H
 
 #include "nodes/parsenodes.h"
+#include "parser/parse_node.h"
 #include "tcop/dest.h"
 #include "utils/array.h"
 
@@ -117,7 +118,8 @@ typedef enum
     PGC_S_OVERRIDE,                /* special case to forcibly set default */
     PGC_S_INTERACTIVE,            /* dividing line for error reporting */
     PGC_S_TEST,                    /* test per-database or per-user setting */
-    PGC_S_SESSION                /* SET command */
+    PGC_S_SESSION,                /* SET command */
+    PGC_S_REMOTE                /* remote setting */
 } GucSource;
 
 /*
@@ -193,7 +195,8 @@ typedef enum
     /* Types of set_config_option actions */
     GUC_ACTION_SET,                /* regular SET command */
     GUC_ACTION_LOCAL,            /* SET LOCAL command */
-    GUC_ACTION_SAVE                /* function SET option, or temp assignment */
+    GUC_ACTION_SAVE,            /* function SET option, or temp assignment */
+    GUC_ACTION_NONXACT            /* transactional setting */
 } GucAction;
 
 #define GUC_QUALIFIER_SEPARATOR '.'
@@ -269,6 +272,8 @@ extern int    tcp_keepalives_idle;
 extern int    tcp_keepalives_interval;
 extern int    tcp_keepalives_count;
 
+extern volatile bool RemoteGucChangePending;
+
 #ifdef TRACE_SORT
 extern bool trace_sort;
 #endif
@@ -276,6 +281,11 @@ extern bool trace_sort;
 /*
  * Functions exported by guc.c
  */
+extern Size GucShmemSize(void);
+extern void GucShmemInit(void);
+extern Datum set_backend_setting(PG_FUNCTION_ARGS);
+extern void HandleRemoteGucSetInterrupt(void);
+extern void HandleGucRemoteChanges(void);
 extern void SetConfigOption(const char *name, const char *value,
                 GucContext context, GucSource source);
 
@@ -353,7 +363,7 @@ extern int    GetConfigOptionFlags(const char *name, bool missing_ok);
 extern void ProcessConfigFile(GucContext context);
 extern void InitializeGUCOptions(void);
 extern bool SelectConfigFiles(const char *userDoption, const char *progname);
-extern void ResetAllOptions(void);
+extern void ResetAllOptions(GucSource src);
 extern void AtStart_GUC(void);
 extern int    NewGUCNestLevel(void);
 extern void AtEOXact_GUC(bool isCommit, int nestLevel);
@@ -367,6 +377,8 @@ extern int set_config_option(const char *name, const char *value,
                   GucAction action, bool changeVal, int elevel,
                   bool is_reload);
 extern void AlterSystemSetConfigFile(AlterSystemStmt *setstmt);
+extern void AlterSessionSetRemoteConfig(ParseState *pstate,
+                                        AlterSessionStmt *setstmt);
 extern char *GetConfigOptionByName(const char *name, const char **varname,
                       bool missing_ok);
 extern void GetConfigOptionByNum(int varnum, const char **values, bool *noshow);
@@ -395,6 +407,9 @@ extern Size EstimateGUCStateSpace(void);
 extern void SerializeGUCState(Size maxsize, char *start_address);
 extern void RestoreGUCState(void *gucstate);
 
+/* Remote GUC setting */
+extern void HandleGucRemoteChanges(void);
+
 /* Support for messages reported from GUC check hooks */
 
 extern PGDLLIMPORT char *GUC_check_errmsg_string;
diff --git a/src/include/utils/guc_tables.h b/src/include/utils/guc_tables.h
index a0970b2e1c..c00520e90c 100644
--- a/src/include/utils/guc_tables.h
+++ b/src/include/utils/guc_tables.h
@@ -115,7 +115,10 @@ typedef enum
     GUC_SAVE,                    /* entry caused by function SET option */
     GUC_SET,                    /* entry caused by plain SET command */
     GUC_LOCAL,                    /* entry caused by SET LOCAL command */
-    GUC_SET_LOCAL                /* entry caused by SET then SET LOCAL */
+    GUC_NONXACT,                /* entry caused by non-transactional ops */
+    GUC_SET_LOCAL,                /* entry caused by SET then SET LOCAL */
+    GUC_NONXACT_SET,            /* entry caused by NONXACT then SET */
+    GUC_NONXACT_LOCAL            /* entry caused by NONXACT then (SET)LOCAL */
 } GucStackState;
 
 typedef struct guc_stack
diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out
index b0d7351145..2d19697a8c 100644
--- a/src/test/regress/expected/guc.out
+++ b/src/test/regress/expected/guc.out
@@ -476,6 +476,229 @@ SELECT '2006-08-13 12:34:56'::timestamptz;
  2006-08-13 12:34:56-07
 (1 row)
 
+-- NONXACT followed by SET, SET LOCAL through COMMIT
+BEGIN;
+SELECT set_config('work_mem', '128kB', false, true); -- NONXACT
+ set_config 
+------------
+ 128kB
+(1 row)
+
+SET work_mem to '256kB';
+SET LOCAL work_mem to '512kB';
+SHOW work_mem;    -- must see 512kB
+ work_mem 
+----------
+ 512kB
+(1 row)
+
+COMMIT;
+SHOW work_mem;    -- must see 256kB
+ work_mem 
+----------
+ 256kB
+(1 row)
+
+-- NONXACT followed by SET, SET LOCAL through ROLLBACK
+BEGIN;
+SELECT set_config('work_mem', '128kB', false, true); -- NONXACT
+ set_config 
+------------
+ 128kB
+(1 row)
+
+SET work_mem to '256kB';
+SET LOCAL work_mem to '512kB';
+SHOW work_mem;    -- must see 512kB
+ work_mem 
+----------
+ 512kB
+(1 row)
+
+ROLLBACK;
+SHOW work_mem;    -- must see 128kB
+ work_mem 
+----------
+ 128kB
+(1 row)
+
+-- SET, SET LOCAL followed by NONXACT through COMMIT
+BEGIN;
+SET work_mem to '256kB';
+SET LOCAL work_mem to '512kB';
+SELECT set_config('work_mem', '128kB', false, true); -- NONXACT
+ set_config 
+------------
+ 128kB
+(1 row)
+
+SHOW work_mem;    -- must see 128kB
+ work_mem 
+----------
+ 128kB
+(1 row)
+
+COMMIT;
+SHOW work_mem;    -- must see 128kB
+ work_mem 
+----------
+ 128kB
+(1 row)
+
+-- SET, SET LOCAL followed by NONXACT through ROLLBACK
+BEGIN;
+SET work_mem to '256kB';
+SET LOCAL work_mem to '512kB';
+SELECT set_config('work_mem', '128kB', false, true); -- NONXACT
+ set_config 
+------------
+ 128kB
+(1 row)
+
+SHOW work_mem;    -- must see 128kB
+ work_mem 
+----------
+ 128kB
+(1 row)
+
+ROLLBACK;
+SHOW work_mem;    -- must see 128kB
+ work_mem 
+----------
+ 128kB
+(1 row)
+
+-- NONXACT and SAVEPOINT
+SET work_mem TO '64kB';
+BEGIN;
+SET work_mem TO '128kB';
+SAVEPOINT a;
+SELECT set_config('work_mem', '256kB', false, true); -- NONXACT
+ set_config 
+------------
+ 256kB
+(1 row)
+
+SHOW work_mem;
+ work_mem 
+----------
+ 256kB
+(1 row)
+
+SET LOCAL work_mem TO '384kB';
+RELEASE SAVEPOINT a;
+SHOW work_mem; -- will see 384kB
+ work_mem 
+----------
+ 384kB
+(1 row)
+
+COMMIT;
+SHOW work_mem; -- will see 256kB
+ work_mem 
+----------
+ 256kB
+(1 row)
+
+--
+SET work_mem TO '64kB';
+BEGIN;
+SET work_mem TO '128kB';
+SAVEPOINT a;
+SELECT set_config('work_mem', '256kB', false, true); -- NONXACT
+ set_config 
+------------
+ 256kB
+(1 row)
+
+SHOW work_mem;
+ work_mem 
+----------
+ 256kB
+(1 row)
+
+SET LOCAL work_mem TO '384kB';
+ROLLBACK TO SAVEPOINT a;
+SHOW work_mem; -- will see 256kB
+ work_mem 
+----------
+ 256kB
+(1 row)
+
+ROLLBACK;
+SHOW work_mem; -- will see 256kB
+ work_mem 
+----------
+ 256kB
+(1 row)
+
+--
+SET work_mem TO '64kB';
+BEGIN;
+SET work_mem TO '128kB';
+SET LOCAL work_mem TO '384kB';
+SAVEPOINT a;
+SELECT set_config('work_mem', '256kB', false, true); -- NONXACT
+ set_config 
+------------
+ 256kB
+(1 row)
+
+SHOW work_mem;
+ work_mem 
+----------
+ 256kB
+(1 row)
+
+SET LOCAL work_mem TO '384kB';
+RELEASE SAVEPOINT a;
+SHOW work_mem; -- will see 384kB
+ work_mem 
+----------
+ 384kB
+(1 row)
+
+ROLLBACK;
+SHOW work_mem; -- will see 256kB
+ work_mem 
+----------
+ 256kB
+(1 row)
+
+--
+SET work_mem TO '64kB';
+BEGIN;
+SET work_mem TO '128kB';
+SET LOCAL work_mem TO '384kB';
+SAVEPOINT a;
+SELECT set_config('work_mem', '256kB', false, true); -- NONXACT
+ set_config 
+------------
+ 256kB
+(1 row)
+
+SHOW work_mem;
+ work_mem 
+----------
+ 256kB
+(1 row)
+
+SET LOCAL work_mem TO '384kB';
+ROLLBACK TO SAVEPOINT a;
+SHOW work_mem; -- will see 256kB
+ work_mem 
+----------
+ 256kB
+(1 row)
+
+COMMIT;
+SHOW work_mem; -- will see 256kB
+ work_mem 
+----------
+ 256kB
+(1 row)
+
+SET work_mem TO DEFAULT;
 --
 -- Test RESET.  We use datestyle because the reset value is forced by
 -- pg_regress, so it doesn't depend on the installation's configuration.
diff --git a/src/test/regress/sql/guc.sql b/src/test/regress/sql/guc.sql
index 3b854ac496..bbb91aaa98 100644
--- a/src/test/regress/sql/guc.sql
+++ b/src/test/regress/sql/guc.sql
@@ -133,6 +133,94 @@ SHOW vacuum_cost_delay;
 SHOW datestyle;
 SELECT '2006-08-13 12:34:56'::timestamptz;
 
+-- NONXACT followed by SET, SET LOCAL through COMMIT
+BEGIN;
+SELECT set_config('work_mem', '128kB', false, true); -- NONXACT
+SET work_mem to '256kB';
+SET LOCAL work_mem to '512kB';
+SHOW work_mem;    -- must see 512kB
+COMMIT;
+SHOW work_mem;    -- must see 256kB
+
+-- NONXACT followed by SET, SET LOCAL through ROLLBACK
+BEGIN;
+SELECT set_config('work_mem', '128kB', false, true); -- NONXACT
+SET work_mem to '256kB';
+SET LOCAL work_mem to '512kB';
+SHOW work_mem;    -- must see 512kB
+ROLLBACK;
+SHOW work_mem;    -- must see 128kB
+
+-- SET, SET LOCAL followed by NONXACT through COMMIT
+BEGIN;
+SET work_mem to '256kB';
+SET LOCAL work_mem to '512kB';
+SELECT set_config('work_mem', '128kB', false, true); -- NONXACT
+SHOW work_mem;    -- must see 128kB
+COMMIT;
+SHOW work_mem;    -- must see 128kB
+
+-- SET, SET LOCAL followed by NONXACT through ROLLBACK
+BEGIN;
+SET work_mem to '256kB';
+SET LOCAL work_mem to '512kB';
+SELECT set_config('work_mem', '128kB', false, true); -- NONXACT
+SHOW work_mem;    -- must see 128kB
+ROLLBACK;
+SHOW work_mem;    -- must see 128kB
+
+-- NONXACT and SAVEPOINT
+SET work_mem TO '64kB';
+BEGIN;
+SET work_mem TO '128kB';
+SAVEPOINT a;
+SELECT set_config('work_mem', '256kB', false, true); -- NONXACT
+SHOW work_mem;
+SET LOCAL work_mem TO '384kB';
+RELEASE SAVEPOINT a;
+SHOW work_mem; -- will see 384kB
+COMMIT;
+SHOW work_mem; -- will see 256kB
+--
+SET work_mem TO '64kB';
+BEGIN;
+SET work_mem TO '128kB';
+SAVEPOINT a;
+SELECT set_config('work_mem', '256kB', false, true); -- NONXACT
+SHOW work_mem;
+SET LOCAL work_mem TO '384kB';
+ROLLBACK TO SAVEPOINT a;
+SHOW work_mem; -- will see 256kB
+ROLLBACK;
+SHOW work_mem; -- will see 256kB
+--
+SET work_mem TO '64kB';
+BEGIN;
+SET work_mem TO '128kB';
+SET LOCAL work_mem TO '384kB';
+SAVEPOINT a;
+SELECT set_config('work_mem', '256kB', false, true); -- NONXACT
+SHOW work_mem;
+SET LOCAL work_mem TO '384kB';
+RELEASE SAVEPOINT a;
+SHOW work_mem; -- will see 384kB
+ROLLBACK;
+SHOW work_mem; -- will see 256kB
+--
+SET work_mem TO '64kB';
+BEGIN;
+SET work_mem TO '128kB';
+SET LOCAL work_mem TO '384kB';
+SAVEPOINT a;
+SELECT set_config('work_mem', '256kB', false, true); -- NONXACT
+SHOW work_mem;
+SET LOCAL work_mem TO '384kB';
+ROLLBACK TO SAVEPOINT a;
+SHOW work_mem; -- will see 256kB
+COMMIT;
+SHOW work_mem; -- will see 256kB
+
+SET work_mem TO DEFAULT;
 --
 -- Test RESET.  We use datestyle because the reset value is forced by
 -- pg_regress, so it doesn't depend on the installation's configuration.
-- 
2.16.3