doc/src/sgml/config.sgml                 |   2 +-
 doc/src/sgml/monitoring.sgml             |  14 ++++
 src/backend/access/heap/vacuumlazy.c     |   2 +-
 src/backend/access/nbtree/nbtsort.c      |   2 +-
 src/backend/catalog/system_views.sql     |   1 +
 src/backend/executor/execParallel.c      |  24 ++++++-
 src/backend/postmaster/autovacuum.c      |   4 +-
 src/backend/postmaster/pgstat.c          |  57 +++++++++++++++-
 src/backend/replication/logical/worker.c |   6 +-
 src/backend/replication/walsender.c      |   6 +-
 src/backend/tcop/postgres.c              |  58 ++++++++++++----
 src/backend/utils/adt/pgstatfuncs.c      | 114 +++++++++++++++++--------------
 src/include/catalog/pg_proc.dat          |   6 +-
 src/include/pgstat.h                     |  11 ++-
 src/test/modules/worker_spi/worker_spi.c |   8 +--
 src/test/regress/expected/rules.out      |   9 +--
 16 files changed, 230 insertions(+), 94 deletions(-)
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 7a7177c550..d2b415629b 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -7230,7 +7230,7 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
        
        Specifies the amount of memory reserved to store the text of the
        currently executing command for each active session, for the
-       pg_stat_activity.query field.
+       pg_stat_activity.query and individual_query fields.
        If this value is specified without units, it is taken as bytes.
        The default value is 1024 bytes.
        This parameter can only be set at server start.
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 7dcddf478a..71782e9ffa 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -896,6 +896,20 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
       
      
 
+     
+      
+       individual_query text
+      
+      
+       Text of this backend's most recent individual query in case query contains multiple statements. If
+       state is active this field shows the
+       currently executing individual query. In all other states, it shows the last individual query
+       that was executed. By default the individual query text is truncated at 1024
+       bytes; this value can be changed via the parameter
+       .
+      
+     
+
      
       
        backend_type text
diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c
index 1bbc4598f7..f9a0ca4bdf 100644
--- a/src/backend/access/heap/vacuumlazy.c
+++ b/src/backend/access/heap/vacuumlazy.c
@@ -3489,7 +3489,7 @@ parallel_vacuum_main(dsm_segment *seg, shm_toc *toc)
 	/* Set debug_query_string for individual workers */
 	sharedquery = shm_toc_lookup(toc, PARALLEL_VACUUM_KEY_QUERY_TEXT, false);
 	debug_query_string = sharedquery;
-	pgstat_report_activity(STATE_RUNNING, debug_query_string);
+	pgstat_report_activity(STATE_RUNNING, debug_query_string, NULL);
 
 	/*
 	 * Open table.  The lock mode is the same as the leader process.  It's
diff --git a/src/backend/access/nbtree/nbtsort.c b/src/backend/access/nbtree/nbtsort.c
index efee86784b..11494a2f7d 100644
--- a/src/backend/access/nbtree/nbtsort.c
+++ b/src/backend/access/nbtree/nbtsort.c
@@ -1810,7 +1810,7 @@ _bt_parallel_build_main(dsm_segment *seg, shm_toc *toc)
 	debug_query_string = sharedquery;
 
 	/* Report the query string from leader */
-	pgstat_report_activity(STATE_RUNNING, debug_query_string);
+	pgstat_report_activity(STATE_RUNNING, debug_query_string, NULL);
 
 	/* Look up nbtree shared state */
 	btshared = shm_toc_lookup(toc, PARALLEL_KEY_BTREE_SHARED, false);
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 8625cbeab6..a411f8b548 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -759,6 +759,7 @@ CREATE VIEW pg_stat_activity AS
             S.backend_xid,
             s.backend_xmin,
             S.query,
+            S.individual_query,
             S.backend_type
     FROM pg_stat_get_activity(NULL) AS S
         LEFT JOIN pg_database AS D ON (S.datid = D.oid)
diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c
index 382e78fb7f..19ebaf13b3 100644
--- a/src/backend/executor/execParallel.c
+++ b/src/backend/executor/execParallel.c
@@ -65,6 +65,7 @@
 #define PARALLEL_KEY_QUERY_TEXT		UINT64CONST(0xE000000000000008)
 #define PARALLEL_KEY_JIT_INSTRUMENTATION UINT64CONST(0xE000000000000009)
 #define PARALLEL_KEY_WAL_USAGE			UINT64CONST(0xE00000000000000A)
+#define PARALLEL_KEY_INDIVIDUAL_QUERY_TEXT		UINT64CONST(0xE00000000000000B)
 
 #define PARALLEL_TUPLE_QUEUE_SIZE		65536
 
