Hi

rebased version + fix warning possibly uninitialized variable

Regards

Pavel
diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml
index b11d9a6ba3..f774ffa310 100644
--- a/doc/src/sgml/protocol.sgml
+++ b/doc/src/sgml/protocol.sgml
@@ -5401,6 +5401,43 @@ psql "dbname=postgres replication=database" -c "IDENTIFY_SYSTEM;"
     </listitem>
    </varlistentry>
 
+   <varlistentry id="protocol-message-formats-ReportGUC">
+    <term>ReportGUC (F)</term>
+    <listitem>
+     <variablelist>
+      <varlistentry>
+       <term>Byte1('r')</term>
+       <listitem>
+        <para>
+         Identifies the message type.  ReportGUC is sent by
+         frontend when the changes of specified GUC option
+         should be (or should not be) reported to state parameter.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>String</term>
+       <listitem>
+        <para>
+         The name of GUC option.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Int16</term>
+       <listitem>
+        <para>
+         1 if reporting should be enables, 0 if reporting should be
+         disabled.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </listitem>
+   </varlistentry>
+
    <varlistentry id="protocol-message-formats-RowDescription">
     <term>RowDescription (B)</term>
     <listitem>
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index dc422373d6..569f50fe7d 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -4551,7 +4551,24 @@ testdb=&gt; <userinput>INSERT INTO my_table VALUES (:'content');</userinput>
         <listitem><para>The port number at which the database server is listening.</para></listitem>
       </varlistentry>
 
-      <varlistentry id="app-psql-prompting-n">
+      <varlistentry id="app-psql-prompting-n-uc">
+        <term><literal>%N</literal></term>
+        <listitem>
+         <para>
+          The database role name. This value is specified by command
+          <command>SET ROLE</command>. Until execution of this command
+          the value is set to the database session user name.
+         </para>
+
+         <para>
+          This substitution requires <productname>PostgreSQL</productname>
+          version 16 and up. When you use older version, the empty string
+          is used instead.
+         </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry id="app-psql-prompting-n-lc">
         <term><literal>%n</literal></term>
         <listitem>
          <para>
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 01b6cc1f7d..54a3f5c1e9 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -451,6 +451,11 @@ SocketBackend(StringInfo inBuf)
 			doing_extended_query_message = false;
 			break;
 
+		case 'r':				/* report GUC */
+			maxmsglen = PQ_SMALL_MESSAGE_LIMIT;
+			doing_extended_query_message = false;
+			break;
+
 		default:
 
 			/*
@@ -4823,6 +4828,24 @@ PostgresMain(const char *dbname, const char *username)
 					pq_flush();
 				break;
 
+			case 'r':			/* report GUC */
+				{
+					const char	   *name;
+					int				create_flag;
+
+					name = pq_getmsgstring(&input_message);
+					create_flag = pq_getmsgint(&input_message, 2);
+					pq_getmsgend(&input_message);
+
+					if (create_flag)
+						SetGUCOptionFlag(name, GUC_REPORT);
+					else
+					UnsetGUCOptionFlag(name, GUC_REPORT);
+
+					send_ready_for_query = true;
+				}
+				break;
+
 			case 'S':			/* sync */
 				pq_getmsgend(&input_message);
 				finish_xact_command();
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 9dd624b3ae..e0dccd1fa3 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -2531,6 +2531,37 @@ BeginReportingGUCOptions(void)
 	}
 }
 
+/*
+ * Allow to set / unset dynamicaly flags to GUC variables
+ */
+void
+SetGUCOptionFlag(const char *name, int flag)
+{
+	struct config_generic *conf;
+
+	/* only GUC_REPORT flag is supported now */
+	Assert(flag == GUC_REPORT);
+
+	conf = find_option(name, false, true, ERROR);
+	conf->flags |= flag;
+
+	if (flag == GUC_REPORT)
+		/* force transmit value of related option to client Parameter Status */
+		ReportGUCOption(conf);
+}
+
+void
+UnsetGUCOptionFlag(const char *name, int flag)
+{
+	struct config_generic *conf;
+
+	/* only GUC_REPORT flag is supported now */
+	Assert(flag == GUC_REPORT);
+
+	conf = find_option(name, false, true, ERROR);
+	conf->flags &= ~flag;
+}
+
 /*
  * ReportChangedGUCOptions: report recently-changed GUC_REPORT variables
  *
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 97f7d97220..5276da54fc 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -3859,6 +3859,7 @@ SyncVariables(void)
 {
 	char		vbuf[32];
 	const char *server_version;
+	int			res;
 
 	/* get stuff from connection */
 	pset.encoding = PQclientEncoding(pset.db);
