1
0
mirror of https://github.com/postgres/postgres.git synced 2025-04-21 12:05:57 +03:00
postgres/contrib/pg_logicalinspect/pg_logicalinspect.c
Tom Lane 55527368bd Use PG_MODULE_MAGIC_EXT in our installable shared libraries.
It seems potentially useful to label our shared libraries with version
information, now that a facility exists for retrieving that.  This
patch labels them with the PG_VERSION string.  There was some
discussion about using semantic versioning conventions, but that
doesn't seem terribly helpful for modules with no SQL-level presence;
and for those that do have SQL objects, we typically expect them
to support multiple revisions of the SQL definitions, so it'd still
not be very helpful.

I did not label any of src/test/modules/.  It seems unnecessary since
we don't install those, and besides there ought to be someplace that
still provides test coverage for the original PG_MODULE_MAGIC macro.

Author: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/dd4d1b59-d0fe-49d5-b28f-1e463b68fa32@gmail.com
2025-03-26 11:11:02 -04:00

208 lines
5.8 KiB
C

/*-------------------------------------------------------------------------
*
* pg_logicalinspect.c
* Functions to inspect contents of PostgreSQL logical snapshots
*
* Copyright (c) 2024-2025, PostgreSQL Global Development Group
*
* IDENTIFICATION
* contrib/pg_logicalinspect/pg_logicalinspect.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "funcapi.h"
#include "replication/snapbuild_internal.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/pg_lsn.h"
PG_MODULE_MAGIC_EXT(
.name = "pg_logicalinspect",
.version = PG_VERSION
);
PG_FUNCTION_INFO_V1(pg_get_logical_snapshot_meta);
PG_FUNCTION_INFO_V1(pg_get_logical_snapshot_info);
/* Return the description of SnapBuildState */
static const char *
get_snapbuild_state_desc(SnapBuildState state)
{
const char *stateDesc = "unknown state";
switch (state)
{
case SNAPBUILD_START:
stateDesc = "start";
break;
case SNAPBUILD_BUILDING_SNAPSHOT:
stateDesc = "building";
break;
case SNAPBUILD_FULL_SNAPSHOT:
stateDesc = "full";
break;
case SNAPBUILD_CONSISTENT:
stateDesc = "consistent";
break;
}
return stateDesc;
}
/*
* Extract the LSN from the given serialized snapshot file name.
*/
static XLogRecPtr
parse_snapshot_filename(const char *filename)
{
uint32 hi;
uint32 lo;
XLogRecPtr lsn;
char tmpfname[MAXPGPATH];
/*
* Extract the values to build the LSN.
*
* Note: Including ".snap" doesn't mean that sscanf() also does the format
* check including the suffix. The subsequent check validates if the given
* filename has the expected suffix.
*/
if (sscanf(filename, "%X-%X.snap", &hi, &lo) != 2)
goto parse_error;
/*
* Bring back the extracted LSN to the snapshot file format and compare it
* to the given filename. This check strictly checks if the given filename
* follows the format of the snapshot filename.
*/
sprintf(tmpfname, "%X-%X.snap", hi, lo);
if (strcmp(tmpfname, filename) != 0)
goto parse_error;
lsn = ((uint64) hi) << 32 | lo;
return lsn;
parse_error:
ereport(ERROR,
errmsg("invalid snapshot file name \"%s\"", filename));
return InvalidXLogRecPtr; /* keep compiler quiet */
}
/*
* Retrieve the logical snapshot file metadata.
*/
Datum
pg_get_logical_snapshot_meta(PG_FUNCTION_ARGS)
{
#define PG_GET_LOGICAL_SNAPSHOT_META_COLS 3
SnapBuildOnDisk ondisk;
HeapTuple tuple;
Datum values[PG_GET_LOGICAL_SNAPSHOT_META_COLS] = {0};
bool nulls[PG_GET_LOGICAL_SNAPSHOT_META_COLS] = {0};
TupleDesc tupdesc;
XLogRecPtr lsn;
int i = 0;
text *filename_t = PG_GETARG_TEXT_PP(0);
/* Build a tuple descriptor for our result type */
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
elog(ERROR, "return type must be a row type");
lsn = parse_snapshot_filename(text_to_cstring(filename_t));
/* Validate and restore the snapshot to 'ondisk' */
SnapBuildRestoreSnapshot(&ondisk, lsn, CurrentMemoryContext, false);
values[i++] = UInt32GetDatum(ondisk.magic);
values[i++] = Int64GetDatum((int64) ondisk.checksum);
values[i++] = UInt32GetDatum(ondisk.version);
Assert(i == PG_GET_LOGICAL_SNAPSHOT_META_COLS);
tuple = heap_form_tuple(tupdesc, values, nulls);
PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
#undef PG_GET_LOGICAL_SNAPSHOT_META_COLS
}
Datum
pg_get_logical_snapshot_info(PG_FUNCTION_ARGS)
{
#define PG_GET_LOGICAL_SNAPSHOT_INFO_COLS 14
SnapBuildOnDisk ondisk;
HeapTuple tuple;
Datum values[PG_GET_LOGICAL_SNAPSHOT_INFO_COLS] = {0};
bool nulls[PG_GET_LOGICAL_SNAPSHOT_INFO_COLS] = {0};
TupleDesc tupdesc;
XLogRecPtr lsn;
int i = 0;
text *filename_t = PG_GETARG_TEXT_PP(0);
/* Build a tuple descriptor for our result type */
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
elog(ERROR, "return type must be a row type");
lsn = parse_snapshot_filename(text_to_cstring(filename_t));
/* Validate and restore the snapshot to 'ondisk' */
SnapBuildRestoreSnapshot(&ondisk, lsn, CurrentMemoryContext, false);
values[i++] = CStringGetTextDatum(get_snapbuild_state_desc(ondisk.builder.state));
values[i++] = TransactionIdGetDatum(ondisk.builder.xmin);
values[i++] = TransactionIdGetDatum(ondisk.builder.xmax);
values[i++] = LSNGetDatum(ondisk.builder.start_decoding_at);
values[i++] = LSNGetDatum(ondisk.builder.two_phase_at);
values[i++] = TransactionIdGetDatum(ondisk.builder.initial_xmin_horizon);
values[i++] = BoolGetDatum(ondisk.builder.building_full_snapshot);
values[i++] = BoolGetDatum(ondisk.builder.in_slot_creation);
values[i++] = LSNGetDatum(ondisk.builder.last_serialized_snapshot);
values[i++] = TransactionIdGetDatum(ondisk.builder.next_phase_at);
values[i++] = UInt32GetDatum(ondisk.builder.committed.xcnt);
if (ondisk.builder.committed.xcnt > 0)
{
Datum *arrayelems;
arrayelems = (Datum *) palloc(ondisk.builder.committed.xcnt * sizeof(Datum));
for (int j = 0; j < ondisk.builder.committed.xcnt; j++)
arrayelems[j] = TransactionIdGetDatum(ondisk.builder.committed.xip[j]);
values[i++] = PointerGetDatum(construct_array_builtin(arrayelems,
ondisk.builder.committed.xcnt,
XIDOID));
}
else
nulls[i++] = true;
values[i++] = UInt32GetDatum(ondisk.builder.catchange.xcnt);
if (ondisk.builder.catchange.xcnt > 0)
{
Datum *arrayelems;
arrayelems = (Datum *) palloc(ondisk.builder.catchange.xcnt * sizeof(Datum));
for (int j = 0; j < ondisk.builder.catchange.xcnt; j++)
arrayelems[j] = TransactionIdGetDatum(ondisk.builder.catchange.xip[j]);
values[i++] = PointerGetDatum(construct_array_builtin(arrayelems,
ondisk.builder.catchange.xcnt,
XIDOID));
}
else
nulls[i++] = true;
Assert(i == PG_GET_LOGICAL_SNAPSHOT_INFO_COLS);
tuple = heap_form_tuple(tupdesc, values, nulls);
PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
#undef PG_GET_LOGICAL_SNAPSHOT_INFO_COLS
}