Add SQL functions to monitor the directory contents of replication slots
authorMichael Paquier <michael@paquier.xyz>
Tue, 23 Nov 2021 10:29:42 +0000 (19:29 +0900)
committerMichael Paquier <michael@paquier.xyz>
Tue, 23 Nov 2021 10:29:42 +0000 (19:29 +0900)
This commit adds a set of functions able to look at the contents of
various paths related to replication slots:
- pg_ls_logicalsnapdir, for pg_logical/snapshots/
- pg_ls_logicalmapdir, for pg_logical/mappings/
- pg_ls_replslotdir, for pg_replslot/<slot_name>/

These are intended to be used by monitoring tools.  Unlike pg_ls_dir(),
execution permission can be granted to non-superusers.  Roles members of
pg_monitor gain have access to those functions.

Bump catalog version.

Author: Bharath Rupireddy
Reviewed-by: Nathan Bossart, Justin Pryzby
Discussion: https://postgr.es/m/CALj2ACWsfizZjMN6bzzdxOk1ADQQeSw8HhEjhmVXn_Pu+7VzLw@mail.gmail.com

contrib/test_decoding/expected/slot.out
contrib/test_decoding/sql/slot.sql
doc/src/sgml/func.sgml
src/backend/catalog/system_functions.sql
src/backend/utils/adt/genfile.c
src/include/catalog/catversion.h
src/include/catalog/pg_proc.dat
src/test/regress/expected/misc_functions.out
src/test/regress/sql/misc_functions.sql

index 75b4b5cc62576d8a953e4b6a5ce1df3c5dcda1bf..63a9940f73abe4935ac333ff16746e4d0ab536d4 100644 (file)
@@ -48,6 +48,27 @@ SELECT pg_drop_replication_slot('regression_slot_t');
 ERROR:  replication slot "regression_slot_t" does not exist
 SELECT pg_drop_replication_slot('regression_slot_t2');
 ERROR:  replication slot "regression_slot_t2" does not exist
+-- monitoring functions for slot directories
+SELECT count(*) >= 0 AS ok FROM pg_ls_logicalmapdir();
+ ok 
+----
+ t
+(1 row)
+
+SELECT count(*) >= 0 AS ok FROM pg_ls_logicalsnapdir();
+ ok 
+----
+ t
+(1 row)
+
+SELECT count(*) >= 0 AS ok FROM pg_ls_replslotdir('regression_slot_p');
+ ok 
+----
+ t
+(1 row)
+
+SELECT count(*) >= 0 AS ok FROM pg_ls_replslotdir('not_existing_slot'); -- fails
+ERROR:  replication slot "not_existing_slot" does not exist
 -- permanent slot has survived
 SELECT pg_drop_replication_slot('regression_slot_p');
  pg_drop_replication_slot 
index 6d83fb26782f362049934fc39fd408c35067625b..1aa27c56674b78dfcb456e89519febf340bb94b1 100644 (file)
@@ -28,6 +28,12 @@ end';
 SELECT pg_drop_replication_slot('regression_slot_t');
 SELECT pg_drop_replication_slot('regression_slot_t2');
 
+-- monitoring functions for slot directories
+SELECT count(*) >= 0 AS ok FROM pg_ls_logicalmapdir();
+SELECT count(*) >= 0 AS ok FROM pg_ls_logicalsnapdir();
+SELECT count(*) >= 0 AS ok FROM pg_ls_replslotdir('regression_slot_p');
+SELECT count(*) >= 0 AS ok FROM pg_ls_replslotdir('not_existing_slot'); -- fails
+
 -- permanent slot has survived
 SELECT pg_drop_replication_slot('regression_slot_p');
 
index 24447c00177332a08a80ee76b1a7a7ad68e2902e..74d3087a7234dc59f33787ef5086f2b0e2e057df 100644 (file)
@@ -27417,6 +27417,79 @@ SELECT convert_from(pg_read_binary_file('file_in_utf8.txt'), 'UTF8');
         can be granted EXECUTE to run the function.
        </para></entry>
       </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_ls_logicalsnapdir</primary>
