diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index e826c19..a55327c 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -5558,6 +5558,21 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-track-sql" xreflabel="track_sql">
+      <term><varname>track_sql</varname> (<type>boolean</type>)
+      <indexterm>
+       <primary><varname>track_sql</> configuration parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Enables collection of different SQL statement statistics that are
+        executed on the instance. This parameter is off by default. Only
+        superusers can change this setting.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-stats-temp-directory" xreflabel="stats_temp_directory">
       <term><varname>stats_temp_directory</varname> (<type>string</type>)
       <indexterm>
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 3de489e..26b4bab 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -513,6 +513,13 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
       <command>VACUUM</>, showing current progress.
       See <xref linkend='vacuum-progress-reporting'>.</entry>
      </row>
+     
+     <row>
+      <entry><structname>pg_stat_sql</><indexterm><primary>pg_stat_sql</primary></indexterm></entry>
+      <entry>Shows statistics about the SQL statements that are
+       executed on the instance.
+       See <xref linkend="pg-stat-sql-view"> for details.</entry>
+     </row>
     </tbody>
    </tgroup>
   </table>
@@ -2391,6 +2398,40 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
    controls exactly which functions are tracked.
   </para>
 
+  <table id="pg-stat-sql-view" xreflabel="pg_stat_sql">
+   <title><structname>pg_stat_sql</structname> View</title>
+   <tgroup cols="3">
+    <thead>
+    <row>
+      <entry>Column</entry>
+      <entry>Type</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+   <tbody>
+    <row>
+     <entry><structfield>tag</></entry>
+     <entry><type>text</></entry>
+     <entry>Name of the SQL statement</entry>
+    </row>
+    <row>
+     <entry><structfield>count</></entry>
+     <entry><type>bigint</></entry>
+     <entry>Number of times the SQL statement is executed</entry>
+    </row>
+    </tbody>
+   </tgroup>
+  </table>
+
+  <para>
+   The <structname>pg_stat_sql</structname> view will contain statistics
+   about number of SQL statements that are executed on the instance.
+   The <xref linkend="guc-track-sql"> parameter controls the SQL statement
+   execution statistics.
+  </para>
+  
+  
  </sect2>
 
  <sect2 id="monitoring-stats-functions">
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index ada2142..0a177b4 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -842,6 +842,11 @@ CREATE VIEW pg_replication_origin_status AS
 
 REVOKE ALL ON pg_replication_origin_status FROM public;
 
+CREATE VIEW pg_stat_sql AS
+    SELECT * FROM pg_stat_sql();
+
+REVOKE EXECUTE ON FUNCTION pg_stat_sql() FROM public;
+
 --
 -- We have a few function definitions in here, too.
 -- At some point there might be enough to justify breaking them out into
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index a9efee8..0649022 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -100,6 +100,7 @@
 #define PGSTAT_DB_HASH_SIZE		16
 #define PGSTAT_TAB_HASH_SIZE	512
 #define PGSTAT_FUNCTION_HASH_SIZE	512
+#define PGSTAT_SQLSTMT_HASH_SIZE 256
 
 
 /* ----------
@@ -108,6 +109,7 @@
  */
 bool		pgstat_track_activities = false;
 bool		pgstat_track_counts = false;
+bool		pgstat_track_sql = false;
 int			pgstat_track_functions = TRACK_FUNC_OFF;
 int			pgstat_track_activity_query_size = 1024;
 
@@ -126,6 +128,19 @@ char	   *pgstat_stat_tmpname = NULL;
  */
 PgStat_MsgBgWriter BgWriterStats;
 
