diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 85459d0..4288992 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -507,6 +507,13 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
       yet included in <structname>pg_stat_user_functions</>).</entry>
      </row>
 
+     <row>
+      <entry><structname>pg_stat_vacuum_progress</><indexterm><primary>pg_stat_vacuum_progress</primary></indexterm></entry>
+      <entry>One row for each backend (including autovacuum worker processes) running
+      <command>VACUUM</>, showing current progress in terms of heap pages it
+      has finished processing. Backends running <command>VACUUM FULL</> are
+      not included.</entry>
+     </row>
     </tbody>
    </tgroup>
   </table>
@@ -1822,6 +1829,88 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
    controls exactly which functions are tracked.
   </para>
 
+  <table id="pg-stat-vacuum-progress" xreflabel="pg_stat_vacuum_progress">
+   <title><structname>pg_stat_vacuum_progress</structname> View</title>
+   <tgroup cols="3">
+    <thead>
+    <row>
+      <entry>Column</entry>
+      <entry>Type</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+   <tbody>
+    <row>
+     <entry><structfield>pid</></entry>
+     <entry><type>integer</></entry>
+     <entry>Process ID of backend</entry>
+    </row>
+    <row>
+     <entry><structfield>relid</></entry>
+     <entry><type>oid</></entry>
+     <entry>OID of a table</entry>
+    </row>
+    <row>
+     <entry><structfield>phase</></entry>
+     <entry><type>integer</></entry>
+     <entry>Current phase of vacuum.
+       Possible values are:
+       <itemizedlist>
+        <listitem>
+         <para>
+          <literal>Scanning Heap</>: Scanning heap blocks.
+         </para>
+        </listitem>
+        <listitem>
+         <para>
+          <literal>Vacuuming Index and Heap</>: Vacuuming index and heap blocks.
+         </para>
+        </listitem>
+       </itemizedlist>
+     </entry>
+    </row>
+    <row>
+     <entry><structfield>total_heap_blks</></entry>
+     <entry><type>integer</></entry>
+     <entry>Total number of heap blocks in this table</entry>
+    </row>
+    <row>
+     <entry><structfield>current_heap_blkno</></entry>
+     <entry><type>integer</></entry>
+     <entry>Current heap block number</entry>
+    </row>
+    <row>
+     <entry><structfield>total_index_pages</></entry>
+     <entry><type>integer</></entry>
+     <entry>Total number of index pages in this table</entry>
+    </row>
+    <row>
+     <entry><structfield>scanned_index_pages</></entry>
+     <entry><type>integer</></entry>
+     <entry>Number of index pages processed</entry>
+    </row>
+    <row>
+     <entry><structfield>index_scan_count</></entry>
+     <entry><type>integer</></entry>
+     <entry>Number of times index scan has been performed so far</entry>
+    </row>
+    <row>
+     <entry><structfield>percent_complete</></entry>
+     <entry><type>double precision</></entry>
+     <entry>Amount of work done in percent</entry>
+    </row>
+   </tbody>
+   </tgroup>
+  </table>
+
+  <para>
+   The <structname>pg_stat_vacuum_progress</structname> view will contain
+   one row for each backend (including autovacuum worker processes), showing
+   progress of <command>VACUUM</> running in it. Note that the backends
+   running <command>VACUUM FULL</> are not shown.
+  </para>
+
  </sect2>
 
  <sect2 id="monitoring-stats-functions">
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index abf9a70..330b638 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -645,6 +645,23 @@ CREATE VIEW pg_stat_activity AS
     WHERE S.datid = D.oid AND
             S.usesysid = U.oid;
 