+        </indexterm>
+        <function>pg_ls_logicalsnapdir</function> ()
+        <returnvalue>setof record</returnvalue>
+        ( <parameter>name</parameter> <type>text</type>,
+        <parameter>size</parameter> <type>bigint</type>,
+        <parameter>modification</parameter> <type>timestamp with time zone</type> )
+       </para>
+       <para>
+        Returns the name, size, and last modification time (mtime) of each
+        ordinary file in the server's <filename>pg_logical/snapshots</filename>
+        directory. Filenames beginning with a dot, directories, and other
+        special files are excluded.
+       </para>
+       <para>
+        This function is restricted to superusers and members of
+        the <literal>pg_monitor</literal> role by default, but other users can
+        be granted EXECUTE to run the function.
+       </para></entry>
+      </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_ls_logicalmapdir</primary>
+        </indexterm>
+        <function>pg_ls_logicalmapdir</function> ()
+        <returnvalue>setof record</returnvalue>
+        ( <parameter>name</parameter> <type>text</type>,
+        <parameter>size</parameter> <type>bigint</type>,
+        <parameter>modification</parameter> <type>timestamp with time zone</type> )
+       </para>
+       <para>
+        Returns the name, size, and last modification time (mtime) of each
+        ordinary file in the server's <filename>pg_logical/mappings</filename>
+        directory. Filenames beginning with a dot, directories, and other
+        special files are excluded.
+       </para>
+       <para>
+        This function is restricted to superusers and members of
+        the <literal>pg_monitor</literal> role by default, but other users can
+        be granted EXECUTE to run the function.
+       </para></entry>
+      </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_ls_replslotdir</primary>
+        </indexterm>
+        <function>pg_ls_replslotdir</function> ( <parameter>slot_name</parameter> <type>text</type> )
+        <returnvalue>setof record</returnvalue>
+        ( <parameter>name</parameter> <type>text</type>,
+        <parameter>size</parameter> <type>bigint</type>,
+        <parameter>modification</parameter> <type>timestamp with time zone</type> )
+       </para>
+       <para>
+        Returns the name, size, and last modification time (mtime) of each
+        ordinary file in the server's <filename>pg_replslot/slot_name</filename>
+        directory, where <parameter>slot_name</parameter> is the name of the
+        replication slot provided as input of the function. Filenames beginning
+        with a dot, directories, and other special files are excluded.
+       </para>
+       <para>
+        This function is restricted to superusers and members of
+        the <literal>pg_monitor</literal> role by default, but other users can
+        be granted EXECUTE to run the function.
+       </para></entry>
+      </row>
      </tbody>
     </tgroup>
    </table>
index 54c93b16c4c195fbcb630bd58bfb275533699ed1..f6789025a5f4961be686df5aee72655d2b01e75f 100644 (file)
@@ -701,6 +701,12 @@ REVOKE EXECUTE ON FUNCTION pg_ls_dir(text,boolean,boolean) FROM public;
 
 REVOKE EXECUTE ON FUNCTION pg_log_backend_memory_contexts(integer) FROM PUBLIC;
 
+REVOKE EXECUTE ON FUNCTION pg_ls_logicalsnapdir() FROM PUBLIC;
+
+REVOKE EXECUTE ON FUNCTION pg_ls_logicalmapdir() FROM PUBLIC;
+
+REVOKE EXECUTE ON FUNCTION pg_ls_replslotdir(text) FROM PUBLIC;
+
 --
 -- We also set up some things as accessible to standard roles.
 --
@@ -715,6 +721,12 @@ GRANT EXECUTE ON FUNCTION pg_ls_tmpdir() TO pg_monitor;
 
 GRANT EXECUTE ON FUNCTION pg_ls_tmpdir(oid) TO pg_monitor;
 
+GRANT EXECUTE ON FUNCTION pg_ls_logicalsnapdir() TO pg_monitor;
+
+GRANT EXECUTE ON FUNCTION pg_ls_logicalmapdir() TO pg_monitor;
+
+GRANT EXECUTE ON FUNCTION pg_ls_replslotdir(text) TO pg_monitor;
+
 GRANT pg_read_all_settings TO pg_monitor;
 
 GRANT pg_read_all_stats TO pg_monitor;
index c436d9318b674049fb7cb059aa1795d0c18423dd..027ed8640014211c3c9ce2b651f6e9736646d244 100644 (file)
@@ -29,6 +29,7 @@
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
 #include "postmaster/syslogger.h"
+#include "replication/slot.h"
 #include "storage/fd.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
@@ -720,3 +721,46 @@ pg_ls_archive_statusdir(PG_FUNCTION_ARGS)
 {
    return pg_ls_dir_files(fcinfo, XLOGDIR "/archive_status", true);
 }
+
+/*
+ * Function to return the list of files in the pg_logical/snapshots directory.
+ */
+Datum
+pg_ls_logicalsnapdir(PG_FUNCTION_ARGS)
+{
+   return pg_ls_dir_files(fcinfo, "pg_logical/snapshots", false);
+}
+
+/*
+ * Function to return the list of files in the pg_logical/mappings directory.
+ */
+Datum
+pg_ls_logicalmapdir(PG_FUNCTION_ARGS)
+{
+   return pg_ls_dir_files(fcinfo, "pg_logical/mappings", false);
+}
+
+/*
+ * Function to return the list of files in the pg_replslot/<replication_slot>
+ * directory.
+ */
+Datum
+pg_ls_replslotdir(PG_FUNCTION_ARGS)
+{
+   text       *slotname_t;
+   char        path[MAXPGPATH];
+   char       *slotname;
+
+   slotname_t = PG_GETARG_TEXT_PP(0);
+
+   slotname = text_to_cstring(slotname_t);
+
+   if (!SearchNamedReplicationSlot(slotname, true))
+       ereport(ERROR,
+               (errcode(ERRCODE_UNDEFINED_OBJECT),
+                errmsg("replication slot \"%s\" does not exist",
+                       slotname)));
+
+   snprintf(path, sizeof(path), "pg_replslot/%s", slotname);
+   return pg_ls_dir_files(fcinfo, path, false);
+}
index cb7117df3ea2e3c5316d7763d12356391d1eb6b7..920390b8b2acb2cd6d0c8be577d2ddf07d2d4667 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 202111171
+#define CATALOG_VERSION_NO 202111231
 
 #endif