@@ -3888,6 +3889,15 @@ SyncVariables(void)
 	/* send stuff to it, too */
 	PQsetErrorVerbosity(pset.db, pset.verbosity);
 	PQsetErrorContextVisibility(pset.db, pset.show_context);
+
+	/* link role GUC when it is needed for prompt */
+	if (pset.prompt_shows_role)
+		res = PQlinkParameterStatus(pset.db, "role");
+	else
+		res = PQunlinkParameterStatus(pset.db, "role");
+
+	if (res < 0)
+		pg_log_info("%s", PQerrorMessage(pset.db));
 }
 
 /*
diff --git a/src/bin/psql/prompt.c b/src/bin/psql/prompt.c
index 969cd9908e..b0f5158c59 100644
--- a/src/bin/psql/prompt.c
+++ b/src/bin/psql/prompt.c
@@ -165,6 +165,41 @@ get_prompt(promptStatus_t status, ConditionalStack cstack)
 					if (pset.db)
 						strlcpy(buf, session_username(), sizeof(buf));
 					break;
+					/* DB server user role */
+				case 'N':
+					if (pset.db)
+					{
+						int			minServerMajor;
+						int			serverMajor;
+						const char *rolename = NULL;
+
+						/*
+						 * This feature requires GUC "role" to be marked
+						 * as GUC_REPORT. Without it is hard to specify fallback
+						 * result. Returning empty value can be messy, returning
+						 * PQuser like session_username can be messy too.
+						 * Exec query is not too practical too, because it doesn't
+						 * work when session is not in transactional state, and
+						 * CURRENT_ROLE returns different result when role is not
+						 * explicitly specified by SET ROLE.
+						 */
+						minServerMajor = 1600;
+						serverMajor = PQserverVersion(pset.db) / 100;
+						if (serverMajor >= minServerMajor)
+						{
+							rolename  = PQparameterStatus(pset.db, "role");
+
+							/* fallback when role is not set yet */
+							if (rolename && strcmp(rolename, "none") == 0)
+								rolename = session_username();
+						}
+
+						if (rolename)
+							strlcpy(buf, rolename, sizeof(buf));
+						else
+							buf[0] = '\0';
+					}
+					break;
 					/* backend pid */
 				case 'p':
 					if (pset.db)
diff --git a/src/bin/psql/settings.h b/src/bin/psql/settings.h
index 73d4b393bc..2417fe25f0 100644
--- a/src/bin/psql/settings.h
+++ b/src/bin/psql/settings.h
@@ -153,6 +153,7 @@ typedef struct _psqlSettings
 	PGVerbosity verbosity;		/* current error verbosity level */
 	bool		show_all_results;
 	PGContextVisibility show_context;	/* current context display level */
+	bool		prompt_shows_role;
 } PsqlSettings;
 
 extern PsqlSettings pset;
diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c
index 5a28b6f713..33ee0057dc 100644
--- a/src/bin/psql/startup.c
+++ b/src/bin/psql/startup.c
@@ -1094,10 +1094,34 @@ histcontrol_hook(const char *newval)
 	return true;
 }
 
+static void
+prompt_needs_role_parameter_status(void)
+{
+	int		res;
+
+	pset.prompt_shows_role = false;
+
+	if (pset.prompt1 && strstr(pset.prompt1, "%N"))
+		pset.prompt_shows_role = true;
+	else if (pset.prompt2 && strstr(pset.prompt2, "%N"))
+		pset.prompt_shows_role = true;
+	else if (pset.prompt3 && strstr(pset.prompt3, "%N"))
+		pset.prompt_shows_role = true;
+
+	if (pset.prompt_shows_role)
+		res = PQlinkParameterStatus(pset.db, "role");
+	else
+		res = PQunlinkParameterStatus(pset.db, "role");
+
+	if (res < 0)
+		pg_log_info("%s", PQerrorMessage(pset.db));
+}
+
 static bool
 prompt1_hook(const char *newval)
 {
 	pset.prompt1 = newval ? newval : "";
+	prompt_needs_role_parameter_status();
 	return true;
 }
 
@@ -1105,6 +1129,7 @@ static bool
 prompt2_hook(const char *newval)
 {
 	pset.prompt2 = newval ? newval : "";
+	prompt_needs_role_parameter_status();
 	return true;
 }
 
