LCOV - code coverage report
Current view: top level - contrib/pg_logicalinspect - pg_logicalinspect.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 67 80 83.8 %
Date: 2025-04-01 15:15:16 Functions: 7 7 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * pg_logicalinspect.c
       4             :  *        Functions to inspect contents of PostgreSQL logical snapshots
       5             :  *
       6             :  * Copyright (c) 2024-2025, PostgreSQL Global Development Group
       7             :  *
       8             :  * IDENTIFICATION
       9             :  *        contrib/pg_logicalinspect/pg_logicalinspect.c
      10             :  *
      11             :  *-------------------------------------------------------------------------
      12             :  */
      13             : #include "postgres.h"
      14             : 
      15             : #include "funcapi.h"
      16             : #include "replication/snapbuild_internal.h"
      17             : #include "utils/array.h"
      18             : #include "utils/builtins.h"
      19             : #include "utils/pg_lsn.h"
      20             : 
      21           6 : PG_MODULE_MAGIC_EXT(
      22             :                     .name = "pg_logicalinspect",
      23             :                     .version = PG_VERSION
      24             : );
      25             : 
      26           8 : PG_FUNCTION_INFO_V1(pg_get_logical_snapshot_meta);
      27           8 : PG_FUNCTION_INFO_V1(pg_get_logical_snapshot_info);
      28             : 
      29             : /* Return the description of SnapBuildState */
      30             : static const char *
      31           2 : get_snapbuild_state_desc(SnapBuildState state)
      32             : {
      33           2 :     const char *stateDesc = "unknown state";
      34             : 
      35           2 :     switch (state)
      36             :     {
      37           0 :         case SNAPBUILD_START:
      38           0 :             stateDesc = "start";
      39           0 :             break;
      40           0 :         case SNAPBUILD_BUILDING_SNAPSHOT:
      41           0 :             stateDesc = "building";
      42           0 :             break;
      43           0 :         case SNAPBUILD_FULL_SNAPSHOT:
      44           0 :             stateDesc = "full";
      45           0 :             break;
      46           2 :         case SNAPBUILD_CONSISTENT:
      47           2 :             stateDesc = "consistent";
      48           2 :             break;
      49             :     }
      50             : 
      51           2 :     return stateDesc;
      52             : }
      53             : 
      54             : /*
      55             :  * Extract the LSN from the given serialized snapshot file name.
      56             :  */
      57             : static XLogRecPtr
      58          30 : parse_snapshot_filename(const char *filename)
      59             : {
      60             :     uint32      hi;
      61             :     uint32      lo;
      62             :     XLogRecPtr  lsn;
      63             :     char        tmpfname[MAXPGPATH];
      64             : 
      65             :     /*
      66             :      * Extract the values to build the LSN.
      67             :      *
      68             :      * Note: Including ".snap" doesn't mean that sscanf() also does the format
      69             :      * check including the suffix. The subsequent check validates if the given
      70             :      * filename has the expected suffix.
      71             :      */
      72          30 :     if (sscanf(filename, "%X-%X.snap", &hi, &lo) != 2)
      73          16 :         goto parse_error;
      74             : 
      75             :     /*
      76             :      * Bring back the extracted LSN to the snapshot file format and compare it
      77             :      * to the given filename. This check strictly checks if the given filename
      78             :      * follows the format of the snapshot filename.
      79             :      */
      80          14 :     sprintf(tmpfname, "%X-%X.snap", hi, lo);
      81          14 :     if (strcmp(tmpfname, filename) != 0)
      82          10 :         goto parse_error;
      83             : 
      84           4 :     lsn = ((uint64) hi) << 32 | lo;
      85             : 
      86           4 :     return lsn;
      87             : 
      88          26 : parse_error:
      89          26 :     ereport(ERROR,
      90             :             errmsg("invalid snapshot file name \"%s\"", filename));
      91             : 
      92             :     return InvalidXLogRecPtr;   /* keep compiler quiet */
      93             : }
      94             : 
      95             : /*
      96             :  * Retrieve the logical snapshot file metadata.
      97             :  */
      98             : Datum
      99          12 : pg_get_logical_snapshot_meta(PG_FUNCTION_ARGS)
     100             : {
     101             : #define PG_GET_LOGICAL_SNAPSHOT_META_COLS 3
     102             :     SnapBuildOnDisk ondisk;
     103             :     HeapTuple   tuple;
     104          12 :     Datum       values[PG_GET_LOGICAL_SNAPSHOT_META_COLS] = {0};
     105          12 :     bool        nulls[PG_GET_LOGICAL_SNAPSHOT_META_COLS] = {0};
     106             :     TupleDesc   tupdesc;
     107             :     XLogRecPtr  lsn;
     108          12 :     int         i = 0;
     109          12 :     text       *filename_t = PG_GETARG_TEXT_PP(0);
     110             : 
     111             :     /* Build a tuple descriptor for our result type */
     112          12 :     if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
     113           0 :         elog(ERROR, "return type must be a row type");
     114             : 
     115          12 :     lsn = parse_snapshot_filename(text_to_cstring(filename_t));
     116             : 
     117             :     /* Validate and restore the snapshot to 'ondisk' */
     118           2 :     SnapBuildRestoreSnapshot(&ondisk, lsn, CurrentMemoryContext, false);
     119             : 
     120           2 :     values[i++] = UInt32GetDatum(ondisk.magic);
     121           2 :     values[i++] = Int64GetDatum((int64) ondisk.checksum);
     122           2 :     values[i++] = UInt32GetDatum(ondisk.version);
     123             : 
     124             :     Assert(i == PG_GET_LOGICAL_SNAPSHOT_META_COLS);
     125             : 
     126           2 :     tuple = heap_form_tuple(tupdesc, values, nulls);
     127             : 
     128           2 :     PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
     129             : 
     130             : #undef PG_GET_LOGICAL_SNAPSHOT_META_COLS
     131             : }
     132             : 
     133             : Datum
     134          18 : pg_get_logical_snapshot_info(PG_FUNCTION_ARGS)
     135             : {
     136             : #define PG_GET_LOGICAL_SNAPSHOT_INFO_COLS 14
     137             :     SnapBuildOnDisk ondisk;
     138             :     HeapTuple   tuple;
     139          18 :     Datum       values[PG_GET_LOGICAL_SNAPSHOT_INFO_COLS] = {0};
     140          18 :     bool        nulls[PG_GET_LOGICAL_SNAPSHOT_INFO_COLS] = {0};
     141             :     TupleDesc   tupdesc;
     142             :     XLogRecPtr  lsn;
     143          18 :     int         i = 0;
     144          18 :     text       *filename_t = PG_GETARG_TEXT_PP(0);
     145             : 
     146             :     /* Build a tuple descriptor for our result type */
     147          18 :     if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
     148           0 :         elog(ERROR, "return type must be a row type");
     149             : 
     150          18 :     lsn = parse_snapshot_filename(text_to_cstring(filename_t));
     151             : 
     152             :     /* Validate and restore the snapshot to 'ondisk' */
     153           2 :     SnapBuildRestoreSnapshot(&ondisk, lsn, CurrentMemoryContext, false);
     154             : 
     155           2 :     values[i++] = CStringGetTextDatum(get_snapbuild_state_desc(ondisk.builder.state));
     156           2 :     values[i++] = TransactionIdGetDatum(ondisk.builder.xmin);
     157           2 :     values[i++] = TransactionIdGetDatum(ondisk.builder.xmax);
     158           2 :     values[i++] = LSNGetDatum(ondisk.builder.start_decoding_at);
     159           2 :     values[i++] = LSNGetDatum(ondisk.builder.two_phase_at);
     160           2 :     values[i++] = TransactionIdGetDatum(ondisk.builder.initial_xmin_horizon);
     161           2 :     values[i++] = BoolGetDatum(ondisk.builder.building_full_snapshot);
     162           2 :     values[i++] = BoolGetDatum(ondisk.builder.in_slot_creation);
     163           2 :     values[i++] = LSNGetDatum(ondisk.builder.last_serialized_snapshot);
     164           2 :     values[i++] = TransactionIdGetDatum(ondisk.builder.next_phase_at);
     165             : 
     166           2 :     values[i++] = UInt32GetDatum(ondisk.builder.committed.xcnt);
     167           2 :     if (ondisk.builder.committed.xcnt > 0)
     168             :     {
     169             :         Datum      *arrayelems;
     170             : 
     171           2 :         arrayelems = (Datum *) palloc(ondisk.builder.committed.xcnt * sizeof(Datum));
     172             : 
     173           4 :         for (int j = 0; j < ondisk.builder.committed.xcnt; j++)
     174           2 :             arrayelems[j] = TransactionIdGetDatum(ondisk.builder.committed.xip[j]);
     175             : 
     176           2 :         values[i++] = PointerGetDatum(construct_array_builtin(arrayelems,
     177           2 :                                                               ondisk.builder.committed.xcnt,
     178             :                                                               XIDOID));
     179             :     }
     180             :     else
     181           0 :         nulls[i++] = true;
     182             : 
     183           2 :     values[i++] = UInt32GetDatum(ondisk.builder.catchange.xcnt);
     184           2 :     if (ondisk.builder.catchange.xcnt > 0)
     185             :     {
     186             :         Datum      *arrayelems;
     187             : 
     188           2 :         arrayelems = (Datum *) palloc(ondisk.builder.catchange.xcnt * sizeof(Datum));
     189             : 
     190           6 :         for (int j = 0; j < ondisk.builder.catchange.xcnt; j++)
     191           4 :             arrayelems[j] = TransactionIdGetDatum(ondisk.builder.catchange.xip[j]);
     192             : 
     193           2 :         values[i++] = PointerGetDatum(construct_array_builtin(arrayelems,
     194           2 :                                                               ondisk.builder.catchange.xcnt,
     195             :                                                               XIDOID));
     196             :     }
     197             :     else
     198           0 :         nulls[i++] = true;
     199             : 
     200             :     Assert(i == PG_GET_LOGICAL_SNAPSHOT_INFO_COLS);
     201             : 
     202           2 :     tuple = heap_form_tuple(tupdesc, values, nulls);
     203             : 
     204           2 :     PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
     205             : 
     206             : #undef PG_GET_LOGICAL_SNAPSHOT_INFO_COLS
     207             : }

Generated by: LCOV version 1.14