LCOV - code coverage report
Current view: top level - contrib/pg_logicalinspect - pg_logicalinspect.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 83.3 % 78 65
Test Date: 2026-07-03 19:57:34 Functions: 100.0 % 7 7
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 51.9 % 27 14

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * pg_logicalinspect.c
       4                 :             :  *        Functions to inspect contents of PostgreSQL logical snapshots
       5                 :             :  *
       6                 :             :  * Copyright (c) 2024-2026, 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                 :           3 : PG_MODULE_MAGIC_EXT(
      22                 :             :                     .name = "pg_logicalinspect",
      23                 :             :                     .version = PG_VERSION
      24                 :             : );
      25                 :             : 
      26                 :           4 : PG_FUNCTION_INFO_V1(pg_get_logical_snapshot_meta);
      27                 :           4 : PG_FUNCTION_INFO_V1(pg_get_logical_snapshot_info);
      28                 :             : 
      29                 :             : /* Return the description of SnapBuildState */
      30                 :             : static const char *
      31                 :           1 : get_snapbuild_state_desc(SnapBuildState state)
      32                 :             : {
      33                 :           1 :     const char *stateDesc = "unknown state";
      34                 :             : 
      35   [ -  -  -  +  :           1 :     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                 :           1 :         case SNAPBUILD_CONSISTENT:
      47                 :           1 :             stateDesc = "consistent";
      48                 :           1 :             break;
      49                 :             :     }
      50                 :             : 
      51                 :           1 :     return stateDesc;
      52                 :             : }
      53                 :             : 
      54                 :             : /*
      55                 :             :  * Extract the LSN from the given serialized snapshot file name.
      56                 :             :  */
      57                 :             : static XLogRecPtr
      58                 :          15 : 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         [ +  + ]:          15 :     if (sscanf(filename, "%X-%X.snap", &hi, &lo) != 2)
      73                 :           8 :         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                 :           7 :     sprintf(tmpfname, "%X-%X.snap", hi, lo);
      81         [ +  + ]:           7 :     if (strcmp(tmpfname, filename) != 0)
      82                 :           5 :         goto parse_error;
      83                 :             : 
      84                 :           2 :     lsn = ((uint64) hi) << 32 | lo;
      85                 :             : 
      86                 :           2 :     return lsn;
      87                 :             : 
      88                 :          13 : parse_error:
      89         [ +  - ]:          13 :     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                 :           6 : 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                 :           6 :     Datum       values[PG_GET_LOGICAL_SNAPSHOT_META_COLS] = {0};
     105                 :           6 :     bool        nulls[PG_GET_LOGICAL_SNAPSHOT_META_COLS] = {0};
     106                 :             :     TupleDesc   tupdesc;
     107                 :             :     XLogRecPtr  lsn;
     108                 :           6 :     int         i = 0;
     109                 :           6 :     text       *filename_t = PG_GETARG_TEXT_PP(0);
     110                 :             : 
     111                 :             :     /* Build a tuple descriptor for our result type */
     112         [ -  + ]:           6 :     if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
     113         [ #  # ]:           0 :         elog(ERROR, "return type must be a row type");
     114                 :             : 
     115                 :           6 :     lsn = parse_snapshot_filename(text_to_cstring(filename_t));
     116                 :             : 
     117                 :             :     /* Validate and restore the snapshot to 'ondisk' */
     118                 :           1 :     SnapBuildRestoreSnapshot(&ondisk, lsn, CurrentMemoryContext, false);
     119                 :             : 
     120                 :           1 :     values[i++] = UInt32GetDatum(ondisk.magic);
     121                 :           1 :     values[i++] = Int64GetDatum((int64) ondisk.checksum);
     122                 :           1 :     values[i++] = UInt32GetDatum(ondisk.version);
     123                 :             : 
     124                 :             :     Assert(i == PG_GET_LOGICAL_SNAPSHOT_META_COLS);
     125                 :             : 
     126                 :           1 :     tuple = heap_form_tuple(tupdesc, values, nulls);
     127                 :             : 
     128                 :           1 :     PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
     129                 :             : 
     130                 :             : #undef PG_GET_LOGICAL_SNAPSHOT_META_COLS
     131                 :             : }
     132                 :             : 
     133                 :             : Datum
     134                 :           9 : 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                 :           9 :     Datum       values[PG_GET_LOGICAL_SNAPSHOT_INFO_COLS] = {0};
     140                 :           9 :     bool        nulls[PG_GET_LOGICAL_SNAPSHOT_INFO_COLS] = {0};
     141                 :             :     TupleDesc   tupdesc;
     142                 :             :     XLogRecPtr  lsn;
     143                 :           9 :     int         i = 0;
     144                 :           9 :     text       *filename_t = PG_GETARG_TEXT_PP(0);
     145                 :             : 
     146                 :             :     /* Build a tuple descriptor for our result type */
     147         [ -  + ]:           9 :     if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
     148         [ #  # ]:           0 :         elog(ERROR, "return type must be a row type");
     149                 :             : 
     150                 :           9 :     lsn = parse_snapshot_filename(text_to_cstring(filename_t));
     151                 :             : 
     152                 :             :     /* Validate and restore the snapshot to 'ondisk' */
     153                 :           1 :     SnapBuildRestoreSnapshot(&ondisk, lsn, CurrentMemoryContext, false);
     154                 :             : 
     155                 :           1 :     values[i++] = CStringGetTextDatum(get_snapbuild_state_desc(ondisk.builder.state));
     156                 :           1 :     values[i++] = TransactionIdGetDatum(ondisk.builder.xmin);
     157                 :           1 :     values[i++] = TransactionIdGetDatum(ondisk.builder.xmax);
     158                 :           1 :     values[i++] = LSNGetDatum(ondisk.builder.start_decoding_at);
     159                 :           1 :     values[i++] = LSNGetDatum(ondisk.builder.two_phase_at);
     160                 :           1 :     values[i++] = TransactionIdGetDatum(ondisk.builder.initial_xmin_horizon);
     161                 :           1 :     values[i++] = BoolGetDatum(ondisk.builder.building_full_snapshot);
     162                 :           1 :     values[i++] = BoolGetDatum(ondisk.builder.in_slot_creation);
     163                 :           1 :     values[i++] = LSNGetDatum(ondisk.builder.last_serialized_snapshot);
     164                 :           1 :     values[i++] = TransactionIdGetDatum(ondisk.builder.next_phase_at);
     165                 :             : 
     166                 :           1 :     values[i++] = UInt32GetDatum(ondisk.builder.committed.xcnt);
     167         [ +  - ]:           1 :     if (ondisk.builder.committed.xcnt > 0)
     168                 :             :     {
     169                 :             :         Datum      *arrayelems;
     170                 :             : 
     171                 :           1 :         arrayelems = (Datum *) palloc(ondisk.builder.committed.xcnt * sizeof(Datum));
     172                 :             : 
     173         [ +  + ]:           2 :         for (int j = 0; j < ondisk.builder.committed.xcnt; j++)
     174                 :           1 :             arrayelems[j] = TransactionIdGetDatum(ondisk.builder.committed.xip[j]);
     175                 :             : 
     176                 :           1 :         values[i++] = PointerGetDatum(construct_array_builtin(arrayelems,
     177                 :             :                                                               ondisk.builder.committed.xcnt,
     178                 :             :                                                               XIDOID));
     179                 :             :     }
     180                 :             :     else
     181                 :           0 :         nulls[i++] = true;
     182                 :             : 
     183                 :           1 :     values[i++] = UInt32GetDatum(ondisk.builder.catchange.xcnt);
     184         [ +  - ]:           1 :     if (ondisk.builder.catchange.xcnt > 0)
     185                 :             :     {
     186                 :             :         Datum      *arrayelems;
     187                 :             : 
     188                 :           1 :         arrayelems = (Datum *) palloc(ondisk.builder.catchange.xcnt * sizeof(Datum));
     189                 :             : 
     190         [ +  + ]:           3 :         for (int j = 0; j < ondisk.builder.catchange.xcnt; j++)
     191                 :           2 :             arrayelems[j] = TransactionIdGetDatum(ondisk.builder.catchange.xip[j]);
     192                 :             : 
     193                 :           1 :         values[i++] = PointerGetDatum(construct_array_builtin(arrayelems,
     194                 :             :                                                               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                 :           1 :     tuple = heap_form_tuple(tupdesc, values, nulls);
     203                 :             : 
     204                 :           1 :     PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
     205                 :             : 
     206                 :             : #undef PG_GET_LOGICAL_SNAPSHOT_INFO_COLS
     207                 :             : }
        

Generated by: LCOV version 2.0-1