@@ -1112,6 +1137,7 @@ static bool
 prompt3_hook(const char *newval)
 {
 	pset.prompt3 = newval ? newval : "";
+	prompt_needs_role_parameter_status();
 	return true;
 }
 
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index ba89d013e6..bee8097ccd 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -370,6 +370,8 @@ extern void ResetAllOptions(void);
 extern void AtStart_GUC(void);
 extern int	NewGUCNestLevel(void);
 extern void AtEOXact_GUC(bool isCommit, int nestLevel);
+extern void SetGUCOptionFlag(const char *name, int flag);
+extern void UnsetGUCOptionFlag(const char *name, int flag);
 extern void BeginReportingGUCOptions(void);
 extern void ReportChangedGUCOptions(void);
 extern void ParseLongOption(const char *string, char **name, char **value);
diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/exports.txt
index 7ded77aff3..4bc74088d7 100644
--- a/src/interfaces/libpq/exports.txt
+++ b/src/interfaces/libpq/exports.txt
@@ -187,3 +187,5 @@ PQsetTraceFlags           184
 PQmblenBounded            185
 PQsendFlushRequest        186
 PQconnectionUsedGSSAPI    187
+PQlinkParameterStatus     187
+PQunlinkParameterStatus   188
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index a16bbf32ef..7620c4c984 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -1069,6 +1069,25 @@ pqSaveMessageField(PGresult *res, char code, const char *value)
 	res->errFields = pfield;
 }
 
+/*
+ * Add GUC_REPORT flag to specified setting and wait for synchronization
+ * with state parameters.
+ */
+int
+PQlinkParameterStatus(PGconn *conn, const char *paramName)
+{
+	return pqSendReportGUCMessage(conn, paramName, true);
+}
+
+/*
+ * Remove GUC_REPORT flag from specified setting.
+ */
+int
+PQunlinkParameterStatus(PGconn *conn, const char *paramName)
+{
+	return pqSendReportGUCMessage(conn, paramName, false);
+}
+
 /*
  * pqSaveParameterStatus - remember parameter status sent by backend
  */
diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c
index 8ab6a88416..8025e63e0b 100644
--- a/src/interfaces/libpq/fe-protocol3.c
+++ b/src/interfaces/libpq/fe-protocol3.c
@@ -1981,6 +1981,152 @@ pqEndcopy3(PGconn *conn)
 	return 1;
 }
 
