On Thu, Jun 9, 2022 at 6:23 AM Robert Haas <robertmh...@gmail.com> wrote:
> On Wed, Jun 8, 2022 at 7:53 PM Jacob Champion <jchamp...@timescale.com> wrote:
> > But I don't have any better ideas for how to achieve both. I'm fine
> > with your suggestion of ClientConnectionInfo, if that sounds good to
> > others; the doc comment can clarify why it differs from Port? Or add
> > one of the Shared-/Gang-/Group- prefixes to it, maybe?
>
> I don't like the prefixes, so I'd prefer explaining it in the struct comment.

Done that way in v11.

Thanks!
--Jacob
commit afb70959a6d46054eb65e9c4b0a8f61d1c87b91b
Author: Jacob Champion <jchamp...@timescale.com>
Date:   Fri Jun 10 10:48:07 2022 -0700

    squash! Allow parallel workers to use pg_session_authn_id()
    
    Per review, switch the global name to ClientConnectionInfo.

diff --git a/src/backend/access/transam/parallel.c 
b/src/backend/access/transam/parallel.c
index 27eda766b1..bc93101ff7 100644
--- a/src/backend/access/transam/parallel.c
+++ b/src/backend/access/transam/parallel.c
@@ -76,7 +76,7 @@
 #define PARALLEL_KEY_REINDEX_STATE                     
UINT64CONST(0xFFFFFFFFFFFF000C)
 #define PARALLEL_KEY_RELMAPPER_STATE           UINT64CONST(0xFFFFFFFFFFFF000D)
 #define PARALLEL_KEY_UNCOMMITTEDENUMS          UINT64CONST(0xFFFFFFFFFFFF000E)
-#define PARALLEL_KEY_PROCINFO                          
UINT64CONST(0xFFFFFFFFFFFF000F)
+#define PARALLEL_KEY_CLIENTCONNINFO                    
UINT64CONST(0xFFFFFFFFFFFF000F)
 
 /* Fixed-size parallel state. */
 typedef struct FixedParallelState
@@ -213,7 +213,7 @@ InitializeParallelDSM(ParallelContext *pcxt)
        Size            reindexlen = 0;
        Size            relmapperlen = 0;
        Size            uncommittedenumslen = 0;
-       Size            procinfolen = 0;
+       Size            clientconninfolen = 0;
        Size            segsize = 0;
        int                     i;
        FixedParallelState *fps;
@@ -274,8 +274,8 @@ InitializeParallelDSM(ParallelContext *pcxt)
                shm_toc_estimate_chunk(&pcxt->estimator, relmapperlen);
                uncommittedenumslen = EstimateUncommittedEnumsSpace();
                shm_toc_estimate_chunk(&pcxt->estimator, uncommittedenumslen);
-               procinfolen = EstimateParallelProcInfoSpace();
-               shm_toc_estimate_chunk(&pcxt->estimator, procinfolen);
+               clientconninfolen = EstimateClientConnectionInfoSpace();
+               shm_toc_estimate_chunk(&pcxt->estimator, clientconninfolen);
                /* If you add more chunks here, you probably need to add keys. 
*/
                shm_toc_estimate_keys(&pcxt->estimator, 12);
 
@@ -356,7 +356,7 @@ InitializeParallelDSM(ParallelContext *pcxt)
                char       *session_dsm_handle_space;
                char       *entrypointstate;
                char       *uncommittedenumsspace;
-               char       *procinfospace;
+               char       *clientconninfospace;
                Size            lnamelen;
 
                /* Serialize shared libraries we have loaded. */
@@ -427,11 +427,11 @@ InitializeParallelDSM(ParallelContext *pcxt)
                shm_toc_insert(pcxt->toc, PARALLEL_KEY_UNCOMMITTEDENUMS,
                                           uncommittedenumsspace);
 
-               /* Serialize our ParallelProcInfo. */
-               procinfospace = shm_toc_allocate(pcxt->toc, procinfolen);
-               SerializeParallelProcInfo(procinfolen, procinfospace);
-               shm_toc_insert(pcxt->toc, PARALLEL_KEY_PROCINFO,
-                                          procinfospace);
+               /* Serialize our ClientConnectionInfo. */
+               clientconninfospace = shm_toc_allocate(pcxt->toc, 
clientconninfolen);
+               SerializeClientConnectionInfo(clientconninfolen, 
clientconninfospace);
+               shm_toc_insert(pcxt->toc, PARALLEL_KEY_CLIENTCONNINFO,
+                                          clientconninfospace);
 
                /* Allocate space for worker information. */
                pcxt->worker = palloc0(sizeof(ParallelWorkerInfo) * 
pcxt->nworkers);
@@ -1281,7 +1281,7 @@ ParallelWorkerMain(Datum main_arg)
        char       *reindexspace;
        char       *relmapperspace;
        char       *uncommittedenumsspace;
