From dde9cb56e4ca0023c92c1def3340d98500f61848 Mon Sep 17 00:00:00 2001
From: Shinya Kato <shinya11.kato@gmail.com>
Date: Thu, 20 Nov 2025 14:56:05 +0900
Subject: [PATCH v7 2/2] Add triggered_by column to pg_stat_progress_analyze.

Exposes the reason why the current ANALYZE operation was initiated
(manual or autovacuum). This allows users and monitoring tools to better
understand ANALYZE behavior.

Bump catalog version.

Author: Shinya Kato <shinya11.kato@gmail.com>
Discussion: https://postgr.es/m/CAA5RZ0suoicwxFeK_eDkUrzF7s0BVTaE7M%2BehCpYcCk5wiECpw%40mail.gmail.com
---
 doc/src/sgml/monitoring.sgml         | 25 +++++++++++++++++++++++++
 src/backend/catalog/system_views.sql |  5 ++++-
 src/backend/commands/analyze.c       |  6 ++++++
 src/include/commands/progress.h      |  5 +++++
 src/test/regress/expected/rules.out  |  7 ++++++-
 5 files changed, 46 insertions(+), 2 deletions(-)

diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index ab4443f420e..4fc066ac1dc 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -5748,6 +5748,31 @@ FROM pg_stat_get_backend_idset() AS backendid;
        zero).
       </para></entry>
      </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>triggered_by</structfield> <type>text</type>
+      </para>
+      <para>
+       Shows how the current <command>ANALYZE</command> operation was
+       initiated.  Possible values are:
+       <itemizedlist>
+        <listitem>
+         <para>
+          <literal>manual</literal>: The analyze was triggered by an explicit
+          <command>ANALYZE</command> or <command>VACUUM (ANALYZE)</command>
+          command.
+         </para>
+        </listitem>
+        <listitem>
+         <para>
+          <literal>autovacuum</literal>: The analyze was triggered by an
+          autovacuum worker.
+         </para>
+        </listitem>
+       </itemizedlist>
+      </para></entry>
+     </row>
     </tbody>
    </tgroup>
   </table>
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index d4b0f06e422..8648e7dbe5e 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -1244,7 +1244,10 @@ CREATE VIEW pg_stat_progress_analyze AS
         S.param6 AS child_tables_total,
         S.param7 AS child_tables_done,
         CAST(S.param8 AS oid) AS current_child_table_relid,
-        S.param9 / 1000000::double precision AS delay_time
+        S.param9 / 1000000::double precision AS delay_time,
+        CASE S.param10 WHEN 1 THEN 'manual'
+                       WHEN 2 THEN 'autovacuum'
+                       ELSE NULL END AS triggered_by
     FROM pg_stat_get_progress_info('ANALYZE') AS S
         LEFT JOIN pg_database D ON S.datid = D.oid;
 
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 25089fae3e0..80798df6e22 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -239,6 +239,12 @@ analyze_rel(Oid relid, RangeVar *relation,
 	 */
 	pgstat_progress_start_command(PROGRESS_COMMAND_ANALYZE,
 								  RelationGetRelid(onerel));
+	if (AmAutoVacuumWorkerProcess())
+		pgstat_progress_update_param(PROGRESS_ANALYZE_TRIGGERED_BY,
+									 PROGRESS_ANALYZE_TRIGGERED_BY_AUTOVACUUM);
+	else
+		pgstat_progress_update_param(PROGRESS_ANALYZE_TRIGGERED_BY,
+									 PROGRESS_ANALYZE_TRIGGERED_BY_MANUAL);
 
 	/*
 	 * Do the normal non-recursive ANALYZE.  We can skip this for partitioned
diff --git a/src/include/commands/progress.h b/src/include/commands/progress.h
index 5fc5d3fcbee..1b46bd337fd 100644
--- a/src/include/commands/progress.h
+++ b/src/include/commands/progress.h
@@ -60,6 +60,7 @@
 #define PROGRESS_ANALYZE_CHILD_TABLES_DONE			6
 #define PROGRESS_ANALYZE_CURRENT_CHILD_TABLE_RELID	7
 #define PROGRESS_ANALYZE_DELAY_TIME					8
+#define PROGRESS_ANALYZE_TRIGGERED_BY			    9
 
 /* Phases of analyze (as advertised via PROGRESS_ANALYZE_PHASE) */
 #define PROGRESS_ANALYZE_PHASE_ACQUIRE_SAMPLE_ROWS		1
@@ -68,6 +69,10 @@
 #define PROGRESS_ANALYZE_PHASE_COMPUTE_EXT_STATS		4
 #define PROGRESS_ANALYZE_PHASE_FINALIZE_ANALYZE			5
 
+/* Reasons for analyze (as advertised via PROGRESS_ANALYZE_TRIGGERED_BY) */
+#define PROGRESS_ANALYZE_TRIGGERED_BY_MANUAL			1
+#define PROGRESS_ANALYZE_TRIGGERED_BY_AUTOVACUUM		2
+
 /* Progress parameters for cluster */
 #define PROGRESS_CLUSTER_COMMAND				0
 #define PROGRESS_CLUSTER_PHASE					1
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 2d75baa21dd..2d151b674c4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1968,7 +1968,12 @@ pg_stat_progress_analyze| SELECT s.pid,
     s.param6 AS child_tables_total,
     s.param7 AS child_tables_done,
     (s.param8)::oid AS current_child_table_relid,
-    ((s.param9)::double precision / (1000000)::double precision) AS delay_time
+    ((s.param9)::double precision / (1000000)::double precision) AS delay_time,
+        CASE s.param10
+            WHEN 1 THEN 'manual'::text
+            WHEN 2 THEN 'autovacuum'::text
+            ELSE NULL::text
+        END AS triggered_by
    FROM (pg_stat_get_progress_info('ANALYZE'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20)
      LEFT JOIN pg_database d ON ((s.datid = d.oid)));
 pg_stat_progress_basebackup| SELECT pid,
-- 
2.47.3