+CREATE VIEW pg_stat_vacuum_progress AS
+	SELECT
+			S.pid,
+			S.relid,
+			CASE S.phase
+				WHEN 1 THEN 'Scanning Heap'
+				WHEN 2 THEN 'Vacuuming Index and Heap'
+				ELSE 'Unknown phase'
+			END AS phase,
+			S.total_heap_blks,
+			S.current_heap_blkno,
+			S.total_index_pages,
+			S.scanned_index_pages,
+			S.index_scan_count,
+			S.percent_complete
+	FROM pg_stat_get_progress_info(1) AS S;
+
 CREATE VIEW pg_stat_replication AS
     SELECT
             S.pid,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 4cb4acf..4401922 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -287,12 +287,17 @@ vacuum(int options, RangeVar *relation, Oid relid, VacuumParams *params,
 		/*
 		 * Loop to process each selected relation.
 		 */
+		pgstat_reset_local_progress();
+
 		foreach(cur, relations)
 		{
 			Oid			relid = lfirst_oid(cur);
 
 			if (options & VACOPT_VACUUM)
 			{
+				if (!(options & VACOPT_FULL))
+					pgstat_report_progress_set_command(COMMAND_LAZY_VACUUM);
+
 				if (!vacuum_rel(relid, relation, options, params))
 					continue;
 			}
@@ -325,6 +330,7 @@ vacuum(int options, RangeVar *relation, Oid relid, VacuumParams *params,
 	{
 		in_vacuum = false;
 		VacuumCostActive = false;
+		pgstat_reset_local_progress();
 		PG_RE_THROW();
 	}
 	PG_END_TRY();
@@ -355,6 +361,7 @@ vacuum(int options, RangeVar *relation, Oid relid, VacuumParams *params,
 		vac_update_datfrozenxid();
 	}
 
+	pgstat_reset_local_progress();
 	/*
 	 * Clean up working storage --- note we must do this after
 	 * StartTransactionCommand, else we might be trying to delete the active
diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c
index 4f6f6e7..9c8a248 100644
--- a/src/backend/commands/vacuumlazy.c
+++ b/src/backend/commands/vacuumlazy.c
@@ -433,7 +433,10 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
 			   Relation *Irel, int nindexes, bool scan_all)
 {
 	BlockNumber nblocks,
-				blkno;
+				blkno,
+				total_heap_blks,
+				total_index_pages = 0,
+				scanned_index_pages = 0;
 	HeapTupleData tuple;
 	char	   *relname;
 	BlockNumber empty_pages,
@@ -450,14 +453,18 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
 	bool		skipping_all_visible_blocks;
 	xl_heap_freeze_tuple *frozen;
 	StringInfoData buf;
+	Oid	relid;
 
 	pg_rusage_init(&ru0);
 
 	relname = RelationGetRelationName(onerel);
+	relid = RelationGetRelid(onerel);
 	ereport(elevel,
 			(errmsg("vacuuming \"%s.%s\"",
 					get_namespace_name(RelationGetNamespace(onerel)),
 					relname)));
+	/* Report relid of the relation*/
+	pgstat_report_progress_set_command_target(relid);
 
 	empty_pages = vacuumed_pages = 0;
 	num_tuples = tups_vacuumed = nkeep = nunused = 0;
@@ -465,7 +472,11 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
 	indstats = (IndexBulkDeleteResult **)
 		palloc0(nindexes * sizeof(IndexBulkDeleteResult *));
 
-	nblocks = RelationGetNumberOfBlocks(onerel);
+	total_heap_blks = nblocks = RelationGetNumberOfBlocks(onerel);
+
+	for (i = 0; i < nindexes; i++)
+		total_index_pages += RelationGetNumberOfBlocks(Irel[i]);
+
 	vacrelstats->rel_pages = nblocks;
 	vacrelstats->scanned_pages = 0;
 	vacrelstats->nonempty_pages = 0;
@@ -474,6 +485,10 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
 	lazy_space_alloc(vacrelstats, nblocks);
 	frozen = palloc(sizeof(xl_heap_freeze_tuple) * MaxHeapTuplesPerPage);
 
+	/* Report count of total heap blocks and total index pages of a relation*/
+	pgstat_report_progress_update_counter(1, total_heap_blks);
+	pgstat_report_progress_update_counter(3, total_index_pages);
+
 	/*
 	 * We want to skip pages that don't require vacuuming according to the
 	 * visibility map, but only when we can skip at least SKIP_PAGES_THRESHOLD
@@ -527,6 +542,7 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
 	else
 		skipping_all_visible_blocks = false;
 
+	pgstat_report_progress_update_counter(0, VACUUM_PHASE_SCAN_HEAP);
 	for (blkno = 0; blkno < nblocks; blkno++)
 	{
 		Buffer		buf;
@@ -543,6 +559,9 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
 		bool		has_dead_tuples;
 		TransactionId visibility_cutoff_xid = InvalidTransactionId;
 
+		/* Update current block number of the relation */
+		pgstat_report_progress_update_counter(2, blkno + 1);
+
 		/* see note above about forcing scanning of last page */
 #define FORCE_CHECK_PAGE() \
 		(blkno == nblocks - 1 && should_attempt_truncation(vacrelstats))
@@ -603,11 +622,18 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
 			/* Log cleanup info before we touch indexes */
 			vacuum_log_cleanup_info(onerel, vacrelstats);
 
+			pgstat_report_progress_update_counter(0, VACUUM_PHASE_VACUUM_INDEX_HEAP);
 			/* Remove index entries */
 			for (i = 0; i < nindexes; i++)
+			{
 				lazy_vacuum_index(Irel[i],
 								  &indstats[i],
 								  vacrelstats);
+				scanned_index_pages += RelationGetNumberOfBlocks(Irel[i]);
+				/* Update scanned index pages of a relation*/
+				pgstat_report_progress_update_counter(4, scanned_index_pages);
+			}
+			pgstat_report_progress_update_counter(5, vacrelstats->num_index_scans);
 			/* Remove tuples from heap */
 			lazy_vacuum_heap(onerel, vacrelstats);
 
@@ -617,8 +643,10 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
 			 * valid.
 			 */
 			vacrelstats->num_dead_tuples = 0;
+			scanned_index_pages = 0;
 			vacrelstats->num_index_scans++;
 		}
+		pgstat_report_progress_update_counter(0, VACUUM_PHASE_SCAN_HEAP);
 
 		/*
 		 * Pin the visibility map page in case we need to mark the page
@@ -1089,8 +1117,11 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
 		 */
 		if (vacrelstats->num_dead_tuples == prev_dead_count)
 			RecordPageWithFreeSpace(onerel, blkno, freespace);
-	}
 
+		if (blkno == nblocks - 1 && vacrelstats->num_dead_tuples == 0 && nindexes != 0
+			&& vacrelstats->num_index_scans == 0)
+			total_index_pages = 0;
+	}
 	pfree(frozen);
 
 	/* save stats for use later */