-       char       *procinfospace;
+       char       *clientconninfospace;
        StringInfoData msgbuf;
        char       *session_dsm_handle_space;
        Snapshot        tsnapshot;
@@ -1491,9 +1491,10 @@ ParallelWorkerMain(Datum main_arg)
                                                                                
   false);
        RestoreUncommittedEnums(uncommittedenumsspace);
 
-       /* Restore the ParallelProcInfo. */
-       procinfospace = shm_toc_lookup(toc, PARALLEL_KEY_PROCINFO, false);
-       RestoreParallelProcInfo(procinfospace);
+       /* Restore the ClientConnectionInfo. */
+       clientconninfospace = shm_toc_lookup(toc, PARALLEL_KEY_CLIENTCONNINFO,
+                                                                               
 false);
+       RestoreClientConnectionInfo(clientconninfospace);
 
        /* Attach to the leader's serializable transaction, if SERIALIZABLE. */
        AttachSerializableXact(fps->serializable_xact_handle);
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index 2e5fe2cc19..6a499efecd 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -342,7 +342,7 @@ auth_failed(Port *port, int status, const char *logdetail)
  * authorization will fail later.
  *
  * The provided string will be copied into TopMemoryContext, to match the
- * lifetime of MyParallelProcInfo, so it is safe to pass a string that is
+ * lifetime of MyClientConnectionInfo, so it is safe to pass a string that is
  * managed by an external library.
  */
 static void
