Add pg_dissect_walfile_name()
authorMichael Paquier <[email protected]>
Tue, 20 Dec 2022 04:36:27 +0000 (13:36 +0900)
committerMichael Paquier <[email protected]>
Tue, 20 Dec 2022 04:36:27 +0000 (13:36 +0900)
This function takes in input a WAL segment name and returns a tuple made
of the segment sequence number (dependent on the WAL segment size of the
cluster) and its timeline, as of a thin SQL wrapper around the existing
XLogFromFileName().

This function has multiple usages, like being able to compile a LSN from
a file name and an offset, or finding the timeline of a segment without
having to do to some maths based on the first eight characters of the
segment.

Bump catalog version.

Author: Bharath Rupireddy
Reviewed-by: Nathan Bossart, Kyotaro Horiguchi, Maxim Orlov, Michael
Paquier
Discussion: https://postgr.es/m/CALj2ACWV=FCddsxcGbVOA=cvPyMr75YCFbSQT6g4KDj=gcJK4g@mail.gmail.com

doc/src/sgml/func.sgml
src/backend/access/transam/xlogfuncs.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 1cd8b11334db3e19a8ac9bad199a066bbb047814..1f63dc6dbac1d851d7fbb53870322ba1e18d375f 100644 (file)
@@ -26098,6 +26098,22 @@ LOG:  Grand total: 1651920 bytes in 201 blocks; 622360 free (88 chunks); 1029560
        </para></entry>
       </row>
 
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_dissect_walfile_name</primary>
+        </indexterm>
+        <function>pg_dissect_walfile_name</function> ( <parameter>file_name</parameter> <type>text</type> )
+        <returnvalue>record</returnvalue>
+        ( <parameter>segno</parameter> <type>numeric</type>,
+        <parameter>timeline_id</parameter> <type>bigint</type> )
+       </para>
+       <para>
+        Extracts the file sequence number and timeline ID from a WAL file
+        name.
+       </para></entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
@@ -26155,6 +26171,23 @@ postgres=# SELECT * FROM pg_walfile_name_offset((pg_backup_stop()).lsn);
     needs to be archived.
    </para>
 
+   <para>
+    <function>pg_dissect_walfile_name</function> is useful to compute a
+    <acronym>LSN</acronym> from a file offset and WAL file name, for example:
+<programlisting>
+postgres=# \set file_name '000000010000000100C000AB'
+postgres=# \set offset 256
+postgres=# SELECT '0/0'::pg_lsn + pd.segno * ps.setting::int + :offset AS lsn
+  FROM pg_dissect_walfile_name(:'file_name') pd,
+       pg_show_all_settings() ps
+  WHERE ps.name = 'wal_segment_size';
+      lsn
+---------------
+ C001/AB000100
+(1 row)
+</programlisting>
+   </para>
+
   </sect2>
 
   <sect2 id="functions-recovery-control">
index 487d5d9cac7f529e66819ecb218fbc213f35e00e..0a31837ef18fa0882da1ff90e15b368c8ae5ab4c 100644 (file)
@@ -432,6 +432,59 @@ pg_walfile_name(PG_FUNCTION_ARGS)
        PG_RETURN_TEXT_P(cstring_to_text(xlogfilename));
 }
 