+/*
+ * Backend specific SQL statement information stored in this hash table,
+ * before sent it to the stats collector.
+ */
+static HTAB *pgStatBackendSql = NULL;
+
+/*
+ * Global SQL statement stats information gathered in the collector are stored in
+ * this hash table.
+ */
+HTAB	   *pgStatSql = NULL;
+
+
 /* ----------
  * Local data
  * ----------
@@ -270,6 +285,7 @@ static bool pgstat_db_requested(Oid databaseid);
 
 static void pgstat_send_tabstat(PgStat_MsgTabstat *tsmsg);
 static void pgstat_send_funcstats(void);
+static void pgstat_send_sqlstmt(void);
 static HTAB *pgstat_collect_oids(Oid catalogid);
 
 static PgStat_TableStatus *get_tabstat_entry(Oid rel_id, bool isshared);
@@ -301,6 +317,7 @@ static void pgstat_recv_funcpurge(PgStat_MsgFuncpurge *msg, int len);
 static void pgstat_recv_recoveryconflict(PgStat_MsgRecoveryConflict *msg, int len);
 static void pgstat_recv_deadlock(PgStat_MsgDeadlock *msg, int len);
 static void pgstat_recv_tempfile(PgStat_MsgTempFile *msg, int len);
+static void pgstat_recv_sqlstmt(PgStat_MsgSqlstmt * msg, int len);
 
 /* ------------------------------------------------------------
  * Public functions called from postmaster follow
@@ -834,6 +851,10 @@ pgstat_report_stat(bool force)
 
 	/* Now, send function statistics */
 	pgstat_send_funcstats();
+
+	/* Now, send sql statistics */
+	if (pgstat_track_sql)
+		pgstat_send_sqlstmt();
 }
 
 /*
@@ -1260,11 +1281,13 @@ pgstat_reset_shared_counters(const char *target)
 		msg.m_resettarget = RESET_ARCHIVER;
 	else if (strcmp(target, "bgwriter") == 0)
 		msg.m_resettarget = RESET_BGWRITER;
+	else if (strcmp(target, "sqlstmt") == 0)
+		msg.m_resettarget = RESET_SQLSTMT;
 	else
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 				 errmsg("unrecognized reset target: \"%s\"", target),
-				 errhint("Target must be \"archiver\" or \"bgwriter\".")));
+				 errhint("Target must be \"archiver\" or \"bgwriter\" or \"sqlstmt\".")));
 
 	pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_RESETSHAREDCOUNTER);
 	pgstat_send(&msg, sizeof(msg));
@@ -3677,6 +3700,89 @@ pgstat_send_bgwriter(void)
 	MemSet(&BgWriterStats, 0, sizeof(BgWriterStats));
 }
 
+/* ----------
+ * pgstat_send_sqlstmt(void)
+ *
+ *		Send SQL statement statistics to the collector
+ * ----------
+ */
+static void
+pgstat_send_sqlstmt(void)
+{
+	PgStat_MsgSqlstmt msg;
+	PgStatSqlstmtEntry *entry;
+	HASH_SEQ_STATUS hstat;
+
+	if (pgStatBackendSql == NULL)
+		return;
+
+	pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_SQLSTMT);
+	msg.m_nentries = 0;
+
+	hash_seq_init(&hstat, pgStatBackendSql);
+	while ((entry = (PgStatSqlstmtEntry *) hash_seq_search(&hstat)) != NULL)
+	{
+		PgStatSqlstmtEntry *m_ent;
+
+		/* Skip it if no counts accumulated since last time */
+		if (entry->count == 0)
+			continue;
+
+		/* need to convert format of time accumulators */
+		m_ent = &msg.m_entry[msg.m_nentries];
+		memcpy(m_ent, entry, sizeof(PgStatSqlstmtEntry));
+
+		if (++msg.m_nentries >= PGSTAT_NUM_SQLSTMTS)
+		{
+			pgstat_send(&msg, offsetof(PgStat_MsgSqlstmt, m_entry[0]) +
+						msg.m_nentries * sizeof(PgStatSqlstmtEntry));
+			msg.m_nentries = 0;
+		}
+
+		/* reset the entry's count */
+		entry->count = 0;
+	}
+
+	if (msg.m_nentries > 0)
+		pgstat_send(&msg, offsetof(PgStat_MsgSqlstmt, m_entry[0]) +
+					msg.m_nentries * sizeof(PgStatSqlstmtEntry));
+}
+
+/*
+ * Count SQL statement for pg_stat_sql view
+ */
+void
+pgstat_count_sqlstmt(const char *commandTag)
+{
+	PgStatSqlstmtEntry *htabent;
+	bool		found;
+
+	if (!pgstat_track_sql)
+		return;
+
+	if (!pgStatBackendSql)
+	{
+		/* First time through - initialize SQL statement stat table */
+		HASHCTL		hash_ctl;
+
+		memset(&hash_ctl, 0, sizeof(hash_ctl));
+		hash_ctl.keysize = NAMEDATALEN;
+		hash_ctl.entrysize = sizeof(PgStatSqlstmtEntry);
+		pgStatBackendSql = hash_create("SQL statement stat entries",
+									   PGSTAT_SQLSTMT_HASH_SIZE,
+									   &hash_ctl,
+									   HASH_ELEM | HASH_BLOBS);
+	}
+
+	/* Get the stats entry for this SQL statement, create if necessary */
+	htabent = hash_search(pgStatBackendSql, commandTag,
+						  HASH_ENTER, &found);
+	if (!found)
+		htabent->count = 1;
+	else
+		htabent->count++;
+}
+
 
 /* ----------
  * PgstatCollectorMain() -
@@ -3893,6 +3999,9 @@ PgstatCollectorMain(int argc, char *argv[])
 					pgstat_recv_tempfile((PgStat_MsgTempFile *) &msg, len);
 					break;
 
+				case PGSTAT_MTYPE_SQLSTMT:
+					pgstat_recv_sqlstmt((PgStat_MsgSqlstmt *) & msg, len);
+					break;
 				default:
 					break;
 			}
@@ -4113,6 +4222,7 @@ pgstat_write_statsfiles(bool permanent, bool allDbs)
 {
 	HASH_SEQ_STATUS hstat;
 	PgStat_StatDBEntry *dbentry;
+	PgStatSqlstmtEntry *sqlstmtentry;
 	FILE	   *fpout;
 	int32		format_id;
 	const char *tmpfile = permanent ? PGSTAT_STAT_PERMANENT_TMPFILE : pgstat_stat_tmpname;
@@ -4185,6 +4295,15 @@ pgstat_write_statsfiles(bool permanent, bool allDbs)
 		(void) rc;				/* we'll check for error with ferror */
 	}
 