@@ -350,7 +350,7 @@ set_authn_id(Port *port, const char *id)
 {
        Assert(id);
 
-       if (MyParallelProcInfo.authn_id)
+       if (MyClientConnectionInfo.authn_id)
        {
                /*
                 * An existing authn_id should never be overwritten; that means 
two
@@ -361,17 +361,17 @@ set_authn_id(Port *port, const char *id)
                ereport(FATAL,
                                (errmsg("authentication identifier set more 
than once"),
                                 errdetail_log("previous identifier: \"%s\"; 
new identifier: \"%s\"",
-                                                          
MyParallelProcInfo.authn_id, id)));
+                                                          
MyClientConnectionInfo.authn_id, id)));
        }
 
-       MyParallelProcInfo.authn_id = MemoryContextStrdup(TopMemoryContext, id);
+       MyClientConnectionInfo.authn_id = MemoryContextStrdup(TopMemoryContext, 
id);
 
        if (Log_connections)
        {
                ereport(LOG,
                                errmsg("connection authenticated: 
identity=\"%s\" method=%s "
                                           "(%s:%d)",
-                                          MyParallelProcInfo.authn_id,
+                                          MyClientConnectionInfo.authn_id,
                                           
hba_authname(port->hba->auth_method), HbaFileName,
                                           port->hba->linenumber));
        }
@@ -1910,7 +1910,7 @@ auth_peer(hbaPort *port)
        set_authn_id(port, pw->pw_name);
 
        ret = check_usermap(port->hba->usermap, port->user_name,
-                                               MyParallelProcInfo.authn_id, 
false);
+                                               
MyClientConnectionInfo.authn_id, false);
 
        return ret;
 #else
diff --git a/src/backend/utils/adt/name.c b/src/backend/utils/adt/name.c
index 24a06bf933..97c827fb9a 100644
--- a/src/backend/utils/adt/name.c
+++ b/src/backend/utils/adt/name.c
@@ -275,10 +275,10 @@ session_user(PG_FUNCTION_ARGS)
 Datum
 pg_session_authn_id(PG_FUNCTION_ARGS)
 {
-       if (!MyParallelProcInfo.authn_id)
+       if (!MyClientConnectionInfo.authn_id)
                PG_RETURN_NULL();
 
-       PG_RETURN_TEXT_P(cstring_to_text(MyParallelProcInfo.authn_id));
+       PG_RETURN_TEXT_P(cstring_to_text(MyClientConnectionInfo.authn_id));
 }
 
 
diff --git a/src/backend/utils/init/miscinit.c 
b/src/backend/utils/init/miscinit.c
index 408fa8953d..1bbe1eaa17 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -935,48 +935,49 @@ GetUserNameFromId(Oid roleid, bool noerr)
 /* ------------------------------------------------------------------------
  *                             Parallel connection state
  *
- * MyParallelProcInfo contains pieces of information about the client that need
- * to be synced to parallel workers when they initialize. Over time, this list
- * will probably grow, and may subsume some of the "user state" variables 
above.
+ * ClientConnectionInfo contains pieces of information about the client that
+ * need to be synced to parallel workers when they initialize. Over time, this
+ * list will probably grow, and may subsume some of the "user state" variables
+ * above.
  *-------------------------------------------------------------------------
  */
 
-ParallelProcInfo MyParallelProcInfo;
+ClientConnectionInfo MyClientConnectionInfo;
 
 /*
- * Calculate the space needed to serialize MyParallelProcInfo.
+ * Calculate the space needed to serialize MyClientConnectionInfo.
  */
 Size
-EstimateParallelProcInfoSpace(void)
+EstimateClientConnectionInfoSpace(void)
 {
        Size            size = 1;
 
-       if (MyParallelProcInfo.authn_id)
-               size = add_size(size, strlen(MyParallelProcInfo.authn_id) + 1);
+       if (MyClientConnectionInfo.authn_id)
+               size = add_size(size, strlen(MyClientConnectionInfo.authn_id) + 
1);
 
        return size;
 }
 
 /*
- * Serialize MyParallelProcInfo for use by parallel workers.
+ * Serialize MyClientConnectionInfo for use by parallel workers.
  */
 void
-SerializeParallelProcInfo(Size maxsize, char *start_address)
+SerializeClientConnectionInfo(Size maxsize, char *start_address)
 {
        /*
         * First byte is an indication of whether or not authn_id has been set 
to
         * non-NULL, to differentiate that case from the empty string.
         */
        Assert(maxsize > 0);
-       start_address[0] = MyParallelProcInfo.authn_id ? 1 : 0;
+       start_address[0] = MyClientConnectionInfo.authn_id ? 1 : 0;
        start_address++;
        maxsize--;
 
-       if (MyParallelProcInfo.authn_id)
+       if (MyClientConnectionInfo.authn_id)
        {
                Size len;
 
-               len = strlcpy(start_address, MyParallelProcInfo.authn_id, 
maxsize) + 1;
+               len = strlcpy(start_address, MyClientConnectionInfo.authn_id, 
maxsize) + 1;
                Assert(len <= maxsize);
                maxsize -= len;
                start_address += len;
@@ -984,22 +985,22 @@ SerializeParallelProcInfo(Size maxsize, char 
*start_address)
 }
 
 /*
- * Restore MyParallelProcInfo from its serialized representation.
+ * Restore MyClientConnectionInfo from its serialized representation.
  */
 void
-RestoreParallelProcInfo(char *procinfo)
+RestoreClientConnectionInfo(char *conninfo)
 {
-       if (procinfo[0] == 0)
+       if (conninfo[0] == 0)
        {
-               MyParallelProcInfo.authn_id = NULL;
-               procinfo++;
+               MyClientConnectionInfo.authn_id = NULL;
+               conninfo++;
        }
        else
        {
-               procinfo++;
-               MyParallelProcInfo.authn_id = 
MemoryContextStrdup(TopMemoryContext,
-                                                                               
                                  procinfo);
-               procinfo += strlen(procinfo) + 1;
+               conninfo++;
+               MyClientConnectionInfo.authn_id = 
MemoryContextStrdup(TopMemoryContext,
+                                                                               
                                          conninfo);
+               conninfo += strlen(conninfo) + 1;
        }
 }
 
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index f381e958ee..c900411fdd 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -99,9 +99,13 @@ typedef struct
 #endif
 
 /*
- * Fields from Port that need to be copied over to parallel workers go into the
- * ParallelProcInfo. The same rules apply for allocations here as for Port 
(must
- * be malloc'd or palloc'd in TopMemoryContext).
+ * Fields describing the client connection, that also need to be copied over to
+ * parallel workers, go into the ClientConnectionInfo rather than Port. The 
same
+ * rules apply for allocations here as for Port (must be malloc'd or palloc'd 
in
+ * TopMemoryContext).
+ *
+ * If you add a struct member here, remember to also handle serialization in
+ * SerializeClientConnectionInfo() et al.
  */
 typedef struct
 {
@@ -117,7 +121,7 @@ typedef struct
         * example if the "trust" auth method is in use.
         */
        const char *authn_id;
-} ParallelProcInfo;
+} ClientConnectionInfo;
 
 /*
  * This is used by the postmaster in its communication with frontends.  It
@@ -335,7 +339,7 @@ extern ssize_t be_gssapi_write(Port *port, void *ptr, 
size_t len);
 #endif                                                 /* ENABLE_GSS */
 
 extern PGDLLIMPORT ProtocolVersion FrontendProtocol;
-extern PGDLLIMPORT ParallelProcInfo MyParallelProcInfo;
+extern PGDLLIMPORT ClientConnectionInfo MyClientConnectionInfo;
 
 /* TCP keepalives configuration. These are no-ops on an AF_UNIX socket. */
 
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 55ad268700..c06796fe4a 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -486,9 +486,9 @@ extern bool has_rolreplication(Oid roleid);
 typedef void (*shmem_request_hook_type) (void);
 extern PGDLLIMPORT shmem_request_hook_type shmem_request_hook;
 
-extern Size EstimateParallelProcInfoSpace(void);
-extern void SerializeParallelProcInfo(Size maxsize, char *start_address);
-extern void RestoreParallelProcInfo(char *procinfo);
+extern Size EstimateClientConnectionInfoSpace(void);
+extern void SerializeClientConnectionInfo(Size maxsize, char *start_address);
+extern void RestoreClientConnectionInfo(char *procinfo);
 
 /* in executor/nodeHash.c */
 extern size_t get_hash_memory_limit(void);
From 77801627c46f9e29918d35ffd7430e861ac03b82 Mon Sep 17 00:00:00 2001
From: Jacob Champion <pchamp...@vmware.com>
Date: Wed, 23 Mar 2022 15:07:05 -0700
Subject: [PATCH v11 2/2] Allow parallel workers to use pg_session_authn_id()

Move authn_id into a new global, MyClientConnectionInfo, which is
intended to hold all the client information that needs to be shared
between the backend and any parallel workers. MyClientConnectionInfo is
serialized and restored using a new parallel key.

With this change, the parallel restriction can be removed from
pg_session_authn_id().
---
 src/backend/access/transam/parallel.c     | 19 +++++-
 src/backend/libpq/auth.c                  | 16 ++---
 src/backend/utils/adt/name.c              |  4 +-
 src/backend/utils/init/miscinit.c         | 72 +++++++++++++++++++++++
 src/include/catalog/pg_proc.dat           |  2 +-
 src/include/libpq/libpq-be.h              | 39 ++++++++----
 src/include/miscadmin.h                   |  4 ++
 src/test/authentication/t/001_password.pl | 33 +++++++++++
 8 files changed, 165 insertions(+), 24 deletions(-)

diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c
index df0cd77558..bc93101ff7 100644
--- a/src/backend/access/transam/parallel.c
+++ b/src/backend/access/transam/parallel.c
@@ -76,6 +76,7 @@
 #define PARALLEL_KEY_REINDEX_STATE			UINT64CONST(0xFFFFFFFFFFFF000C)
 #define PARALLEL_KEY_RELMAPPER_STATE		UINT64CONST(0xFFFFFFFFFFFF000D)
 #define PARALLEL_KEY_UNCOMMITTEDENUMS		UINT64CONST(0xFFFFFFFFFFFF000E)
+#define PARALLEL_KEY_CLIENTCONNINFO			UINT64CONST(0xFFFFFFFFFFFF000F)
 
 /* Fixed-size parallel state. */
 typedef struct FixedParallelState
@@ -212,6 +213,7 @@ InitializeParallelDSM(ParallelContext *pcxt)
 	Size		reindexlen = 0;
 	Size		relmapperlen = 0;
 	Size		uncommittedenumslen = 0;
+	Size		clientconninfolen = 0;
 	Size		segsize = 0;
 	int			i;
 	FixedParallelState *fps;
@@ -272,8 +274,10 @@ InitializeParallelDSM(ParallelContext *pcxt)
 		shm_toc_estimate_chunk(&pcxt->estimator, relmapperlen);
 		uncommittedenumslen = EstimateUncommittedEnumsSpace();
 		shm_toc_estimate_chunk(&pcxt->estimator, uncommittedenumslen);
+		clientconninfolen = EstimateClientConnectionInfoSpace();
+		shm_toc_estimate_chunk(&pcxt->estimator, clientconninfolen);
 		/* If you add more chunks here, you probably need to add keys. */
-		shm_toc_estimate_keys(&pcxt->estimator, 11);
+		shm_toc_estimate_keys(&pcxt->estimator, 12);
 
 		/* Estimate space need for error queues. */
 		StaticAssertStmt(BUFFERALIGN(PARALLEL_ERROR_QUEUE_SIZE) ==
@@ -352,6 +356,7 @@ InitializeParallelDSM(ParallelContext *pcxt)
 		char	   *session_dsm_handle_space;
 		char	   *entrypointstate;
 		char	   *uncommittedenumsspace;
+		char	   *clientconninfospace;
 		Size		lnamelen;
 
 		/* Serialize shared libraries we have loaded. */
@@ -422,6 +427,12 @@ InitializeParallelDSM(ParallelContext *pcxt)
 		shm_toc_insert(pcxt->toc, PARALLEL_KEY_UNCOMMITTEDENUMS,
 					   uncommittedenumsspace);
 
+		/* Serialize our ClientConnectionInfo. */
+		clientconninfospace = shm_toc_allocate(pcxt->toc, clientconninfolen);
+		SerializeClientConnectionInfo(clientconninfolen, clientconninfospace);
+		shm_toc_insert(pcxt->toc, PARALLEL_KEY_CLIENTCONNINFO,
+					   clientconninfospace);
+
 		/* Allocate space for worker information. */
 		pcxt->worker = palloc0(sizeof(ParallelWorkerInfo) * pcxt->nworkers);
 
@@ -1270,6 +1281,7 @@ ParallelWorkerMain(Datum main_arg)
 	char	   *reindexspace;
 	char	   *relmapperspace;
 	char	   *uncommittedenumsspace;
+	char	   *clientconninfospace;
 	StringInfoData msgbuf;
 	char	   *session_dsm_handle_space;
 	Snapshot	tsnapshot;
@@ -1479,6 +1491,11 @@ ParallelWorkerMain(Datum main_arg)
 										   false);
 	RestoreUncommittedEnums(uncommittedenumsspace);
 
+	/* Restore the ClientConnectionInfo. */
+	clientconninfospace = shm_toc_lookup(toc, PARALLEL_KEY_CLIENTCONNINFO,
+										 false);
+	RestoreClientConnectionInfo(clientconninfospace);
+
 	/* Attach to the leader's serializable transaction, if SERIALIZABLE. */
 	AttachSerializableXact(fps->serializable_xact_handle);
 
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index efc53f3135..6a499efecd 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -342,15 +342,15 @@ auth_failed(Port *port, int status, const char *logdetail)
  * authorization will fail later.
  *
  * The provided string will be copied into TopMemoryContext, to match the
- * lifetime of the Port, so it is safe to pass a string that is managed by an
- * external library.
+ * lifetime of MyClientConnectionInfo, so it is safe to pass a string that is
+ * managed by an external library.
  */
 static void
 set_authn_id(Port *port, const char *id)
 {
 	Assert(id);
 
-	if (port->authn_id)
+	if (MyClientConnectionInfo.authn_id)
 	{
 		/*
 		 * An existing authn_id should never be overwritten; that means two
@@ -361,17 +361,18 @@ set_authn_id(Port *port, const char *id)
 		ereport(FATAL,
 				(errmsg("authentication identifier set more than once"),
 				 errdetail_log("previous identifier: \"%s\"; new identifier: \"%s\"",
-							   port->authn_id, id)));
+							   MyClientConnectionInfo.authn_id, id)));
 	}
 
-	port->authn_id = MemoryContextStrdup(TopMemoryContext, id);
+	MyClientConnectionInfo.authn_id = MemoryContextStrdup(TopMemoryContext, id);
 
 	if (Log_connections)
 	{
 		ereport(LOG,
 				errmsg("connection authenticated: identity=\"%s\" method=%s "
 					   "(%s:%d)",
-					   port->authn_id, hba_authname(port->hba->auth_method), HbaFileName,
+					   MyClientConnectionInfo.authn_id,
+					   hba_authname(port->hba->auth_method), HbaFileName,
 					   port->hba->linenumber));
 	}
 }
@@ -1908,7 +1909,8 @@ auth_peer(hbaPort *port)
 	 */
 	set_authn_id(port, pw->pw_name);
 
-	ret = check_usermap(port->hba->usermap, port->user_name, port->authn_id, false);
+	ret = check_usermap(port->hba->usermap, port->user_name,
+						MyClientConnectionInfo.authn_id, false);
 
 	return ret;
 #else
diff --git a/src/backend/utils/adt/name.c b/src/backend/utils/adt/name.c
index 662a7943ed..97c827fb9a 100644
--- a/src/backend/utils/adt/name.c
+++ b/src/backend/utils/adt/name.c
@@ -275,10 +275,10 @@ session_user(PG_FUNCTION_ARGS)
 Datum
 pg_session_authn_id(PG_FUNCTION_ARGS)
 {
-	if (!MyProcPort || !MyProcPort->authn_id)
+	if (!MyClientConnectionInfo.authn_id)
 		PG_RETURN_NULL();
 
-	PG_RETURN_TEXT_P(cstring_to_text(MyProcPort->authn_id));
+	PG_RETURN_TEXT_P(cstring_to_text(MyClientConnectionInfo.authn_id));
 }
 
 
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
index b25bd0e583..1bbe1eaa17 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -932,6 +932,78 @@ GetUserNameFromId(Oid roleid, bool noerr)
 	return result;
 }
 
+/* ------------------------------------------------------------------------
+ *				Parallel connection state
+ *
+ * ClientConnectionInfo contains pieces of information about the client that
+ * need to be synced to parallel workers when they initialize. Over time, this
+ * list will probably grow, and may subsume some of the "user state" variables
+ * above.
+ *-------------------------------------------------------------------------
+ */
+
+ClientConnectionInfo MyClientConnectionInfo;
+
+/*
+ * Calculate the space needed to serialize MyClientConnectionInfo.
+ */
+Size
+EstimateClientConnectionInfoSpace(void)
+{
+	Size		size = 1;
+
+	if (MyClientConnectionInfo.authn_id)
+		size = add_size(size, strlen(MyClientConnectionInfo.authn_id) + 1);
+
+	return size;
+}
+
+/*
+ * Serialize MyClientConnectionInfo for use by parallel workers.
+ */
+void
+SerializeClientConnectionInfo(Size maxsize, char *start_address)
+{
+	/*
+	 * First byte is an indication of whether or not authn_id has been set to
+	 * non-NULL, to differentiate that case from the empty string.
+	 */
+	Assert(maxsize > 0);
+	start_address[0] = MyClientConnectionInfo.authn_id ? 1 : 0;
+	start_address++;
+	maxsize--;
+
+	if (MyClientConnectionInfo.authn_id)
+	{
+		Size len;
+
+		len = strlcpy(start_address, MyClientConnectionInfo.authn_id, maxsize) + 1;
+		Assert(len <= maxsize);
+		maxsize -= len;
+		start_address += len;
+	}
+}
+
+/*
+ * Restore MyClientConnectionInfo from its serialized representation.
+ */
+void
+RestoreClientConnectionInfo(char *conninfo)
+{
+	if (conninfo[0] == 0)
+	{
+		MyClientConnectionInfo.authn_id = NULL;
+		conninfo++;
+	}
+	else
+	{
+		conninfo++;
+		MyClientConnectionInfo.authn_id = MemoryContextStrdup(TopMemoryContext,
+															  conninfo);
+		conninfo += strlen(conninfo) + 1;
+	}
+}
+
 
 /*-------------------------------------------------------------------------
  *				Interlock-file support
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 8e181b4771..d4fa9d32dd 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -1509,7 +1509,7 @@
   proname => 'session_user', provolatile => 's', prorettype => 'name',
   proargtypes => '', prosrc => 'session_user' },
 { oid => '9774', descr => 'session authenticated identity',
-  proname => 'pg_session_authn_id', provolatile => 's', proparallel => 'r',
+  proname => 'pg_session_authn_id', provolatile => 's',
   prorettype => 'text', proargtypes => '', prosrc => 'pg_session_authn_id' },
 
 { oid => '744',
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 90c20da22b..c900411fdd 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -98,6 +98,31 @@ typedef struct
 } pg_gssinfo;
 #endif
 
+/*
+ * Fields describing the client connection, that also need to be copied over to
+ * parallel workers, go into the ClientConnectionInfo rather than Port. The same
+ * rules apply for allocations here as for Port (must be malloc'd or palloc'd in
+ * TopMemoryContext).
+ *
+ * If you add a struct member here, remember to also handle serialization in
+ * SerializeClientConnectionInfo() et al.
+ */
+typedef struct
+{
+	/*
+	 * Authenticated identity.  The meaning of this identifier is dependent on
+	 * hba->auth_method; it is the identity (if any) that the user presented
+	 * during the authentication cycle, before they were assigned a database
+	 * role.  (It is effectively the "SYSTEM-USERNAME" of a pg_ident usermap
+	 * -- though the exact string in use may be different, depending on pg_hba
+	 * options.)
+	 *
+	 * authn_id is NULL if the user has not actually been authenticated, for
+	 * example if the "trust" auth method is in use.
+	 */
+	const char *authn_id;
+} ClientConnectionInfo;
+
 /*
  * This is used by the postmaster in its communication with frontends.  It
  * contains all state information needed during this communication before the
@@ -158,19 +183,6 @@ typedef struct Port
 	 */
 	HbaLine    *hba;
 
-	/*
-	 * Authenticated identity.  The meaning of this identifier is dependent on
-	 * hba->auth_method; it is the identity (if any) that the user presented
-	 * during the authentication cycle, before they were assigned a database
-	 * role.  (It is effectively the "SYSTEM-USERNAME" of a pg_ident usermap
-	 * -- though the exact string in use may be different, depending on pg_hba
-	 * options.)
-	 *
-	 * authn_id is NULL if the user has not actually been authenticated, for
-	 * example if the "trust" auth method is in use.
-	 */
-	const char *authn_id;
-
 	/*
 	 * TCP keepalive and user timeout settings.
 	 *
@@ -327,6 +339,7 @@ extern ssize_t be_gssapi_write(Port *port, void *ptr, size_t len);
 #endif							/* ENABLE_GSS */
 
 extern PGDLLIMPORT ProtocolVersion FrontendProtocol;
+extern PGDLLIMPORT ClientConnectionInfo MyClientConnectionInfo;
 
 /* TCP keepalives configuration. These are no-ops on an AF_UNIX socket. */
 
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 0af130fbc5..c06796fe4a 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -486,6 +486,10 @@ extern bool has_rolreplication(Oid roleid);
 typedef void (*shmem_request_hook_type) (void);
 extern PGDLLIMPORT shmem_request_hook_type shmem_request_hook;
 
+extern Size EstimateClientConnectionInfoSpace(void);
+extern void SerializeClientConnectionInfo(Size maxsize, char *start_address);
+extern void RestoreClientConnectionInfo(char *procinfo);
+
 /* in executor/nodeHash.c */
 extern size_t get_hash_memory_limit(void);
 
diff --git a/src/test/authentication/t/001_password.pl b/src/test/authentication/t/001_password.pl
index f0bdeda52d..3f8629b3a6 100644
--- a/src/test/authentication/t/001_password.pl
+++ b/src/test/authentication/t/001_password.pl
@@ -74,6 +74,14 @@ $node->safe_psql('postgres',
 );
 $ENV{"PGPASSWORD"} = 'pass';
 
+# Set up a table for parallel worker testing.
+$node->safe_psql('postgres',
+	'CREATE TABLE nulls (n) AS SELECT NULL FROM generate_series(1, 200000);'
+);
+$node->safe_psql('postgres',
+	'GRANT SELECT ON nulls TO md5_role;'
+);
+
 # For "trust" method, all users should be able to connect. These users are not
 # considered to be authenticated.
 reset_pg_hba($node, 'trust');
@@ -86,6 +94,19 @@ my $res =
   $node->safe_psql('postgres', "SELECT pg_session_authn_id() IS NULL;");
 is($res, 't', "users with trust authentication have NULL authn_id");
 
+# Test pg_session_authn_id() with parallel workers.
+$res = $node->safe_psql(
+	'postgres', '
+		SET min_parallel_table_scan_size TO 0;
+		SET parallel_setup_cost TO 0;
+		SET parallel_tuple_cost TO 0;
+		SET max_parallel_workers_per_gather TO 2;
+
+		SELECT bool_and(pg_session_authn_id() IS NOT DISTINCT FROM n) FROM nulls;
+	',
+	connstr => "user=md5_role");
+is($res, 't', "parallel workers return a null authn_id when not authenticated");
+
 # For plain "password" method, all users should also be able to connect.
 reset_pg_hba($node, 'password');
 test_role($node, 'scram_role', 'password', 0,
@@ -102,6 +123,18 @@ $res = $node->safe_psql(
 is($res, 'md5_role',
 	"users with md5 authentication have authn_id matching role name");
 
+$res = $node->safe_psql(
+	'postgres', '
+		SET min_parallel_table_scan_size TO 0;
+		SET parallel_setup_cost TO 0;
+		SET parallel_tuple_cost TO 0;
+		SET max_parallel_workers_per_gather TO 2;
+
+		SELECT bool_and(pg_session_authn_id() IS DISTINCT FROM n) FROM nulls;
+	',
+	connstr => "user=md5_role");
+is($res, 't', "parallel workers return a non-null authn_id when authenticated");
+
 # For "scram-sha-256" method, user "scram_role" should be able to connect.
 reset_pg_hba($node, 'scram-sha-256');
 test_role(
-- 
2.25.1

From 31a9d3ab7928d41c8e5d4778893455a31defc6a6 Mon Sep 17 00:00:00 2001
From: Jacob Champion <pchamp...@vmware.com>
Date: Mon, 14 Feb 2022 08:10:53 -0800
Subject: [PATCH v11 1/2] Add API to retrieve authn_id from SQL

The authn_id field in MyProcPort is currently only accessible to the
backend itself.  Add a SQL function, pg_session_authn_id(), to expose
the field to triggers that may want to make use of it.
---
 doc/src/sgml/func.sgml                    | 26 +++++++++++++++++++++++
 src/backend/utils/adt/name.c              | 12 ++++++++++-
 src/include/catalog/pg_proc.dat           |  3 +++
 src/test/authentication/t/001_password.pl | 11 ++++++++++
 src/test/ssl/t/001_ssltests.pl            |  7 ++++++
 5 files changed, 58 insertions(+), 1 deletion(-)

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 478a216dbb..b45659b609 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -23344,6 +23344,32 @@ SELECT * FROM pg_ls_dir('.') WITH ORDINALITY AS t(ls,n);
        </para></entry>
       </row>
 
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_session_authn_id</primary>
+        </indexterm>
+        <function>pg_session_authn_id</function> ()
+        <returnvalue>text</returnvalue>
+       </para>
+       <para>
+        Returns the authenticated identity for the current connection, or
+        <literal>NULL</literal> if the user has not been authenticated.
+       </para>
+       <para>
+        The authenticated identity is an immutable identifier for the user
+        presented during the connection handshake; the exact format depends on
+        the authentication method in use. (For example, when using the
+        <literal>scram-sha-256</literal> auth method, the authenticated identity
+        is simply the username. When using the <literal>cert</literal> auth
+        method, the authenticated identity is the Distinguished Name of the
+        client certificate.) Even for auth methods which use the username as
+        the authenticated identity, this function differs from
+        <literal>session_user</literal> in that its return value cannot be
+        changed after login.
+       </para></entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
diff --git a/src/backend/utils/adt/name.c b/src/backend/utils/adt/name.c
index e8bba3670c..662a7943ed 100644
--- a/src/backend/utils/adt/name.c
+++ b/src/backend/utils/adt/name.c
@@ -23,6 +23,7 @@
 #include "catalog/namespace.h"
 #include "catalog/pg_collation.h"
 #include "catalog/pg_type.h"
+#include "libpq/libpq-be.h"
 #include "libpq/pqformat.h"
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
@@ -257,7 +258,7 @@ namestrcmp(Name name, const char *str)
 
 
 /*
- * SQL-functions CURRENT_USER, SESSION_USER
+ * SQL-functions CURRENT_USER, SESSION_USER, PG_SESSION_AUTHN_ID
  */
 Datum
 current_user(PG_FUNCTION_ARGS)
@@ -271,6 +272,15 @@ session_user(PG_FUNCTION_ARGS)
 	PG_RETURN_DATUM(DirectFunctionCall1(namein, CStringGetDatum(GetUserNameFromId(GetSessionUserId(), false))));
 }
 
+Datum
+pg_session_authn_id(PG_FUNCTION_ARGS)
+{
+	if (!MyProcPort || !MyProcPort->authn_id)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(MyProcPort->authn_id));
+}
+
 
 /*
  * SQL-functions CURRENT_SCHEMA, CURRENT_SCHEMAS
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 87aa571a33..8e181b4771 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -1508,6 +1508,9 @@
 { oid => '746', descr => 'session user name',
   proname => 'session_user', provolatile => 's', prorettype => 'name',
   proargtypes => '', prosrc => 'session_user' },
+{ oid => '9774', descr => 'session authenticated identity',
+  proname => 'pg_session_authn_id', provolatile => 's', proparallel => 'r',
+  prorettype => 'text', proargtypes => '', prosrc => 'pg_session_authn_id' },
 
 { oid => '744',
   proname => 'array_eq', prorettype => 'bool',
diff --git a/src/test/authentication/t/001_password.pl b/src/test/authentication/t/001_password.pl
index 3e3079c824..f0bdeda52d 100644
--- a/src/test/authentication/t/001_password.pl
+++ b/src/test/authentication/t/001_password.pl
@@ -82,6 +82,10 @@ test_role($node, 'scram_role', 'trust', 0,
 test_role($node, 'md5_role', 'trust', 0,
 	log_unlike => [qr/connection authenticated:/]);
 
+my $res =
+  $node->safe_psql('postgres', "SELECT pg_session_authn_id() IS NULL;");
+is($res, 't', "users with trust authentication have NULL authn_id");
+
 # For plain "password" method, all users should also be able to connect.
 reset_pg_hba($node, 'password');
 test_role($node, 'scram_role', 'password', 0,
@@ -91,6 +95,13 @@ test_role($node, 'md5_role', 'password', 0,
 	log_like =>
 	  [qr/connection authenticated: identity="md5_role" method=password/]);
 
+$res = $node->safe_psql(
+	'postgres',
+	"SELECT pg_session_authn_id();",
+	connstr => "user=md5_role");
+is($res, 'md5_role',
+	"users with md5 authentication have authn_id matching role name");
+
 # For "scram-sha-256" method, user "scram_role" should be able to connect.
 reset_pg_hba($node, 'scram-sha-256');
 test_role(
diff --git a/src/test/ssl/t/001_ssltests.pl b/src/test/ssl/t/001_ssltests.pl
index c0b4a5739c..2941eb0bde 100644
--- a/src/test/ssl/t/001_ssltests.pl
+++ b/src/test/ssl/t/001_ssltests.pl
@@ -562,6 +562,13 @@ $node->connect_ok(
 		qr/connection authenticated: identity="CN=ssltestuser-dn,OU=Testing,OU=Engineering,O=PGDG" method=cert/
 	],);
 
+# Sanity-check pg_session_authn_id() for long ID strings
+my $res = $node->safe_psql('postgres',
+	"SELECT pg_session_authn_id();",
+	connstr => "$dn_connstr user=ssltestuser sslcert=ssl/client-dn.crt " . sslkey('client-dn.key'),
+);
+is($res, "CN=ssltestuser-dn,OU=Testing,OU=Engineering,O=PGDG", "users with cert authentication have entire DN as authn_id");
+
 # same thing but with a regex
 $dn_connstr = "$common_connstr dbname=certdb_dn_re";
 
-- 
2.25.1

Reply via email to