+/*
+ * Extract the sequence number and the timeline ID from given a WAL file
+ * name.
+ */
+Datum
+pg_dissect_walfile_name(PG_FUNCTION_ARGS)
+{
+#define PG_DISSECT_WALFILE_NAME_COLS 2
+       char       *fname = text_to_cstring(PG_GETARG_TEXT_PP(0));
+       char       *fname_upper;
+       char       *p;
+       TimeLineID      tli;
+       XLogSegNo       segno;
+       Datum           values[PG_DISSECT_WALFILE_NAME_COLS] = {0};
+       bool            isnull[PG_DISSECT_WALFILE_NAME_COLS] = {0};
+       TupleDesc       tupdesc;
+       HeapTuple       tuple;
+       char            buf[256];
+       Datum           result;
+
+       fname_upper = pstrdup(fname);
+
+       /* Capitalize WAL file name. */
+       for (p = fname_upper; *p; p++)
+               *p = pg_toupper((unsigned char) *p);
+
+       if (!IsXLogFileName(fname_upper))
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                errmsg("invalid WAL file name \"%s\"", fname)));
+
+       XLogFromFileName(fname_upper, &tli, &segno, wal_segment_size);
+
+       if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+               elog(ERROR, "return type must be a row type");
+
+       /* Convert to numeric. */
+       snprintf(buf, sizeof buf, UINT64_FORMAT, segno);
+       values[0] = DirectFunctionCall3(numeric_in,
+                                                                       CStringGetDatum(buf),
+                                                                       ObjectIdGetDatum(0),
+                                                                       Int32GetDatum(-1));
+
+       values[1] = Int64GetDatum(tli);
+
+       tuple = heap_form_tuple(tupdesc, values, isnull);
+       result = HeapTupleGetDatum(tuple);
+
+       PG_RETURN_DATUM(result);
+
+#undef PG_DISSECT_WALFILE_NAME_COLS
+}
+
 /*
  * pg_wal_replay_pause - Request to pause recovery
  *
index 094f59f82db94d29b834f56d725f61330c306c5f..091ad94c5eb1a8194e0cf0973babf094eda8a3cf 100644 (file)
@@ -57,6 +57,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     202212191
+#define CATALOG_VERSION_NO     202212201
 
 #endif
index d763419c0de5e287c53ea31a061b933c6680d248..98d90d9338931a77310f357da3da4dae7e283689 100644 (file)
 { oid => '2851', descr => 'wal filename, given a wal location',
   proname => 'pg_walfile_name', prorettype => 'text', proargtypes => 'pg_lsn',
   prosrc => 'pg_walfile_name' },
+{ oid => '8205',
+  descr => 'sequence number and timeline ID given a wal filename',
+  proname => 'pg_dissect_walfile_name', provolatile => 's',
+  prorettype => 'record', proargtypes => 'text',
+  proallargtypes => '{text,numeric,int8}', proargmodes => '{i,o,o}',
+  proargnames => '{file_name,segno,timeline_id}',
+  prosrc => 'pg_dissect_walfile_name' },
 
 { oid => '3165', descr => 'difference in bytes, given two wal locations',
   proname => 'pg_wal_lsn_diff', prorettype => 'numeric',
index 88bb696ded813a2209716e315e3ab28694ec3bd6..2907f779a75090fe38ebe4d6ddb4e03fb00a5962 100644 (file)
@@ -619,3 +619,26 @@ SELECT count(*) > 0 AS ok FROM pg_control_system();
  t
 (1 row)
 
+-- pg_dissect_walfile_name
+SELECT * FROM pg_dissect_walfile_name(NULL);
+ segno | timeline_id 
+-------+-------------
+       |            
+(1 row)
+
+SELECT * FROM pg_dissect_walfile_name('invalid');
+ERROR:  invalid WAL file name "invalid"
+SELECT segno > 0 AS ok_segno, timeline_id
+  FROM pg_dissect_walfile_name('000000010000000100000000');
+ ok_segno | timeline_id 
+----------+-------------
+ t        |           1
+(1 row)
+
+SELECT segno > 0 AS ok_segno, timeline_id
+  FROM pg_dissect_walfile_name('ffffffFF00000001000000af');
+ ok_segno | timeline_id 
+----------+-------------
+ t        |  4294967295
+(1 row)
+
index b07e9e8dbb3c5d943fc5996f28f8436213139dbf..0c3d75fd1ac5b49eeadc5be6f0716d34e5cc3be1 100644 (file)
@@ -229,3 +229,11 @@ SELECT count(*) > 0 AS ok FROM pg_control_checkpoint();
 SELECT count(*) > 0 AS ok FROM pg_control_init();
 SELECT count(*) > 0 AS ok FROM pg_control_recovery();
 SELECT count(*) > 0 AS ok FROM pg_control_system();
+
+-- pg_dissect_walfile_name
+SELECT * FROM pg_dissect_walfile_name(NULL);
+SELECT * FROM pg_dissect_walfile_name('invalid');
+SELECT segno > 0 AS ok_segno, timeline_id
+  FROM pg_dissect_walfile_name('000000010000000100000000');
+SELECT segno > 0 AS ok_segno, timeline_id
+  FROM pg_dissect_walfile_name('ffffffFF00000001000000af');