From 54bd07a9d54ac0e8e7df80bfae97d6117261f6b3 Mon Sep 17 00:00:00 2001
From: Shinya Kato <shinya11.kato@gmail.com>
Date: Thu, 11 Jun 2026 16:15:32 +0900
Subject: [PATCH v1 3/5] Count md5_password_auth in pg_stat_deprecated_features

---
 doc/src/sgml/monitoring.sgml                          | 11 +++++++++++
 src/backend/libpq/crypt.c                             |  5 +++++
 .../utils/activity/pgstat_deprecated_features.c       |  1 +
 src/include/catalog/pg_proc.dat                       |  2 +-
 src/include/pgstat.h                                  |  1 +
 src/test/authentication/t/001_password.pl             | 10 ++++++++++
 src/test/regress/expected/stats.out                   |  7 ++++---
 7 files changed, 33 insertions(+), 4 deletions(-)

diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 5195d160dc4..4e5d5cc7e84 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -3501,6 +3501,17 @@ description | Waiting for a newly initialized WAL file to reach durable storage
    The tracked deprecated features are:
 
    <variablelist>
+    <varlistentry>
+     <term><literal>md5_password_auth</literal></term>
+     <listitem>
+      <para>
+       Successful authentication against an MD5-encrypted password, via
+       either <literal>md5</literal> or <literal>password</literal>
+       authentication.
+      </para>
+     </listitem>
+    </varlistentry>
+
     <varlistentry>
      <term><literal>md5_password_set</literal></term>
      <listitem>
diff --git a/src/backend/libpq/crypt.c b/src/backend/libpq/crypt.c
index 345a9d43698..e2030db48b6 100644
--- a/src/backend/libpq/crypt.c
+++ b/src/backend/libpq/crypt.c
@@ -309,6 +309,8 @@ md5_crypt_verify(const char *role, const char *shadow_pass,
 
 		retval = STATUS_OK;
 
+		pgstat_count_deprecated_feature(PGSTAT_DEPRECATED_FEATURE_MD5_PASSWORD_AUTH);
+
 		oldcontext = MemoryContextSwitchTo(TopMemoryContext);
 
 		warning = pstrdup(_("authenticated with an MD5-encrypted password"));
@@ -385,7 +387,10 @@ plain_crypt_verify(const char *role, const char *shadow_pass,
 			}
 			if (strlen(crypt_client_pass) == strlen(shadow_pass) &&
 				timingsafe_bcmp(crypt_client_pass, shadow_pass, strlen(shadow_pass)) == 0)
+			{
+				pgstat_count_deprecated_feature(PGSTAT_DEPRECATED_FEATURE_MD5_PASSWORD_AUTH);
 				return STATUS_OK;
+			}
 			else
 			{
 				*logdetail = psprintf(_("Password does not match for user \"%s\"."),
diff --git a/src/backend/utils/activity/pgstat_deprecated_features.c b/src/backend/utils/activity/pgstat_deprecated_features.c
index 489fce29069..770ed195741 100644
--- a/src/backend/utils/activity/pgstat_deprecated_features.c
+++ b/src/backend/utils/activity/pgstat_deprecated_features.c
@@ -27,6 +27,7 @@
  * be kept in the same order, sorted by name.
  */
 static const char *const pgstat_deprecated_feature_names[] = {
+	[PGSTAT_DEPRECATED_FEATURE_MD5_PASSWORD_AUTH] = "md5_password_auth",
 	[PGSTAT_DEPRECATED_FEATURE_MD5_PASSWORD_SET] = "md5_password_set",
 };
 
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 37de65c65f6..ab4df79e1ef 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6070,7 +6070,7 @@
   prosrc => 'pg_stat_get_lock' },
 
 { oid => '8987', descr => 'statistics: deprecated feature usage',
-  proname => 'pg_stat_get_deprecated_features', prorows => '1',
+  proname => 'pg_stat_get_deprecated_features', prorows => '2',
   proretset => 't', provolatile => 'v', proparallel => 'r',
   prorettype => 'record', proargtypes => '',
   proallargtypes => '{text,int8,timestamptz,timestamptz}',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 241e7757492..8c483ea05f6 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -276,6 +276,7 @@ typedef struct PgStat_CheckpointerStats
 /* Deprecated features counted by pg_stat_deprecated_features */
 typedef enum PgStat_DeprecatedFeature
 {
+	PGSTAT_DEPRECATED_FEATURE_MD5_PASSWORD_AUTH,
 	PGSTAT_DEPRECATED_FEATURE_MD5_PASSWORD_SET,
 } PgStat_DeprecatedFeature;
 
diff --git a/src/test/authentication/t/001_password.pl b/src/test/authentication/t/001_password.pl
index fca78fc4d6a..11ae5e6b8d3 100644
--- a/src/test/authentication/t/001_password.pl
+++ b/src/test/authentication/t/001_password.pl
@@ -521,6 +521,16 @@ SKIP:
 		log_like => [
 			qr/connection authenticated: identity="md5_role_no_warnings" method=md5/
 		]);
+
+	# This session is authenticated with an MD5-encrypted password, so the
+	# md5_password_auth deprecated feature counter must be positive once
+	# this session's pending statistics have been flushed.
+	$res = $node->safe_psql(
+		'postgres',
+		"SELECT pg_stat_force_next_flush();
+SELECT usage_count >= 1 FROM pg_stat_deprecated_features WHERE name = 'md5_password_auth';",
+		connstr => "user=md5_role");
+	is($res, qq(\nt), 'md5_password_auth deprecated feature usage counted');
 }
 
 # require_auth succeeds with SCRAM required.
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 9f93a450a53..ecf5f210c59 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -1144,10 +1144,11 @@ SELECT stats_reset > :'checkpointer_reset_ts'::timestamptz FROM pg_stat_checkpoi
 
 -- The deprecated features view should list the expected features
 SELECT name FROM pg_stat_deprecated_features ORDER BY name COLLATE "C";
-       name       
-------------------
+       name        
+-------------------
+ md5_password_auth
  md5_password_set
-(1 row)
+(2 rows)
 
 -- Test that reset_shared with deprecated_features specified as the stats type works
 SELECT max(stats_reset) AS deprecated_features_reset_ts FROM pg_stat_deprecated_features \gset
-- 
2.47.3