@@ -1120,14 +1151,22 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
 		/* Log cleanup info before we touch indexes */
 		vacuum_log_cleanup_info(onerel, vacrelstats);
 
+		pgstat_report_progress_update_counter(0, VACUUM_PHASE_VACUUM_INDEX_HEAP);
 		/* Remove index entries */
 		for (i = 0; i < nindexes; i++)
+		{
 			lazy_vacuum_index(Irel[i],
 							  &indstats[i],
 							  vacrelstats);
+			scanned_index_pages += RelationGetNumberOfBlocks(Irel[i]);
+			/* Update the scanned index pages and number of index scan */
+			pgstat_report_progress_update_counter(4, scanned_index_pages);
+		}
+		pgstat_report_progress_update_counter(5, vacrelstats->num_index_scans + 1);
 		/* Remove tuples from heap */
 		lazy_vacuum_heap(onerel, vacrelstats);
 		vacrelstats->num_index_scans++;
+		scanned_index_pages = 0;
 	}
 
 	/* Do post-vacuum cleanup and statistics update for each index */
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index da768c6..7c4c1c5 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -2851,6 +2851,82 @@ pgstat_report_activity(BackendState state, const char *cmd_str)
 	pgstat_increment_changecount_after(beentry);
 }
 
+/*-----------
+ * pgstat_report_progress_update_counter()-
+ *
+ * Called to update different values of command progress
+ *-----------
+ */
+void
+pgstat_report_progress_update_counter(int index, uint32 counter)
+{
+	volatile PgBackendStatus *beentry = MyBEEntry;
+
+	if(!beentry)
+		return;
+
+	if (!pgstat_track_activities)
+		return;
+
+	pgstat_increment_changecount_before(beentry);
+	beentry->st_progress_param[index] = counter;
+	pgstat_increment_changecount_after(beentry);
+}
+
+/* ----------
+ *	pgstat_report_progress_set_command_target() -
+ *
+ *	Called to update command target relation oid.
+ * ----------
+ */
+void
+pgstat_report_progress_set_command_target(Oid relid)
+{
+	volatile PgBackendStatus *beentry = MyBEEntry;
+
+	if (!beentry)
+		return;
+
+	if (!pgstat_track_activities)
+		return;
+
+	pgstat_increment_changecount_before(beentry);
+	beentry->st_relid = relid;
+	pgstat_increment_changecount_after(beentry);
+}
+
+/*-----------
+ * pgstat_report_progress_set_command()-
+ *
+ * Called to update command the backend is about to start running.
+ *-----------
+ */
+void
+pgstat_report_progress_set_command(int32 commandId)
+{
+	volatile PgBackendStatus *beentry = MyBEEntry;
+
+	pgstat_increment_changecount_before(beentry);
+	beentry->st_command = commandId;
+	pgstat_increment_changecount_after(beentry);
+}
+
+/*--------
+ * pgstat_reset_local_progress()-
+ *
+ * Reset local backend's progress parameters. Resetting st_command will do.
+ *--------
+ */
+void
+pgstat_reset_local_progress()
+{
+	PgBackendStatus *beentry = MyBEEntry;
+
+	pgstat_increment_changecount_before(beentry);
+	beentry->st_command = 0;
+	pgstat_increment_changecount_after(beentry);
+}
+
 /* ----------
  * pgstat_report_appname() -
  *
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 1b22fcc..bb6cc7a 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -53,6 +53,7 @@ extern Datum pg_stat_get_function_self_time(PG_FUNCTION_ARGS);
 
 extern Datum pg_stat_get_backend_idset(PG_FUNCTION_ARGS);
 extern Datum pg_stat_get_activity(PG_FUNCTION_ARGS);
+extern Datum pg_stat_get_progress_info(PG_FUNCTION_ARGS);
 extern Datum pg_backend_pid(PG_FUNCTION_ARGS);
 extern Datum pg_stat_get_backend_pid(PG_FUNCTION_ARGS);
 extern Datum pg_stat_get_backend_dbid(PG_FUNCTION_ARGS);
@@ -523,7 +524,106 @@ pg_stat_get_backend_idset(PG_FUNCTION_ARGS)
 		SRF_RETURN_DONE(funcctx);
 	}
 }
+/*
+ * Returns progress values of commands stored by each backend
+ * executing command.
+ */
+Datum
+pg_stat_get_progress_info(PG_FUNCTION_ARGS)
+{
+#define PG_STAT_GET_PROGRESS_COLS	30
+	int			num_backends = pgstat_fetch_stat_numbackends();
+	int			curr_backend;
+	int			cmdtype = PG_GETARG_INT32(0);
+	TupleDesc	tupdesc;
+	Tuplestorestate *tupstore;
+	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+	MemoryContext per_query_ctx;
+	MemoryContext oldcontext;
+
+	/* check to see if caller supports us returning a tuplestore */
+	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("set-valued function called in context that cannot accept a set")));
+	if (!(rsinfo->allowedModes & SFRM_Materialize))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("materialize mode required, but it is not " \
+						"allowed in this context")));
+
+	/* Build a tuple descriptor for our result type */
+	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+		elog(ERROR, "return type must be a row type");
+
+	per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
+	oldcontext = MemoryContextSwitchTo(per_query_ctx);
+
+	tupstore = tuplestore_begin_heap(true, false, work_mem);
+	rsinfo->returnMode = SFRM_Materialize;
+	rsinfo->setResult = tupstore;
+	rsinfo->setDesc = tupdesc;
+	MemoryContextSwitchTo(oldcontext);
 