+	/* Walk through the SQL statement Stats Hash table. */
+	hash_seq_init(&hstat, pgStatSql);
+	while ((sqlstmtentry = (PgStatSqlstmtEntry *) hash_seq_search(&hstat)) != NULL)
+	{
+		fputc('S', fpout);
+		rc = fwrite(sqlstmtentry, sizeof(PgStatSqlstmtEntry), 1, fpout);
+		(void) rc;				/* we'll check for error with ferror */
+	}
+
 	/*
 	 * No more output to be done. Close the temp file and replace the old
 	 * pgstat.stat with it.  The ferror() check replaces testing for error
@@ -4409,6 +4528,14 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
 	dbhash = hash_create("Databases hash", PGSTAT_DB_HASH_SIZE, &hash_ctl,
 						 HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
 
+	/* Create SQL statement counter Stats HashTable */
+	memset(&hash_ctl, 0, sizeof(hash_ctl));
+	hash_ctl.keysize = NAMEDATALEN;
+	hash_ctl.entrysize = sizeof(PgStatSqlstmtEntry);
+	pgStatSql = hash_create("SQL statement counter",
+							PGSTAT_SQLSTMT_HASH_SIZE, &hash_ctl,
+							HASH_ELEM);
+
 	/*
 	 * Clear out global and archiver statistics so they start from zero in
 	 * case we can't load an existing statsfile.
@@ -4554,6 +4681,38 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
 
 				break;
 
+			case 'S':
+				{
+					PgStatSqlstmtEntry stats;
+					PgStatSqlstmtEntry *lstats;
+
+					if (fread(&stats, 1, sizeof(PgStatSqlstmtEntry), fpin) != sizeof(PgStatSqlstmtEntry))
+					{
+						ereport(pgStatRunningInCollector ? LOG : WARNING,
+								(errmsg("corrupted statistics file \"%s\"",
+										statfile)));
+						goto done;
+					}
+
+					/*
+					 * Add to the SQL statement counter hash
+					 */
+					lstats = (PgStatSqlstmtEntry *) hash_search(pgStatSql,
+													   (void *) &(stats.tag),
+																HASH_ENTER,
+																&found);
+					if (found)
+					{
+						ereport(pgStatRunningInCollector ? LOG : WARNING,
+								(errmsg("corrupted statistics file \"%s\"",
+										statfile)));
+						goto done;
+					}
+
+					memcpy(lstats, &stats, sizeof(PgStatSqlstmtEntry));
+
+					break;
+				}
 			case 'E':
 				goto done;
 