@@ -600,7 +601,9 @@ ExecInitParallelPlan(PlanState *planstate, EState *estate,
 	int			instrument_offset = 0;
 	Size		dsa_minsize = dsa_minimum_size();
 	char	   *query_string;
+	char	   *individual_query_string;
 	int			query_len;
+	int			individual_query_len;
 
 	/*
 	 * Force any initplan outputs that we're going to pass to workers to be
@@ -638,6 +641,15 @@ ExecInitParallelPlan(PlanState *planstate, EState *estate,
 						   sizeof(FixedParallelExecutorState));
 	shm_toc_estimate_keys(&pcxt->estimator, 1);
 
+	/* Estimate space for individual query text. */
+	if (estate->es_plannedstmt->stmt_len == 0)
+		individual_query_len = strlen(estate->es_sourceText) - estate->es_plannedstmt->stmt_location;
+	else
+		individual_query_len = estate->es_plannedstmt->stmt_len;
+
+	shm_toc_estimate_chunk(&pcxt->estimator, individual_query_len + 1);
+	shm_toc_estimate_keys(&pcxt->estimator, 1);
+
 	/* Estimate space for query text. */
 	query_len = strlen(estate->es_sourceText);
 	shm_toc_estimate_chunk(&pcxt->estimator, query_len + 1);
@@ -732,6 +744,11 @@ ExecInitParallelPlan(PlanState *planstate, EState *estate,
 	fpes->jit_flags = estate->es_jit_flags;
 	shm_toc_insert(pcxt->toc, PARALLEL_KEY_EXECUTOR_FIXED, fpes);
 
+	/* Store individual query string */
+	individual_query_string = shm_toc_allocate(pcxt->toc, individual_query_len + 1);
+	memcpy(individual_query_string, estate->es_sourceText + estate->es_plannedstmt->stmt_location, individual_query_len + 1);
+	shm_toc_insert(pcxt->toc, PARALLEL_KEY_INDIVIDUAL_QUERY_TEXT, individual_query_string);
+
 	/* Store query string */
 	query_string = shm_toc_allocate(pcxt->toc, query_len + 1);
 	memcpy(query_string, estate->es_sourceText, query_len + 1);
@@ -1388,6 +1405,7 @@ ParallelQueryMain(dsm_segment *seg, shm_toc *toc)
 	void	   *area_space;
 	dsa_area   *area;
 	ParallelWorkerContext pwcxt;
+	char *individual_query;
 
 	/* Get fixed-size state. */
 	fpes = shm_toc_lookup(toc, PARALLEL_KEY_EXECUTOR_FIXED, false);
@@ -1403,9 +1421,13 @@ ParallelQueryMain(dsm_segment *seg, shm_toc *toc)
 
 	/* Setting debug_query_string for individual workers */
 	debug_query_string = queryDesc->sourceText;
+	individual_query = shm_toc_lookup(toc, PARALLEL_KEY_INDIVIDUAL_QUERY_TEXT, false);
 
 	/* Report workers' query for monitoring purposes */
-	pgstat_report_activity(STATE_RUNNING, debug_query_string);
+	if (strlen(debug_query_string) != strlen(individual_query))
+		pgstat_report_activity(STATE_RUNNING, debug_query_string, individual_query);
+	else
+		pgstat_report_activity(STATE_RUNNING, debug_query_string, NULL);
 
 	/* Attach to the dynamic shared memory area. */
 	area_space = shm_toc_lookup(toc, PARALLEL_KEY_DSA, false);
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 9c7d4b0c60..340ab8a76e 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -3176,7 +3176,7 @@ autovac_report_activity(autovac_table *tab)
 	/* Set statement_timestamp() to current time for pg_stat_activity */
 	SetCurrentStatementStartTimestamp();
 
-	pgstat_report_activity(STATE_RUNNING, activity);
+	pgstat_report_activity(STATE_RUNNING, activity, NULL);
 }
 
 /*
@@ -3215,7 +3215,7 @@ autovac_report_workitem(AutoVacuumWorkItem *workitem,
 	/* Set statement_timestamp() to current time for pg_stat_activity */
 	SetCurrentStatementStartTimestamp();
 
-	pgstat_report_activity(STATE_RUNNING, activity);
+	pgstat_report_activity(STATE_RUNNING, activity, NULL);
 }
 
 /*
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 15f92b66c6..44fb7e77eb 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -2702,7 +2702,9 @@ static PgBackendStatus *MyBEEntry = NULL;
 static char *BackendAppnameBuffer = NULL;
 static char *BackendClientHostnameBuffer = NULL;
 static char *BackendActivityBuffer = NULL;
+static char *BackendIndividualActivityBuffer = NULL;
 static Size BackendActivityBufferSize = 0;
+static Size BackendIndividualActivityBufferSize = 0;
 #ifdef USE_SSL
 static PgBackendSSLStatus *BackendSslStatusBuffer = NULL;
 #endif
@@ -2730,6 +2732,9 @@ BackendStatusShmemSize(void)
 	/* BackendActivityBuffer: */
 	size = add_size(size,
 					mul_size(pgstat_track_activity_query_size, NumBackendStatSlots));
+	/* BackendIndividualActivityBuffer: */
+	size = add_size(size,
+					mul_size(pgstat_track_activity_query_size, NumBackendStatSlots));
 #ifdef USE_SSL
 	/* BackendSslStatusBuffer: */
 	size = add_size(size,
@@ -2820,6 +2825,27 @@ CreateSharedBackendStatus(void)
 		}
 	}
 
+	/* Create or attach to the indiviudal shared activity buffer */
+	BackendIndividualActivityBufferSize = mul_size(pgstat_track_activity_query_size,
+										 NumBackendStatSlots);
+	BackendIndividualActivityBuffer = (char *)
+		ShmemInitStruct("Backend Individual Activity Buffer",
+						BackendIndividualActivityBufferSize,
+						&found);
+
+	if (!found)
+	{
+		MemSet(BackendIndividualActivityBuffer, 0, BackendIndividualActivityBufferSize);
+
+		/* Initialize st_individual_activity pointers. */
+		buffer = BackendIndividualActivityBuffer;
+		for (i = 0; i < NumBackendStatSlots; i++)
+		{
+			BackendStatusArray[i].st_individual_activity_raw = buffer;
+			buffer += pgstat_track_activity_query_size;
+		}
+	}
+
 #ifdef USE_SSL
 	/* Create or attach to the shared SSL status buffer */
 	size = mul_size(sizeof(PgBackendSSLStatus), NumBackendStatSlots);