index 6412f369f18f68b9029ccfb424173c334420f6d5..e934361dc32068233d3957c8dc7ecdaf4da85e97 100644 (file)
   proallargtypes => '{oid,text,int8,timestamptz}', proargmodes => '{i,o,o,o}',
   proargnames => '{tablespace,name,size,modification}',
   prosrc => 'pg_ls_tmpdir_1arg' },
+{ oid => '9858',
+  descr => 'list of files in the pg_logical/snapshots directory',
+  proname => 'pg_ls_logicalsnapdir', procost => '10', prorows => '20',
+  proretset => 't', provolatile => 'v', prorettype => 'record',
+  proargtypes => '', proallargtypes => '{text,int8,timestamptz}',
+  proargmodes => '{o,o,o}', proargnames => '{name,size,modification}',
+  prosrc => 'pg_ls_logicalsnapdir' },
+{ oid => '9859',
+  descr => 'list of files in the pg_logical/mappings directory',
+  proname => 'pg_ls_logicalmapdir', procost => '10', prorows => '20',
+  proretset => 't', provolatile => 'v', prorettype => 'record',
+  proargtypes => '', proallargtypes => '{text,int8,timestamptz}',
+  proargmodes => '{o,o,o}', proargnames => '{name,size,modification}',
+  prosrc => 'pg_ls_logicalmapdir' },
+{ oid => '9860',
+  descr => 'list of files in the pg_replslot/slot_name directory',
+  proname => 'pg_ls_replslotdir', procost => '10', prorows => '20',
+  proretset => 't', provolatile => 'v', prorettype => 'record',
+  proargtypes => 'text', proallargtypes => '{text,text,int8,timestamptz}',
+  proargmodes => '{i,o,o,o}',
+  proargnames => '{slot_name,name,size,modification}',
+  prosrc => 'pg_ls_replslotdir' },
 
 # hash partitioning constraint function
 { oid => '5028', descr => 'hash partition CHECK constraint',
index 71d316cad3e472a281db527bb373e9dc9bf3c9d4..1013d17f87d5327abd0ffdb8c372ecd91569f647 100644 (file)
@@ -243,6 +243,56 @@ select count(*) > 0 from
  t
 (1 row)
 
+--
+-- Test replication slot directory functions
+--
+CREATE ROLE regress_slot_dir_funcs;
+-- Not available by default.
+SELECT has_function_privilege('regress_slot_dir_funcs',
+  'pg_ls_logicalsnapdir()', 'EXECUTE');
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+SELECT has_function_privilege('regress_slot_dir_funcs',
+  'pg_ls_logicalmapdir()', 'EXECUTE');
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+SELECT has_function_privilege('regress_slot_dir_funcs',
+  'pg_ls_replslotdir(text)', 'EXECUTE');
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+GRANT pg_monitor TO regress_slot_dir_funcs;
+-- Role is now part of pg_monitor, so these are available.
+SELECT has_function_privilege('regress_slot_dir_funcs',
+  'pg_ls_logicalsnapdir()', 'EXECUTE');
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+SELECT has_function_privilege('regress_slot_dir_funcs',
+  'pg_ls_logicalmapdir()', 'EXECUTE');
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+SELECT has_function_privilege('regress_slot_dir_funcs',
+  'pg_ls_replslotdir(text)', 'EXECUTE');
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+DROP ROLE regress_slot_dir_funcs;
 --
 -- Test adding a support function to a subject function
 --
index 8c23874b3f5dfe1d486f1bb5536aeee0f2837f1f..7ab9b2a1509b0dde681146efb69953f29267cf4c 100644 (file)
@@ -91,6 +91,27 @@ select count(*) > 0 from
    where spcname = 'pg_default') pts
   join pg_database db on pts.pts = db.oid;
 
+--
+-- Test replication slot directory functions
+--
+CREATE ROLE regress_slot_dir_funcs;
+-- Not available by default.
+SELECT has_function_privilege('regress_slot_dir_funcs',
+  'pg_ls_logicalsnapdir()', 'EXECUTE');
+SELECT has_function_privilege('regress_slot_dir_funcs',
+  'pg_ls_logicalmapdir()', 'EXECUTE');
+SELECT has_function_privilege('regress_slot_dir_funcs',
+  'pg_ls_replslotdir(text)', 'EXECUTE');
+GRANT pg_monitor TO regress_slot_dir_funcs;
+-- Role is now part of pg_monitor, so these are available.
+SELECT has_function_privilege('regress_slot_dir_funcs',
+  'pg_ls_logicalsnapdir()', 'EXECUTE');
+SELECT has_function_privilege('regress_slot_dir_funcs',
+  'pg_ls_logicalmapdir()', 'EXECUTE');
+SELECT has_function_privilege('regress_slot_dir_funcs',
+  'pg_ls_replslotdir(text)', 'EXECUTE');
+DROP ROLE regress_slot_dir_funcs;
+
 --
 -- Test adding a support function to a subject function
 --