+/*
+ * sends report GUC message, and wait and process related backend
+ * messages until 'backend is ready' including this message. Only
+ * allowed backend massages are 'N' and 'E'.
+ */
+int
+pqSendReportGUCMessage(PGconn *conn, const char *paramName, bool create_flag)
+{
+	bool		needInput = false;
+	bool		is_error = false;
+	char		id;
+	int			msgLength;
+	int			avail;
+
+	if (!conn)
+		return 0;
+
+	/* construct Report GUC message */
+	if (pqPutMsgStart('r', conn) < 0 ||
+		pqPuts(paramName, conn) < 0 ||
+		pqPutInt(create_flag ? 1 : 0, 2, conn) < 0 ||
+		pqPutMsgEnd(conn) < 0 ||
+		pqFlush(conn))
+		return -1;
+
+	if (pqWait(true, false, conn) ||
+		pqReadData(conn) < 0)
+		return -1;
+
+	for (;;)
+	{
+		if (needInput)
+		{
+			/* Wait for some data to arrive (or for the channel to close) */
+			if (pqWait(true, false, conn) ||
+				pqReadData(conn) < 0)
+				break;
+		}
+
+		/*
+		 * Scan the message. If we run out of data, loop around to try again.
+		 */
+		needInput = true;
+
+		conn->inCursor = conn->inStart;
+		if (pqGetc(&id, conn))
+			continue;
+		if (pqGetInt(&msgLength, 4, conn))
+			continue;
+
+		/*
+		 * Try to validate message type/length here.  A length less than 4 is
+		 * definitely broken.  Large lengths should only be believed for a few
+		 * message types.
+		 */
+		if (msgLength < 4)
+		{
+			handleSyncLoss(conn, id, msgLength);
+			break;
+		}
+		if (msgLength > 30000 && !VALID_LONG_MESSAGE_TYPE(id))
+		{
+			handleSyncLoss(conn, id, msgLength);
+			break;
+		}
+
+		/*
+		 * Can't process if message body isn't all here yet.
+		 */
+		msgLength -= 4;
+		avail = conn->inEnd - conn->inCursor;
+		if (avail < msgLength)
+		{
+			/*
+			 * Before looping, enlarge the input buffer if needed to hold the
+			 * whole message.  See notes in parseInput.
+			 */
+			if (pqCheckInBufferSpace(conn->inCursor + (size_t) msgLength,
+									 conn))
+			{
+				/*
+				 * XXX add some better recovery code... plan is to skip over
+				 * the message using its length, then report an error. For the
+				 * moment, just treat this like loss of sync (which indeed it
+				 * might be!)
+				 */
+				handleSyncLoss(conn, id, msgLength);
+				break;
+			}
+			continue;
+		}
+
+		/*
+		 * We should see V or E response to the command, but might get N
+		 * and/or A notices first. We also need to swallow the final Z before
+		 * returning.
+		 */
+		switch (id)
+		{
+			case 'E':			/* error return */
+				if (pqGetErrorNotice3(conn, true))
+					continue;
+				is_error = true;
+				break;
+			case 'N':			/* notice */
+				/* handle notice and go back to processing return values */
+				if (pqGetErrorNotice3(conn, false))
+					continue;
+				break;
+			case 'Z':			/* backend is ready for new query */
+				if (getReadyForQuery(conn))
+					continue;
+				/* consume the message and exit */
+				conn->inStart += 5 + msgLength;
+
+				return !is_error ? 0 : -1;
+			case 'S':			/* parameter status */
+				if (getParameterStatus(conn))
+					continue;
+				break;
+			default:
+				/* The backend violates the protocol. */
+				libpq_append_conn_error(conn, "protocol error: id=0x%x", id);
+
+				/* trust the specified message length as what to skip */
+				conn->inStart += 5 + msgLength;
+				return -1;
+		}
+
+		/* trace server-to-client message */
+		if (conn->Pfdebug)
+			pqTraceOutputMessage(conn, conn->inBuffer + conn->inStart, false);
+
+		/* Completed this message, keep going */
+		/* trust the specified message length as what to skip */
+		conn->inStart += 5 + msgLength;
+		needInput = false;
+	}
+
+	/*
+	 * We fall out of the loop only upon failing to read data.
+	 * conn->errorMessage has been set by pqWait or pqReadData.
+	 */
+
+	return -1;
+}
 
 /*
  * PQfn - Send a function call to the POSTGRES backend.
diff --git a/src/interfaces/libpq/fe-trace.c b/src/interfaces/libpq/fe-trace.c
index 402784f40e..d73ef9f25f 100644
--- a/src/interfaces/libpq/fe-trace.c
+++ b/src/interfaces/libpq/fe-trace.c
@@ -522,6 +522,15 @@ pqTraceOutputZ(FILE *f, const char *message, int *cursor)
 	pqTraceOutputByte1(f, message, cursor);
 }
 
+/* ReportGUC */
+static void
+pqTraceOutputr(FILE *f, const char *message, int *cursor)
+{
+	fprintf(f, "ReportGUC\t");
+	pqTraceOutputString(f, message, cursor, false);
+	pqTraceOutputInt16(f, message, cursor);
+}
+
 /*
  * Print the given message to the trace output stream.
  */
@@ -633,6 +642,9 @@ pqTraceOutputMessage(PGconn *conn, const char *message, bool toServer)
 		case 'Q':				/* Query */
 			pqTraceOutputQ(conn->Pfdebug, message, &logCursor);
 			break;
+		case 'r':				/* Report GUC */
+			pqTraceOutputr(conn->Pfdebug, message, &logCursor);
+			break;
 		case 'R':				/* Authentication */
 			pqTraceOutputR(conn->Pfdebug, message, &logCursor);
 			break;
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index 7476dbe0e9..64ec8d9299 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -587,6 +587,9 @@ extern unsigned char *PQescapeBytea(const unsigned char *from, size_t from_lengt
 									size_t *to_length);
 
 
+/* Control of dynamic propagation settings to state parameters */
+extern int PQlinkParameterStatus(PGconn *conn, const char *paramName);
+extern int PQunlinkParameterStatus(PGconn *conn, const char *paramName);
 
 /* === in fe-print.c === */
 
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index ce0167c1b6..6027d24894 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -725,6 +725,7 @@ extern PGresult *pqFunctionCall3(PGconn *conn, Oid fnid,
 								 int *result_buf, int *actual_result_len,
 								 int result_is_int,
 								 const PQArgBlock *args, int nargs);
+extern int pqSendReportGUCMessage(PGconn *conn, const char *paramName, bool create_flag);
 
 /* === in fe-misc.c === */
 

Reply via email to