@@ -3062,10 +3088,12 @@ pgstat_bestart(void)
 	else
 		lbeentry.st_clienthostname[0] = '\0';
 	lbeentry.st_activity_raw[0] = '\0';
+	lbeentry.st_individual_activity_raw[0] = '\0';
 	/* Also make sure the last byte in each string area is always 0 */
 	lbeentry.st_appname[NAMEDATALEN - 1] = '\0';
 	lbeentry.st_clienthostname[NAMEDATALEN - 1] = '\0';
 	lbeentry.st_activity_raw[pgstat_track_activity_query_size - 1] = '\0';
+	lbeentry.st_individual_activity_raw[pgstat_track_activity_query_size - 1] = '\0';
 
 #ifdef USE_SSL
 	memcpy(lbeentry.st_sslstatus, &lsslstatus, sizeof(PgBackendSSLStatus));
@@ -3129,12 +3157,13 @@ pgstat_beshutdown_hook(int code, Datum arg)
  * ----------
  */
 void
-pgstat_report_activity(BackendState state, const char *cmd_str)
+pgstat_report_activity(BackendState state, const char *cmd_str, const char *individual_cmd_str)
 {
 	volatile PgBackendStatus *beentry = MyBEEntry;
 	TimestampTz start_timestamp;
 	TimestampTz current_timestamp;
 	int			len = 0;
+	int			individual_len = 0;
 
 	TRACE_POSTGRESQL_STATEMENT_STATUS(cmd_str);
 
@@ -3156,6 +3185,7 @@ pgstat_report_activity(BackendState state, const char *cmd_str)
 			beentry->st_state = STATE_DISABLED;
 			beentry->st_state_start_timestamp = 0;
 			beentry->st_activity_raw[0] = '\0';
+			beentry->st_individual_activity_raw[0] = '\0';
 			beentry->st_activity_start_timestamp = 0;
 			/* st_xact_start_timestamp and wait_event_info are also disabled */
 			beentry->st_xact_start_timestamp = 0;
@@ -3179,6 +3209,16 @@ pgstat_report_activity(BackendState state, const char *cmd_str)
 		 */
 		len = Min(strlen(cmd_str), pgstat_track_activity_query_size - 1);
 	}
+
+	if (individual_cmd_str != NULL)
+	{
+		/*
+		 * Compute length of to-be-stored string unaware of multi-byte
+		 * characters. For speed reasons that'll get corrected on read, rather
+		 * than computed every write.
+		 */
+		individual_len = Min(strlen(individual_cmd_str), pgstat_track_activity_query_size - 1);
+	}
 	current_timestamp = GetCurrentTimestamp();
 
 	/*
@@ -3196,6 +3236,12 @@ pgstat_report_activity(BackendState state, const char *cmd_str)
 		beentry->st_activity_start_timestamp = start_timestamp;
 	}
 
+	if (individual_cmd_str != NULL)
+	{
+		memcpy((char *) beentry->st_individual_activity_raw, individual_cmd_str, individual_len);
+		beentry->st_individual_activity_raw[len] = '\0';
+	}
+
 	PGSTAT_END_WRITE_ACTIVITY(beentry);
 }
 
@@ -3365,7 +3411,8 @@ pgstat_read_current_status(void)
 	LocalPgBackendStatus *localentry;
 	char	   *localappname,
 			   *localclienthostname,
-			   *localactivity;
+			   *localactivity,
+			   *local_individual_activity;
 #ifdef USE_SSL
 	PgBackendSSLStatus *localsslstatus;
 #endif
@@ -3400,6 +3447,9 @@ pgstat_read_current_status(void)
 	localactivity = (char *)
 		MemoryContextAllocHuge(pgStatLocalContext,
 							   pgstat_track_activity_query_size * NumBackendStatSlots);
+	local_individual_activity = (char *)
+		MemoryContextAllocHuge(pgStatLocalContext,
+							   pgstat_track_activity_query_size * NumBackendStatSlots);
 #ifdef USE_SSL
 	localsslstatus = (PgBackendSSLStatus *)
 		MemoryContextAlloc(pgStatLocalContext,
@@ -3451,6 +3501,8 @@ pgstat_read_current_status(void)
 				localentry->backendStatus.st_clienthostname = localclienthostname;
 				strcpy(localactivity, (char *) beentry->st_activity_raw);
 				localentry->backendStatus.st_activity_raw = localactivity;
+				strcpy(local_individual_activity, (char *) beentry->st_individual_activity_raw);
+				localentry->backendStatus.st_individual_activity_raw = local_individual_activity;
 #ifdef USE_SSL
 				if (beentry->st_ssl)
 				{
@@ -3489,6 +3541,7 @@ pgstat_read_current_status(void)
 			localappname += NAMEDATALEN;
 			localclienthostname += NAMEDATALEN;
 			localactivity += pgstat_track_activity_query_size;
+			local_individual_activity += pgstat_track_activity_query_size;
 #ifdef USE_SSL
 			localsslstatus++;
 #endif
diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c
index 2fcf2e61bc..de7f0ce0af 100644
--- a/src/backend/replication/logical/worker.c
+++ b/src/backend/replication/logical/worker.c
@@ -556,7 +556,7 @@ apply_handle_begin(StringInfo s)
 
 	in_remote_transaction = true;
 
-	pgstat_report_activity(STATE_RUNNING, NULL);
+	pgstat_report_activity(STATE_RUNNING, NULL, NULL);
 }
 
 /*
@@ -600,7 +600,7 @@ apply_handle_commit(StringInfo s)
 	/* Process any tables that are being synchronized in parallel. */
 	process_syncing_tables(commit_data.end_lsn);
 