+	for (curr_backend = 1; curr_backend <= num_backends; curr_backend++)
+	{
+		Datum		values[PG_STAT_GET_PROGRESS_COLS];
+		bool		nulls[PG_STAT_GET_PROGRESS_COLS];
+		LocalPgBackendStatus *local_beentry;
+		PgBackendStatus *beentry;
+
+		MemSet(values, 0, sizeof(values));
+		MemSet(nulls, 0, sizeof(nulls));
+
+		local_beentry = pgstat_fetch_stat_local_beentry(curr_backend);
+		if (!local_beentry)
+			continue;
+		beentry = &local_beentry->backendStatus;
+
+		/* Report values for only those backends which are running command */
+		if(!beentry || beentry->st_command != cmdtype)
+			continue;
+
+		/* Values available to all callers */
+		values[0] = Int32GetDatum(beentry->st_procpid);
+		values[1] = ObjectIdGetDatum(beentry->st_relid);
+
+		/*  Report values for only those backends which are running VACUUM command */
+		if (cmdtype == COMMAND_LAZY_VACUUM)
+		{
+			/*Progress can only be viewed by role member.*/
+			if (has_privs_of_role(GetUserId(), beentry->st_userid))
+			{
+				values[2] = UInt32GetDatum(beentry->st_progress_param[0]);
+				values[3] = UInt32GetDatum(beentry->st_progress_param[1]);
+				values[4] = UInt32GetDatum(beentry->st_progress_param[2]);
+				values[5] = UInt32GetDatum(beentry->st_progress_param[3]);
+				values[6] = UInt32GetDatum(beentry->st_progress_param[4]);
+				values[7] = UInt32GetDatum(beentry->st_progress_param[5]);
+				if (beentry->st_progress_param[1] != 0)
+					values[8] = Float8GetDatum(beentry->st_progress_param[2] * 100 / beentry->st_progress_param[1]);
+				else
+					nulls[8] = true;
+			}
+			else
+			{
+				nulls[2] = true;
+				nulls[3] = true;
+				nulls[4] = true;
+				nulls[5] = true;
+				nulls[6] = true;
+				nulls[7] = true;
+				nulls[8] = true;
+			}
+		}
+		tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+	}
+
+	/* clean up and return the tuplestore */
+	tuplestore_donestoring(tupstore);
+
+	return (Datum) 0;
+}
 /*
  * Returns activity of PG backends.
  */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 62b9125..4e6daa0 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -2710,6 +2710,8 @@ DATA(insert OID = 1936 (  pg_stat_get_backend_idset		PGNSP PGUID 12 1 100 0 0 f
 DESCR("statistics: currently active backend IDs");
 DATA(insert OID = 2022 (  pg_stat_get_activity			PGNSP PGUID 12 1 100 0 0 f f f f f t s r 1 0 2249 "23" "{23,26,23,26,25,25,25,16,1184,1184,1184,1184,869,25,23,28,28,16,25,25,23,16,25}" "{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}" "{pid,datid,pid,usesysid,application_name,state,query,waiting,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port,backend_xid,backend_xmin,ssl,sslversion,sslcipher,sslbits,sslcompression,sslclientdn}" _null_ _null_ pg_stat_get_activity _null_ _null_ _null_ ));
 DESCR("statistics: information about currently active backends");
+DATA(insert OID = 3319 (  pg_stat_get_progress_info           PGNSP PGUID 12 1 100 0 0 f f f f f t s r 1 0 2249 "23" "{23,23,26,23,23,23,23,23,23,701}" "{i,o,o,o,o,o,o,o,o,o}" "{cmdtype,pid,relid,phase,total_heap_blks,current_heap_blkno,total_index_pages,scanned_index_pages,index_scan_count,percent_complete}" _null_ _null_ pg_stat_get_progress_info _null_ _null_ _null_ ));
+DESCR("statistics: information about progress of backends running maintenance command");
 DATA(insert OID = 3099 (  pg_stat_get_wal_senders	PGNSP PGUID 12 1 10 0 0 f f f f f t s r 0 0 2249 "" "{23,25,3220,3220,3220,3220,23,25}" "{o,o,o,o,o,o,o,o}" "{pid,state,sent_location,write_location,flush_location,replay_location,sync_priority,sync_state}" _null_ _null_ pg_stat_get_wal_senders _null_ _null_ _null_ ));
 DESCR("statistics: information about currently active replication");
 DATA(insert OID = 3317 (  pg_stat_get_wal_receiver	PGNSP PGUID 12 1 0 0 0 f f f f f f s r 0 0 2249 "" "{23,25,3220,23,3220,23,1184,1184,3220,1184,25}" "{o,o,o,o,o,o,o,o,o,o,o}" "{pid,status,receive_start_lsn,receive_start_tli,received_lsn,received_tli,last_msg_send_time,last_msg_receipt_time,latest_end_lsn,latest_end_time,slot_name}" _null_ _null_ pg_stat_get_wal_receiver _null_ _null_ _null_ ));
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 65e968e..3ee2c5a 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -205,6 +205,9 @@ typedef struct PgStat_MsgHdr
 #define PGSTAT_MAX_MSG_SIZE 1000
 #define PGSTAT_MSG_PAYLOAD	(PGSTAT_MAX_MSG_SIZE - sizeof(PgStat_MsgHdr))
 
+#define N_PROGRESS_PARAM 10
+#define VACUUM_PHASE_SCAN_HEAP 1
+#define VACUUM_PHASE_VACUUM_INDEX_HEAP 2
 
 /* ----------
  * PgStat_MsgDummy				A dummy message, ignored by the collector
@@ -776,6 +779,19 @@ typedef struct PgBackendStatus
 
 	/* current command string; MUST be null-terminated */
 	char	   *st_activity;
+
+	/*
+	 * Information about the progress of activity/command being run by the backend.
+	 * The progress parameters indicate progress of a command. Different
+	 * commands can report different number of parameters of each type.
+	 *
+	 * st_command reports which activity/command is being run by the backend.
+	 * This is used in the SQL callable functions to display progress values
+	 * for respective commands.
+	 */
+	uint32		st_command;
+	uint32		st_progress_param[N_PROGRESS_PARAM];
+	Oid		st_relid;
 } PgBackendStatus;
 
 /*
@@ -815,6 +831,7 @@ typedef struct PgBackendStatus
 		save_changecount = beentry->st_changecount; \
 	} while (0)
 
+#define COMMAND_LAZY_VACUUM 0x01
 /* ----------
  * LocalPgBackendStatus
  *
@@ -928,6 +945,9 @@ 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_progress_set_command(int32 commandId);
+extern void pgstat_reset_local_progress(void);
+extern void pgstat_report_progress_update_counter(int index, uint32 counter);
 extern void pgstat_report_tempfile(size_t filesize);
 extern void pgstat_report_appname(const char *appname);
 extern void pgstat_report_xact_timestamp(TimestampTz tstamp);
@@ -938,6 +958,7 @@ extern const char *pgstat_get_crashed_backend_activity(int pid, char *buffer,
 
 extern PgStat_TableStatus *find_tabstat_entry(Oid rel_id);
 extern PgStat_BackendFunctionEntry *find_funcstat_entry(Oid func_id);
+extern void pgstat_report_progress_set_command_target(Oid relid);
 
 extern void pgstat_initstats(Relation rel);
 
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 81bc5c9..e667c8c 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1851,6 +1851,20 @@ pg_stat_user_tables| SELECT pg_stat_all_tables.relid,
     pg_stat_all_tables.autoanalyze_count
    FROM pg_stat_all_tables
   WHERE ((pg_stat_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_tables.schemaname !~ '^pg_toast'::text));
+pg_stat_vacuum_progress| SELECT s.pid,
+    s.relid,
+        CASE s.phase
+            WHEN 1 THEN 'Scanning Heap'::text
+            WHEN 2 THEN 'Vacuuming Index and Heap'::text
+            ELSE 'Unknown phase'::text
+        END AS phase,
+    s.total_heap_blks,
+    s.current_heap_blkno,
+    s.total_index_pages,
+    s.scanned_index_pages,
+    s.index_scan_count,
+    s.percent_complete
+   FROM pg_stat_get_progress_info(1) s(pid, relid, phase, total_heap_blks, current_heap_blkno, total_index_pages, scanned_index_pages, index_scan_count, percent_complete);
 pg_stat_wal_receiver| SELECT s.pid,
     s.status,
     s.receive_start_lsn,
