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 : }