-	pgstat_report_activity(STATE_IDLE, NULL);
+	pgstat_report_activity(STATE_IDLE, NULL, NULL);
 }
 
 /*
@@ -1571,7 +1571,7 @@ LogicalRepApplyLoop(XLogRecPtr last_received)
 												ALLOCSET_DEFAULT_SIZES);
 
 	/* mark as idle, before starting to loop */
-	pgstat_report_activity(STATE_IDLE, NULL);
+	pgstat_report_activity(STATE_IDLE, NULL, NULL);
 
 	for (;;)
 	{
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index 5e2210dd7b..fd9225a987 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -1601,7 +1601,7 @@ exec_replication_command(const char *cmd_string)
 	initStringInfo(&tmpbuf);
 
 	/* Report to pgstat that this process is running */
-	pgstat_report_activity(STATE_RUNNING, NULL);
+	pgstat_report_activity(STATE_RUNNING, NULL, NULL);
 
 	switch (cmd_node->type)
 	{
@@ -1660,7 +1660,7 @@ exec_replication_command(const char *cmd_string)
 						(errmsg("cannot execute SQL commands in WAL sender for physical replication")));
 
 			/* Report to pgstat that this process is now idle */
-			pgstat_report_activity(STATE_IDLE, NULL);
+			pgstat_report_activity(STATE_IDLE, NULL, NULL);
 
 			/* Tell the caller that this wasn't a WalSender command. */
 			return false;
@@ -1679,7 +1679,7 @@ exec_replication_command(const char *cmd_string)
 	EndCommand(&qc, DestRemote, true);
 
 	/* Report to pgstat that this process is now idle */
-	pgstat_report_activity(STATE_IDLE, NULL);
+	pgstat_report_activity(STATE_IDLE, NULL, NULL);
 
 	return true;
 }
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index c9424f167c..98544439b0 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -167,6 +167,9 @@ static ProcSignalReason RecoveryConflictReason;
 static MemoryContext row_description_context = NULL;
 static StringInfoData row_description_buf;
 
+/* reused buffer to pass the individual queries */
+static StringInfoData individual_query_buf;
+
 /* ----------------------------------------------------------------
  *		decls for routines only used in this file
  * ----------------------------------------------------------------
@@ -991,14 +994,6 @@ exec_simple_query(const char *query_string)
 	bool		use_implicit_block;
 	char		msec_str[32];
 
-	/*
-	 * Report query to various monitoring facilities.
-	 */
-	debug_query_string = query_string;
-
-	pgstat_report_activity(STATE_RUNNING, query_string);
-
-	TRACE_POSTGRESQL_QUERY_START(query_string);
 
 	/*
 	 * We use save_log_statement_stats so ShowUsage doesn't report incorrect
@@ -1075,7 +1070,38 @@ exec_simple_query(const char *query_string)
 		Portal		portal;
 		DestReceiver *receiver;
 		int16		format;
+		char		*individual_query;
+		int 		individual_query_length;
+
+		/* if statement does not end with ;
+		 * then parsetree->stmt_len == 0
+		 */
+		if (parsetree->stmt_len ==  0)
+			individual_query_length = strlen(query_string) - parsetree->stmt_location;
+		else
+			individual_query_length = parsetree->stmt_len + 1;
 
+		/* extract the query text */
+		individual_query = palloc(individual_query_length + 1);
+		strncpy(individual_query, query_string + parsetree->stmt_location, individual_query_length);
+		individual_query[individual_query_length] = '\0';
+		/*
+		 * Report query to various monitoring facilities.
+		 */
+
+		resetStringInfo(&individual_query_buf);
+		appendStringInfoString(&individual_query_buf, individual_query);
+
+		debug_query_string = query_string;
+
+		if (strlen(debug_query_string) != individual_query_buf.len)
+			pgstat_report_activity(STATE_RUNNING, query_string, individual_query_buf.data);
+		else
+			pgstat_report_activity(STATE_RUNNING, query_string, NULL);
+
+		TRACE_POSTGRESQL_QUERY_START(query_string);
+
+		pfree(individual_query);
 		/*
 		 * Get the command name for use in status display (it also becomes the
 		 * default completion tag, down inside PortalRun).  Set ps_status and
@@ -1366,7 +1392,7 @@ exec_parse_message(const char *query_string,	/* string to execute */
 	 */
 	debug_query_string = query_string;
 
-	pgstat_report_activity(STATE_RUNNING, query_string);
+	pgstat_report_activity(STATE_RUNNING, query_string, NULL);
 
 	set_ps_display("PARSE");
 
@@ -1657,7 +1683,7 @@ exec_bind_message(StringInfo input_message)
 	 */
 	debug_query_string = psrc->query_string;
 