@@ -4853,6 +5012,21 @@ pgstat_read_db_statsfile_timestamp(Oid databaseid, bool permanent,
 
 				break;
 
+			case 'S':
+			{
+				PgStatSqlstmtEntry stats;
+
+				if (fread(&stats, 1, sizeof(PgStatSqlstmtEntry), fpin) != sizeof(PgStatSqlstmtEntry))
+				{
+					ereport(pgStatRunningInCollector ? LOG : WARNING,
+							(errmsg("corrupted statistics file \"%s\"",
+									statfile)));
+					goto done;
+				}
+
+				break;
+			}
+
 			case 'E':
 				goto done;
 
@@ -5378,6 +5552,21 @@ pgstat_recv_resetsharedcounter(PgStat_MsgResetsharedcounter *msg, int len)
 		memset(&archiverStats, 0, sizeof(archiverStats));
 		archiverStats.stat_reset_timestamp = GetCurrentTimestamp();
 	}
+	else if (msg->m_resettarget == RESET_SQLSTMT)
+	{
+		HASHCTL		hash_ctl;
+
+		/* Destroy the SQL statement counter stats HashTable */
+		hash_destroy(pgStatSql);
+
+		/* Create SQL statement counter Stats HashTable */
+		memset(&hash_ctl, 0, sizeof(hash_ctl));
+		hash_ctl.keysize = NAMEDATALEN;
+		hash_ctl.entrysize = sizeof(PgStatSqlstmtEntry);
+		pgStatSql = hash_create("SQL statement counter",
+								PGSTAT_SQLSTMT_HASH_SIZE, &hash_ctl,
+								HASH_ELEM);
+	}
 
 	/*
 	 * Presumably the sender of this message validated the target, don't
@@ -5558,6 +5747,37 @@ pgstat_recv_bgwriter(PgStat_MsgBgWriter *msg, int len)
 }
 
 /* ----------
+ * pgstat_recv_sqlstmt() -
+ *
+ *	Process a SQL statement statistics message.
+ * ----------
+ */
+static void
+pgstat_recv_sqlstmt(PgStat_MsgSqlstmt * msg, int len)
+{
+	PgStatSqlstmtEntry *entry;
+	bool		found;
+	int			i;
+
+	for (i = 0; i < msg->m_nentries; i++)
+	{
+		/* Get the stats entry for this SQL statement, create if necessary */
+		entry = (PgStatSqlstmtEntry *) hash_search(pgStatSql, (void *) &(msg->m_entry[i].tag),
+												   HASH_ENTER, &found);
+
+		if (found)
+		{
+			entry->count += msg->m_entry[i].count;
+		}
+		else
+		{
+			memcpy(entry->tag, msg->m_entry[i].tag, NAMEDATALEN);
+			entry->count = msg->m_entry[i].count;
+		}
+	}
+}
+
+/* ----------
  * pgstat_recv_recoveryconflict() -
  *
  *	Process a RECOVERYCONFLICT message.
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 2347f1b..4a168ca 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -1102,6 +1102,12 @@ exec_simple_query(const char *query_string)
 
 		PortalDrop(portal, false);
 
+		/*
+		 * Count SQL statement for pg_stat_sql view
+		 */
+		if (pgstat_track_sql)
+			pgstat_count_sqlstmt(commandTag);
+
 		if (IsA(parsetree, TransactionStmt))
 		{
 			/*
@@ -1983,6 +1989,29 @@ exec_execute_message(const char *portal_name, long max_rows)
 
 	(*receiver->rDestroy) (receiver);
 
+	/*
+	 * Count SQL Statement for pgx_stat_sql
+	 */
+	if (pgstat_track_sql)
+	{
+		CachedPlanSource *psrc = NULL;
+
+		if (portal->prepStmtName)
+		{
+			PreparedStatement *pstmt;
+
+			pstmt = FetchPreparedStatement(portal->prepStmtName, false);
+			if (pstmt)
+				psrc = pstmt->plansource;
+		}
+		else
+			psrc = unnamed_stmt_psrc;
+
+		/* psrc should not be NULL here */
+		if (psrc && psrc->commandTag && !execute_is_fetch)
+			pgstat_count_sqlstmt(psrc->commandTag);
+	}
+
 	if (completed)
 	{
 		if (is_xact_command)
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 2d3cf9e..c3d451b 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -95,6 +95,7 @@ extern Datum pg_stat_get_db_blk_read_time(PG_FUNCTION_ARGS);
 extern Datum pg_stat_get_db_blk_write_time(PG_FUNCTION_ARGS);
 
 extern Datum pg_stat_get_archiver(PG_FUNCTION_ARGS);
+extern Datum pg_stat_sql(PG_FUNCTION_ARGS);
 
 extern Datum pg_stat_get_bgwriter_timed_checkpoints(PG_FUNCTION_ARGS);
 extern Datum pg_stat_get_bgwriter_requested_checkpoints(PG_FUNCTION_ARGS);
@@ -1931,3 +1932,77 @@ pg_stat_get_archiver(PG_FUNCTION_ARGS)
 	PG_RETURN_DATUM(HeapTupleGetDatum(
 								   heap_form_tuple(tupdesc, values, nulls)));
 }
+
+Datum
+pg_stat_sql(PG_FUNCTION_ARGS)
+{
+	TupleDesc	tupdesc;
+	Datum		values[2];
+	bool		nulls[2];
+	ReturnSetInfo *rsi;
+	MemoryContext old_cxt;
+	Tuplestorestate *tuple_store;
+	PgStatSqlstmtEntry *sqlstmtentry;
+	HASH_SEQ_STATUS hstat;
+
+	/* Function call to let the backend read the stats file */
+	pgstat_fetch_global();
+
+	/* Initialize values and nulls arrays */
+	MemSet(values, 0, sizeof(values));
+	MemSet(nulls, 0, sizeof(nulls));
+
+	/*
+	 * We must use the Materialize mode to be more efficient than checking the
+	 * HASH every time.
+	 */
+	rsi = (ReturnSetInfo *) fcinfo->resultinfo;
+
+	if (!rsi || !IsA(rsi, ReturnSetInfo) ||
+		(rsi->allowedModes & SFRM_Materialize) == 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("set-valued function called in context that cannot accept a set")));
+
+	rsi->returnMode = SFRM_Materialize;
+
+	/*
+	 * Create the tupledesc and tuplestore in the per_query context as
+	 * required for SFRM_Materialize.
+	 */
+	old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
+
+	/* Initialize attributes information in the tuple descriptor */
+	tupdesc = CreateTemplateTupleDesc(2, false);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "tag",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "count",
+					   INT8OID, -1, 0);
+
+	BlessTupleDesc(tupdesc);
+
+	tuple_store =
+		tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
+							  false, work_mem);
+
+	MemoryContextSwitchTo(old_cxt);
+
+	/* Walk through the SQL statement Stats Hash table. */
+	hash_seq_init(&hstat, pgStatSql);
+	while ((sqlstmtentry = (PgStatSqlstmtEntry *) hash_seq_search(&hstat)) != NULL)
+	{
+		HeapTuple	tuple;
+
+		/* Fill values and NULLs */
+		values[0] = CStringGetTextDatum(sqlstmtentry->tag);
+		values[1] = Int64GetDatum(sqlstmtentry->count);
+
+		tuple = heap_form_tuple(tupdesc, values, nulls);
+		tuplestore_puttuple(tuple_store, tuple);
+	}
+
+	rsi->setDesc = tupdesc;
+	rsi->setResult = tuple_store;
+
+	PG_RETURN_NULL();
+}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 622279b..8eb6c85 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -1222,6 +1222,16 @@ static struct config_bool ConfigureNamesBool[] =
 	},
 
 	{
+		{"track_sql", PGC_SUSET, STATS_COLLECTOR,
+			gettext_noop("Collects timing statistics for database I/O activity."),
+			NULL
+		},
+		&pgstat_track_sql,
+		false,
+		NULL, NULL, NULL
+	},
+
+	{
 		{"update_process_title", PGC_SUSET, PROCESS_TITLE,
 			gettext_noop("Updates the process title to show the active SQL command."),
 			gettext_noop("Enables updating of the process title every time a new SQL command is received by the server.")
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 05b1373..4b720a6 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -475,6 +475,7 @@
 
 #track_activities = on
 #track_counts = on
+#track_sql = off
 #track_io_timing = off
 #track_functions = none			# none, pl, all
 #track_activity_query_size = 1024	# (change requires restart)
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index e2d08ba..6b52222 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -2896,6 +2896,9 @@ DESCR("statistics: total execution time of function in current transaction, in m
 DATA(insert OID = 3048 (  pg_stat_get_xact_function_self_time	PGNSP PGUID 12 1 0 0 0 f f f f t f v r 1 0 701 "26" _null_ _null_ _null_ _null_ _null_ pg_stat_get_xact_function_self_time _null_ _null_ _null_ ));
 DESCR("statistics: self execution time of function in current transaction, in msec");
 
+DATA(insert OID = 3343 (  pg_stat_sql PGNSP PGUID 12 1 1000 0 0 f f f f t t v s 0 0 2249 "" "{25,20}" "{o,o}" "{tag,count}" _null_ _null_ pg_stat_sql _null_ _null_ _null_ ));
+DESCR("statistics: Show SQL statement statistics");
+
 DATA(insert OID = 3788 (  pg_stat_get_snapshot_timestamp PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 1184 "" _null_ _null_ _null_ _null_ _null_	pg_stat_get_snapshot_timestamp _null_ _null_ _null_ ));
 DESCR("statistics: timestamp of the current statistics snapshot");
 DATA(insert OID = 2230 (  pg_stat_clear_snapshot		PGNSP PGUID 12 1 0 0 0 f f f f f f v r 0 0 2278 "" _null_ _null_ _null_ _null_ _null_ pg_stat_clear_snapshot _null_ _null_ _null_ ));
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 1c9bf13..346ffef 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -64,7 +64,8 @@ typedef enum StatMsgType
 	PGSTAT_MTYPE_FUNCPURGE,
 	PGSTAT_MTYPE_RECOVERYCONFLICT,
 	PGSTAT_MTYPE_TEMPFILE,
-	PGSTAT_MTYPE_DEADLOCK
+	PGSTAT_MTYPE_DEADLOCK,
+	PGSTAT_MTYPE_SQLSTMT
 } StatMsgType;
 
 /* ----------
@@ -119,7 +120,8 @@ typedef struct PgStat_TableCounts
 typedef enum PgStat_Shared_Reset_Target
 {
 	RESET_ARCHIVER,
-	RESET_BGWRITER
+	RESET_BGWRITER,
+	RESET_SQLSTMT
 } PgStat_Shared_Reset_Target;
 
 /* Possible object types for resetting single counters */
@@ -530,6 +532,30 @@ typedef struct PgStat_MsgDeadlock
 	Oid			m_databaseid;
 } PgStat_MsgDeadlock;
 
+/* ----------
+ * PgStatSqlstmt
+ * ----------
+ */
+typedef struct PgStatSqlstmtEntry
+{
+	char tag[NAMEDATALEN];
+	PgStat_Counter 	count;
+} PgStatSqlstmtEntry;
+
+
+/* ----------
+ * PgStatMsgSqlstmt			Sent by the sql to update statistics.
+ * ----------
+ */
+#define PGSTAT_NUM_SQLSTMTS  \
+	((PGSTAT_MSG_PAYLOAD - sizeof(int)) / sizeof(PgStatSqlstmtEntry))
+
+typedef struct PgStatMsgSqlstmt
+{
+	PgStat_MsgHdr m_hdr;
+	int			m_nentries;
+	PgStatSqlstmtEntry m_entry[PGSTAT_NUM_SQLSTMTS];
+} PgStat_MsgSqlstmt;
 
 /* ----------
  * PgStat_Msg					Union over all possible messages.
@@ -555,6 +581,7 @@ typedef union PgStat_Msg
 	PgStat_MsgFuncpurge msg_funcpurge;
 	PgStat_MsgRecoveryConflict msg_recoveryconflict;
 	PgStat_MsgDeadlock msg_deadlock;
+	PgStat_MsgSqlstmt msg_sqlstmt;
 } PgStat_Msg;
 
 
@@ -566,7 +593,7 @@ typedef union PgStat_Msg
  * ------------------------------------------------------------
  */
 
-#define PGSTAT_FILE_FORMAT_ID	0x01A5BC9D
+#define PGSTAT_FILE_FORMAT_ID	0x01A5BC9E
 
 /* ----------
  * PgStat_StatDBEntry			The collector's data per database
@@ -994,6 +1021,7 @@ typedef struct PgStat_FunctionCallUsage
  */
 extern bool pgstat_track_activities;
 extern bool pgstat_track_counts;
+extern bool pgstat_track_sql;
 extern int	pgstat_track_functions;
 extern PGDLLIMPORT int pgstat_track_activity_query_size;
 extern char *pgstat_stat_directory;
@@ -1006,6 +1034,11 @@ extern char *pgstat_stat_filename;
 extern PgStat_MsgBgWriter BgWriterStats;
 
 /*
+ * SQL statement statistics counter Hash table
+ */
+extern HTAB *pgStatSql;
+
+/*
  * Updated by pgstat_count_buffer_*_time macros
  */
 extern PgStat_Counter pgStatBlockReadTime;
@@ -1199,6 +1232,8 @@ extern void pgstat_twophase_postabort(TransactionId xid, uint16 info,
 extern void pgstat_send_archiver(const char *xlog, bool failed);
 extern void pgstat_send_bgwriter(void);
 
+extern void pgstat_count_sqlstmt(const char *commandTag);
+
 /* ----------
  * Support functions for the SQL-callable functions to
  * generate the pgstat* views.
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 00700f2..55934ca 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1795,6 +1795,9 @@ pg_stat_replication| SELECT s.pid,
    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, ssl, sslversion, sslcipher, sslbits, sslcompression, sslclientdn)
      JOIN pg_stat_get_wal_senders() w(pid, state, sent_location, write_location, flush_location, replay_location, sync_priority, sync_state) ON ((s.pid = w.pid)))
      LEFT JOIN pg_authid u ON ((s.usesysid = u.oid)));
+pg_stat_sql| SELECT pg_stat_sql.tag,
+    pg_stat_sql.count
+   FROM pg_stat_sql() pg_stat_sql(tag, count);
 pg_stat_ssl| SELECT s.pid,
     s.ssl,
     s.sslversion AS version,
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index a811265..be6db13 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -4,6 +4,62 @@
 -- Must be run after tenk2 has been created (by create_table),
 -- populated (by create_misc) and indexed (by create_index).
 --
+-- pg_stat_sql
+SET track_sql TO OFF;
+SELECT pg_stat_reset_shared('sqlstmt'); -- reset the counters
+ pg_stat_reset_shared 
+----------------------
+ 
+(1 row)
+
+-- force the rate-limiting logic in pgstat_report_stat() to time out
+-- and send a message
+SELECT pg_sleep(1.0);
+ pg_sleep 
+----------
+ 
+(1 row)
+
+SELECT * FROM pg_stat_sql where tag='SELECT';
+ tag | count 
+-----+-------
+(0 rows)
+
+SET track_sql TO ON;
+-- force the rate-limiting logic in pgstat_report_stat() to time out
+-- and send a message
+SELECT pg_sleep(1.0);
+ pg_sleep 
+----------
+ 
+(1 row)
+
+SELECT * FROM pg_stat_sql where tag='SELECT';
+  tag   | count 
+--------+-------
+ SELECT |     1
+(1 row)
+
+SET track_sql TO OFF;
+SELECT pg_stat_reset_shared('sqlstmt'); -- reset the counters
+ pg_stat_reset_shared 
+----------------------
+ 
+(1 row)
+
+-- force the rate-limiting logic in pgstat_report_stat() to time out
+-- and send a message
+SELECT pg_sleep(1.0);
+ pg_sleep 
+----------
+ 
+(1 row)
+
+SELECT * FROM pg_stat_sql where tag='SELECT';
+ tag | count 
+-----+-------
+(0 rows)
+
 -- conditio sine qua non
 SHOW track_counts;  -- must be on
  track_counts 
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index b3e2efa..34716d6 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -5,6 +5,30 @@
 -- populated (by create_misc) and indexed (by create_index).
 --
 
+-- pg_stat_sql
+SET track_sql TO OFF;
+SELECT pg_stat_reset_shared('sqlstmt'); -- reset the counters
+
+-- force the rate-limiting logic in pgstat_report_stat() to time out
+-- and send a message
+SELECT pg_sleep(1.0);
+SELECT * FROM pg_stat_sql where tag='SELECT';
+
+SET track_sql TO ON;
+
+-- force the rate-limiting logic in pgstat_report_stat() to time out
+-- and send a message
+SELECT pg_sleep(1.0);
+SELECT * FROM pg_stat_sql where tag='SELECT';
+
+SET track_sql TO OFF;
+SELECT pg_stat_reset_shared('sqlstmt'); -- reset the counters
+
+-- force the rate-limiting logic in pgstat_report_stat() to time out
+-- and send a message
+SELECT pg_sleep(1.0);
+SELECT * FROM pg_stat_sql where tag='SELECT';
+
 -- conditio sine qua non
 SHOW track_counts;  -- must be on
 
