mirror of
				https://github.com/postgres/postgres.git
				synced 2025-10-25 13:17:41 +03:00 
			
		
		
		
	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
		
			
				
	
	
		
			208 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			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
 | |
| }
 |