-	pgstat_report_activity(STATE_RUNNING, psrc->query_string);
+	pgstat_report_activity(STATE_RUNNING, psrc->query_string, NULL);
 
 	set_ps_display("BIND");
 
@@ -2115,7 +2141,7 @@ exec_execute_message(const char *portal_name, long max_rows)
 	 */
 	debug_query_string = sourceText;
 
-	pgstat_report_activity(STATE_RUNNING, sourceText);
+	pgstat_report_activity(STATE_RUNNING, sourceText, NULL);
 
 	set_ps_display(GetCommandTagName(portal->commandTag));
 
@@ -4011,6 +4037,8 @@ PostgresMain(int argc, char *argv[],
 	initStringInfo(&row_description_buf);
 	MemoryContextSwitchTo(TopMemoryContext);
 
+	initStringInfo(&individual_query_buf);
+
 	/*
 	 * Remember stand-alone backend startup time
 	 */
@@ -4196,7 +4224,7 @@ PostgresMain(int argc, char *argv[],
 			if (IsAbortedTransactionBlockState())
 			{
 				set_ps_display("idle in transaction (aborted)");
-				pgstat_report_activity(STATE_IDLEINTRANSACTION_ABORTED, NULL);
+				pgstat_report_activity(STATE_IDLEINTRANSACTION_ABORTED, NULL, NULL);
 
 				/* Start the idle-in-transaction timer */
 				if (IdleInTransactionSessionTimeout > 0)
@@ -4209,7 +4237,7 @@ PostgresMain(int argc, char *argv[],
 			else if (IsTransactionOrTransactionBlock())
 			{
 				set_ps_display("idle in transaction");
-				pgstat_report_activity(STATE_IDLEINTRANSACTION, NULL);
+				pgstat_report_activity(STATE_IDLEINTRANSACTION, NULL, NULL);
 
 				/* Start the idle-in-transaction timer */
 				if (IdleInTransactionSessionTimeout > 0)
@@ -4236,7 +4264,7 @@ PostgresMain(int argc, char *argv[],
 				pgstat_report_stat(false);
 
 				set_ps_display("idle");
-				pgstat_report_activity(STATE_IDLE, NULL);
+				pgstat_report_activity(STATE_IDLE, NULL, NULL);
 			}
 
 			ReadyForQuery(whereToSendOutput);
@@ -4384,7 +4412,7 @@ PostgresMain(int argc, char *argv[],
 				SetCurrentStatementStartTimestamp();
 
 				/* Report query to various monitoring facilities. */
-				pgstat_report_activity(STATE_FASTPATH, NULL);
+				pgstat_report_activity(STATE_FASTPATH, NULL, NULL);
 				set_ps_display("");
 
 				/* start an xact for this function invocation */
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 95738a4e34..976dfc7583 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -567,7 +567,7 @@ pg_stat_get_progress_info(PG_FUNCTION_ARGS)
 Datum
 pg_stat_get_activity(PG_FUNCTION_ARGS)
 {
-#define PG_STAT_GET_ACTIVITY_COLS	30
+#define PG_STAT_GET_ACTIVITY_COLS	31
 	int			num_backends = pgstat_fetch_stat_numbackends();
 	int			curr_backend;
 	int			pid = PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0);
@@ -631,6 +631,8 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 
 			nulls[5] = false;
 			values[5] = CStringGetTextDatum("");
+			nulls[6] = false;
+			values[6] = CStringGetTextDatum("");
 
 			tuplestore_putvalues(tupstore, tupdesc, values, nulls);
 			continue;
@@ -661,20 +663,21 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 			nulls[3] = true;
 
 		if (TransactionIdIsValid(local_beentry->backend_xid))
-			values[15] = TransactionIdGetDatum(local_beentry->backend_xid);
+			values[16] = TransactionIdGetDatum(local_beentry->backend_xid);
 		else
-			nulls[15] = true;
+			nulls[16] = true;
 
 		if (TransactionIdIsValid(local_beentry->backend_xmin))
-			values[16] = TransactionIdGetDatum(local_beentry->backend_xmin);
+			values[17] = TransactionIdGetDatum(local_beentry->backend_xmin);
 		else
-			nulls[16] = true;
+			nulls[17] = true;
 
 		/* Values only available to role member or pg_read_all_stats */
 		if (HAS_PGSTAT_PERMISSIONS(beentry->st_userid))
 		{
 			SockAddr	zero_clientaddr;
 			char	   *clipped_activity;
+			char	   *clipped_individual_activity;
 
 			switch (beentry->st_state)
 			{
@@ -705,8 +708,12 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 			values[5] = CStringGetTextDatum(clipped_activity);
 			pfree(clipped_activity);
 
+			clipped_individual_activity = pgstat_clip_activity(beentry->st_individual_activity_raw);
+			values[6] = CStringGetTextDatum(clipped_individual_activity);
+			pfree(clipped_individual_activity);
+
 			/* leader_pid */
-			nulls[29] = true;
+			nulls[30] = true;
 
 			proc = BackendPidGetProc(beentry->st_procpid);
 
@@ -743,20 +750,20 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 				 */
 				if (leader && leader->pid != beentry->st_procpid)
 				{
-					values[29] = Int32GetDatum(leader->pid);
-					nulls[29] = false;
+					values[30] = Int32GetDatum(leader->pid);
+					nulls[30] = false;
 				}
 			}
 
 			if (wait_event_type)
-				values[6] = CStringGetTextDatum(wait_event_type);
+				values[7] = CStringGetTextDatum(wait_event_type);
 			else
-				nulls[6] = true;
+				nulls[7] = true;
 
 			if (wait_event)
-				values[7] = CStringGetTextDatum(wait_event);
+				values[8] = CStringGetTextDatum(wait_event);
 			else
-				nulls[7] = true;
+				nulls[8] = true;
 
 			/*
 			 * Don't expose transaction time for walsenders; it confuses
@@ -765,33 +772,33 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 			 */
 			if (beentry->st_xact_start_timestamp != 0 &&
 				beentry->st_backendType != B_WAL_SENDER)
-				values[8] = TimestampTzGetDatum(beentry->st_xact_start_timestamp);
+				values[9] = TimestampTzGetDatum(beentry->st_xact_start_timestamp);
 			else
-				nulls[8] = true;
+				nulls[9] = true;
 
 			if (beentry->st_activity_start_timestamp != 0)
-				values[9] = TimestampTzGetDatum(beentry->st_activity_start_timestamp);
+				values[10] = TimestampTzGetDatum(beentry->st_activity_start_timestamp);
 			else
-				nulls[9] = true;
+				nulls[10] = true;
 
 			if (beentry->st_proc_start_timestamp != 0)
-				values[10] = TimestampTzGetDatum(beentry->st_proc_start_timestamp);
+				values[11] = TimestampTzGetDatum(beentry->st_proc_start_timestamp);
 			else
-				nulls[10] = true;
+				nulls[11] = true;
 
 			if (beentry->st_state_start_timestamp != 0)
-				values[11] = TimestampTzGetDatum(beentry->st_state_start_timestamp);
+				values[12] = TimestampTzGetDatum(beentry->st_state_start_timestamp);
 			else
-				nulls[11] = true;
+				nulls[12] = true;
 
 			/* A zeroed client addr means we don't know */
 			memset(&zero_clientaddr, 0, sizeof(zero_clientaddr));
 			if (memcmp(&(beentry->st_clientaddr), &zero_clientaddr,
 					   sizeof(zero_clientaddr)) == 0)
 			{
-				nulls[12] = true;
 				nulls[13] = true;
 				nulls[14] = true;
+				nulls[15] = true;
 			}
 			else
 			{
@@ -815,20 +822,20 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 					if (ret == 0)
 					{
 						clean_ipv6_addr(beentry->st_clientaddr.addr.ss_family, remote_host);
-						values[12] = DirectFunctionCall1(inet_in,
+						values[13] = DirectFunctionCall1(inet_in,
 														 CStringGetDatum(remote_host));
 						if (beentry->st_clienthostname &&
 							beentry->st_clienthostname[0])
-							values[13] = CStringGetTextDatum(beentry->st_clienthostname);
+							values[14] = CStringGetTextDatum(beentry->st_clienthostname);
 						else
-							nulls[13] = true;
-						values[14] = Int32GetDatum(atoi(remote_port));
+							nulls[14] = true;
+						values[15] = Int32GetDatum(atoi(remote_port));
 					}
 					else
 					{
-						nulls[12] = true;
 						nulls[13] = true;
 						nulls[14] = true;
+						nulls[15] = true;
 					}
 				}
 				else if (beentry->st_clientaddr.addr.ss_family == AF_UNIX)
@@ -839,16 +846,16 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 					 * connections we have no permissions to view, or with
 					 * errors.
 					 */
-					nulls[12] = true;
 					nulls[13] = true;
-					values[14] = Int32GetDatum(-1);
+					nulls[14] = true;
+					values[15] = Int32GetDatum(-1);
 				}
 				else
 				{
 					/* Unknown address type, should never happen */
-					nulls[12] = true;
 					nulls[13] = true;
 					nulls[14] = true;
+					nulls[15] = true;
 				}
 			}
 			/* Add backend type */
@@ -858,59 +865,59 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 
 				bgw_type = GetBackgroundWorkerTypeByPid(beentry->st_procpid);
 				if (bgw_type)
-					values[17] = CStringGetTextDatum(bgw_type);
+					values[18] = CStringGetTextDatum(bgw_type);
 				else
-					nulls[17] = true;
+					nulls[18] = true;
 			}
 			else
-				values[17] =
+				values[18] =
 					CStringGetTextDatum(GetBackendTypeDesc(beentry->st_backendType));
 
 			/* SSL information */
 			if (beentry->st_ssl)
 			{
-				values[18] = BoolGetDatum(true);	/* ssl */
-				values[19] = CStringGetTextDatum(beentry->st_sslstatus->ssl_version);
-				values[20] = CStringGetTextDatum(beentry->st_sslstatus->ssl_cipher);
-				values[21] = Int32GetDatum(beentry->st_sslstatus->ssl_bits);
-				values[22] = BoolGetDatum(beentry->st_sslstatus->ssl_compression);
+				values[19] = BoolGetDatum(true);	/* ssl */
+				values[20] = CStringGetTextDatum(beentry->st_sslstatus->ssl_version);
+				values[21] = CStringGetTextDatum(beentry->st_sslstatus->ssl_cipher);
+				values[22] = Int32GetDatum(beentry->st_sslstatus->ssl_bits);
+				values[23] = BoolGetDatum(beentry->st_sslstatus->ssl_compression);
 
 				if (beentry->st_sslstatus->ssl_client_dn[0])
-					values[23] = CStringGetTextDatum(beentry->st_sslstatus->ssl_client_dn);
+					values[24] = CStringGetTextDatum(beentry->st_sslstatus->ssl_client_dn);
 				else
-					nulls[23] = true;
+					nulls[24] = true;
 
 				if (beentry->st_sslstatus->ssl_client_serial[0])
-					values[24] = DirectFunctionCall3(numeric_in,
+					values[25] = DirectFunctionCall3(numeric_in,
 													 CStringGetDatum(beentry->st_sslstatus->ssl_client_serial),
 													 ObjectIdGetDatum(InvalidOid),
 													 Int32GetDatum(-1));
 				else
-					nulls[24] = true;
+					nulls[25] = true;
 
 				if (beentry->st_sslstatus->ssl_issuer_dn[0])
-					values[25] = CStringGetTextDatum(beentry->st_sslstatus->ssl_issuer_dn);
+					values[26] = CStringGetTextDatum(beentry->st_sslstatus->ssl_issuer_dn);
 				else
-					nulls[25] = true;
+					nulls[26] = true;
 			}
 			else
 			{
-				values[18] = BoolGetDatum(false);	/* ssl */
-				nulls[19] = nulls[20] = nulls[21] = nulls[22] = nulls[23] = nulls[24] = nulls[25] = true;
+				values[19] = BoolGetDatum(false);	/* ssl */
+				nulls[20] = nulls[21] = nulls[22] = nulls[23] = nulls[24] = nulls[25] = nulls[26] = true;
 			}
 
 			/* GSSAPI information */
 			if (beentry->st_gss)
 			{
-				values[26] = BoolGetDatum(beentry->st_gssstatus->gss_auth); /* gss_auth */
-				values[27] = CStringGetTextDatum(beentry->st_gssstatus->gss_princ);
-				values[28] = BoolGetDatum(beentry->st_gssstatus->gss_enc);	/* GSS Encryption in use */
+				values[27] = BoolGetDatum(beentry->st_gssstatus->gss_auth); /* gss_auth */
+				values[28] = CStringGetTextDatum(beentry->st_gssstatus->gss_princ);
+				values[29] = BoolGetDatum(beentry->st_gssstatus->gss_enc);	/* GSS Encryption in use */
 			}
 			else
 			{
-				values[26] = BoolGetDatum(false);	/* gss_auth */
-				nulls[27] = true;	/* No GSS principal */
-				values[28] = BoolGetDatum(false);	/* GSS Encryption not in
+				values[27] = BoolGetDatum(false);	/* gss_auth */
+				nulls[28] = true;	/* No GSS principal */
+				values[29] = BoolGetDatum(false);	/* GSS Encryption not in
 													 * use */
 			}
 		}
@@ -918,8 +925,8 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 		{
 			/* No permissions to view data about this session */
 			values[5] = CStringGetTextDatum("");
+			values[6] = CStringGetTextDatum("");
 			nulls[4] = true;
-			nulls[6] = true;
 			nulls[7] = true;
 			nulls[8] = true;
 			nulls[9] = true;
@@ -928,7 +935,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 			nulls[12] = true;
 			nulls[13] = true;
 			nulls[14] = true;
-			nulls[17] = true;
+			nulls[15] = true;
 			nulls[18] = true;
 			nulls[19] = true;
 			nulls[20] = true;
@@ -941,6 +948,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 			nulls[27] = true;
 			nulls[28] = true;
 			nulls[29] = true;
+			nulls[30] = true;
 		}
 
 		tuplestore_putvalues(tupstore, tupdesc, values, nulls);
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 082a11f270..a4451c1ef6 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5222,9 +5222,9 @@
   proname => 'pg_stat_get_activity', prorows => '100', proisstrict => 'f',
   proretset => 't', provolatile => 's', proparallel => 'r',
   prorettype => 'record', proargtypes => 'int4',
-  proallargtypes => '{int4,oid,int4,oid,text,text,text,text,text,timestamptz,timestamptz,timestamptz,timestamptz,inet,text,int4,xid,xid,text,bool,text,text,int4,bool,text,numeric,text,bool,text,bool,int4}',
-  proargmodes => '{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}',
-  proargnames => '{pid,datid,pid,usesysid,application_name,state,query,wait_event_type,wait_event,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port,backend_xid,backend_xmin,backend_type,ssl,sslversion,sslcipher,sslbits,sslcompression,ssl_client_dn,ssl_client_serial,ssl_issuer_dn,gss_auth,gss_princ,gss_enc,leader_pid}',
+  proallargtypes => '{int4,oid,int4,oid,text,text,text,text,text,text,timestamptz,timestamptz,timestamptz,timestamptz,inet,text,int4,xid,xid,text,bool,text,text,int4,bool,text,numeric,text,bool,text,bool,int4}',
+  proargmodes => '{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}',
+  proargnames => '{pid,datid,pid,usesysid,application_name,state,query,individual_query,wait_event_type,wait_event,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port,backend_xid,backend_xmin,backend_type,ssl,sslversion,sslcipher,sslbits,sslcompression,ssl_client_dn,ssl_client_serial,ssl_issuer_dn,gss_auth,gss_princ,gss_enc,leader_pid}',
   prosrc => 'pg_stat_get_activity' },
 { oid => '3318',
   descr => 'statistics: information about progress of backends running maintenance command',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 1387201382..f29dfcbf13 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -1123,6 +1123,15 @@ typedef struct PgBackendStatus
 	 */
 	char	   *st_activity_raw;
 
+	/*
+	 * Current individual command string; MUST be null-terminated. Note that this string
+	 * possibly is truncated in the middle of a multi-byte character. As
+	 * activity strings are stored more frequently than read, that allows to
+	 * move the cost of correct truncation to the display side. Use
+	 * pgstat_clip_activity() to truncate correctly.
+	 */
+	char	   *st_individual_activity_raw;
+
 	/*
 	 * Command progress reporting.  Any command which wishes can advertise
 	 * that it is running by setting st_progress_command,
@@ -1314,7 +1323,7 @@ extern void pgstat_report_checksum_failure(void);
 extern void pgstat_initialize(void);
 extern void pgstat_bestart(void);
 
-extern void pgstat_report_activity(BackendState state, const char *cmd_str);
+extern void pgstat_report_activity(BackendState state, const char *cmd_str, const char *individual_cmd_str);
 extern void pgstat_report_tempfile(size_t filesize);
 extern void pgstat_report_appname(const char *appname);
 extern void pgstat_report_xact_timestamp(TimestampTz tstamp);
diff --git a/src/test/modules/worker_spi/worker_spi.c b/src/test/modules/worker_spi/worker_spi.c
index 1c7b17c56f..bc7b4a4a54 100644
--- a/src/test/modules/worker_spi/worker_spi.c
+++ b/src/test/modules/worker_spi/worker_spi.c
@@ -112,7 +112,7 @@ initialize_worker_spi(worktable *table)
 	StartTransactionCommand();
 	SPI_connect();
 	PushActiveSnapshot(GetTransactionSnapshot());
-	pgstat_report_activity(STATE_RUNNING, "initializing worker_spi schema");
+	pgstat_report_activity(STATE_RUNNING, "initializing worker_spi schema", NULL);
 
 	/* XXX could we use CREATE SCHEMA IF NOT EXISTS? */
 	initStringInfo(&buf);
@@ -156,7 +156,7 @@ initialize_worker_spi(worktable *table)
 	SPI_finish();
 	PopActiveSnapshot();
 	CommitTransactionCommand();
-	pgstat_report_activity(STATE_IDLE, NULL);
+	pgstat_report_activity(STATE_IDLE, NULL, NULL);
 }
 
 void
@@ -262,7 +262,7 @@ worker_spi_main(Datum main_arg)
 		StartTransactionCommand();
 		SPI_connect();
 		PushActiveSnapshot(GetTransactionSnapshot());
-		pgstat_report_activity(STATE_RUNNING, buf.data);
+		pgstat_report_activity(STATE_RUNNING, buf.data, NULL);
 
 		/* We can now execute queries via SPI */
 		ret = SPI_execute(buf.data, false, 0);
@@ -292,7 +292,7 @@ worker_spi_main(Datum main_arg)
 		PopActiveSnapshot();
 		CommitTransactionCommand();
 		pgstat_report_stat(false);
-		pgstat_report_activity(STATE_IDLE, NULL);
+		pgstat_report_activity(STATE_IDLE, NULL, NULL);
 	}
 
 	proc_exit(1);
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 601734a6f1..5635e80811 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1751,8 +1751,9 @@ pg_stat_activity| SELECT s.datid,
     s.backend_xid,
     s.backend_xmin,
     s.query,
+    s.individual_query,
     s.backend_type
-   FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, sslcompression, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid)
+   FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, individual_query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, sslcompression, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid)
      LEFT JOIN pg_database d ON ((s.datid = d.oid)))
      LEFT JOIN pg_authid u ON ((s.usesysid = u.oid)));
 pg_stat_all_indexes| SELECT c.oid AS relid,
