From 8a955a031598864e1acbe88c6bac8417319d0fb7 Mon Sep 17 00:00:00 2001
From: Shveta Malik <shveta.malik@gmail.com>
Date: Wed, 20 Dec 2023 10:54:53 +0530
Subject: [PATCH v1] Function to get invalidation cause of a replication slot.

This patch implements a new system function
pg_get_slot_invalidation_cause('slot_name')
to get invalidation cause of a replication slot.

Currently, users do not have a way to know the invalidation cause.
One can only find out if the slot is invalidated or not by querying
the 'conflicting' field of pg_replication_slots. This new function
provides a way to query invalidation cause as well.

This function returns NULL if the replication slot is not found and 0 if
the replication slot is not invalidated. Possible invalidation causes
returned are:
    1 = required WAL has been removed.
    2 = required rows have been removed.
    3 = wal_level insufficient for the slot
---
 doc/src/sgml/func.sgml              | 33 +++++++++++++++++++++++++++++
 src/backend/replication/slotfuncs.c | 27 +++++++++++++++++++++++
 src/include/catalog/pg_proc.dat     |  4 ++++
 3 files changed, 64 insertions(+)

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 20da3ed033..0826168192 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -27684,6 +27684,39 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
        </para></entry>
       </row>
 
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_get_slot_invalidation_cause</primary>
+        </indexterm>
+        <function>pg_get_slot_invalidation_cause</function> ( <parameter>slot_name</parameter> <type>name</type> )
+        <returnvalue>integer</returnvalue>
+       </para>
+       <para>
+        Returns invalidation cause of the replication slot named
+        <parameter>slot_name</parameter>. Returns NULL if the replication slot
+        is not found and 0 if the replication slot is not invalidated.
+        Possible invalidation causes are:
+        <itemizedlist spacing="compact">
+         <listitem>
+          <para>
+           <literal>1</literal> = required WAL has been removed.
+          </para>
+         </listitem>
+         <listitem>
+          <para>
+           <literal>2</literal> = required rows have been removed.
+          </para>
+         </listitem>
+         <listitem>
+          <para>
+           <literal>3</literal> = wal_level insufficient for the slot
+          </para>
+         </listitem>
+        </itemizedlist>
+       </para></entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
diff --git a/src/backend/replication/slotfuncs.c b/src/backend/replication/slotfuncs.c
index 4b694a03d0..a8643c621c 100644
--- a/src/backend/replication/slotfuncs.c
+++ b/src/backend/replication/slotfuncs.c
@@ -225,6 +225,33 @@ pg_drop_replication_slot(PG_FUNCTION_ARGS)
 	PG_RETURN_VOID();
 }
 
+/*
+ * SQL function for getting invalidation cause of a slot.
+ *
+ * Returns ReplicationSlotInvalidationCause enum value for valid slot_name;
+ * returns NULL if slot with given name is not found.
+ *
+ * Returns RS_INVAL_NONE if the given slot is not invalidated.
+ */
+Datum
+pg_get_slot_invalidation_cause(PG_FUNCTION_ARGS)
+{
+	Name		name = PG_GETARG_NAME(0);
+	ReplicationSlot *s;
+	ReplicationSlotInvalidationCause cause;
+
+	s = SearchNamedReplicationSlot(NameStr(*name), true);
+
+	if (s == NULL)
+		PG_RETURN_NULL();
+
+	SpinLockAcquire(&s->mutex);
+	cause = s->data.invalidated;
+	SpinLockRelease(&s->mutex);
+
+	PG_RETURN_INT16(cause);
+}
+
 /*
  * pg_get_replication_slots - SQL SRF showing all replication slots
  * that currently exist on the database cluster.
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 77e8b13764..ce001dacb9 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -11095,6 +11095,10 @@
   proname => 'pg_drop_replication_slot', provolatile => 'v', proparallel => 'u',
   prorettype => 'void', proargtypes => 'name',
   prosrc => 'pg_drop_replication_slot' },
+{ oid => '8484', descr => 'what caused the replication slot to become invalid',
+  proname => 'pg_get_slot_invalidation_cause', provolatile => 's', proisstrict => 't',
+  prorettype => 'int2', proargtypes => 'name',
+  prosrc => 'pg_get_slot_invalidation_cause' },
 { oid => '3781',
   descr => 'information about replication slots currently in use',
   proname => 'pg_get_replication_slots', prorows => '10', proisstrict => 'f',
-- 
2.34.1