@@ -1857,7 +1858,7 @@ pg_stat_gssapi| SELECT s.pid,
     s.gss_auth AS gss_authenticated,
     s.gss_princ AS principal,
     s.gss_enc AS encrypted
-   FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, sslcompression, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid)
+   FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, individual_query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, sslcompression, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid)
   WHERE (s.client_port IS NOT NULL);
 pg_stat_progress_analyze| SELECT s.pid,
     s.datid,
@@ -2005,7 +2006,7 @@ pg_stat_replication| SELECT s.pid,
     w.sync_priority,
     w.sync_state,
     w.reply_time
-   FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, sslcompression, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid)
+   FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, individual_query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, sslcompression, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid)
      JOIN pg_stat_get_wal_senders() w(pid, state, sent_lsn, write_lsn, flush_lsn, replay_lsn, write_lag, flush_lag, replay_lag, sync_priority, sync_state, reply_time) ON ((s.pid = w.pid)))
      LEFT JOIN pg_authid u ON ((s.usesysid = u.oid)));
 pg_stat_slru| SELECT s.name,
@@ -2027,7 +2028,7 @@ pg_stat_ssl| SELECT s.pid,
     s.ssl_client_dn AS client_dn,
     s.ssl_client_serial AS client_serial,
     s.ssl_issuer_dn AS issuer_dn
-   FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, sslcompression, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid)
+   FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, individual_query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, sslcompression, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid)
   WHERE (s.client_port IS NOT NULL);
 pg_stat_subscription| SELECT su.oid AS subid,
     su.subname,