mirror of
				https://github.com/postgres/postgres.git
				synced 2025-10-29 22:49:41 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			161 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			161 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*-------------------------------------------------------------------------
 | |
|  *
 | |
|  * time_mapping.c
 | |
|  *	  time to XID mapping information
 | |
|  *
 | |
|  * Copyright (c) 2020-2022, PostgreSQL Global Development Group
 | |
|  *
 | |
|  *	  contrib/old_snapshot/time_mapping.c
 | |
|  *-------------------------------------------------------------------------
 | |
|  */
 | |
| #include "postgres.h"
 | |
| 
 | |
| #include "funcapi.h"
 | |
| #include "storage/lwlock.h"
 | |
| #include "utils/old_snapshot.h"
 | |
| #include "utils/snapmgr.h"
 | |
| #include "utils/timestamp.h"
 | |
| 
 | |
| /*
 | |
|  * Backend-private copy of the information from oldSnapshotControl which relates
 | |
|  * to the time to XID mapping, plus an index so that we can iterate.
 | |
|  *
 | |
|  * Note that the length of the xid_by_minute array is given by
 | |
|  * OLD_SNAPSHOT_TIME_MAP_ENTRIES (which is not a compile-time constant).
 | |
|  */
 | |
| typedef struct
 | |
| {
 | |
| 	int			current_index;
 | |
| 	int			head_offset;
 | |
| 	TimestampTz head_timestamp;
 | |
| 	int			count_used;
 | |
| 	TransactionId xid_by_minute[FLEXIBLE_ARRAY_MEMBER];
 | |
| } OldSnapshotTimeMapping;
 | |
| 
 | |
| #define NUM_TIME_MAPPING_COLUMNS 3
 | |
| 
 | |
| PG_MODULE_MAGIC;
 | |
| PG_FUNCTION_INFO_V1(pg_old_snapshot_time_mapping);
 | |
| 
 | |
| static OldSnapshotTimeMapping *GetOldSnapshotTimeMapping(void);
 | |
| static TupleDesc MakeOldSnapshotTimeMappingTupleDesc(void);
 | |
| static HeapTuple MakeOldSnapshotTimeMappingTuple(TupleDesc tupdesc,
 | |
| 												 OldSnapshotTimeMapping *mapping);
 | |
| 
 | |
| /*
 | |
|  * SQL-callable set-returning function.
 | |
|  */
 | |
| Datum
 | |
| pg_old_snapshot_time_mapping(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	FuncCallContext *funcctx;
 | |
| 	OldSnapshotTimeMapping *mapping;
 | |
| 
 | |
| 	if (SRF_IS_FIRSTCALL())
 | |
| 	{
 | |
| 		MemoryContext oldcontext;
 | |
| 
 | |
| 		funcctx = SRF_FIRSTCALL_INIT();
 | |
| 		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
 | |
| 		mapping = GetOldSnapshotTimeMapping();
 | |
| 		funcctx->user_fctx = mapping;
 | |
| 		funcctx->tuple_desc = MakeOldSnapshotTimeMappingTupleDesc();
 | |
| 		MemoryContextSwitchTo(oldcontext);
 | |
| 	}
 | |
| 
 | |
| 	funcctx = SRF_PERCALL_SETUP();
 | |
| 	mapping = (OldSnapshotTimeMapping *) funcctx->user_fctx;
 | |
| 
 | |
| 	while (mapping->current_index < mapping->count_used)
 | |
| 	{
 | |
| 		HeapTuple	tuple;
 | |
| 
 | |
| 		tuple = MakeOldSnapshotTimeMappingTuple(funcctx->tuple_desc, mapping);
 | |
| 		++mapping->current_index;
 | |
| 		SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
 | |
| 	}
 | |
| 
 | |
| 	SRF_RETURN_DONE(funcctx);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Get the old snapshot time mapping data from shared memory.
 | |
|  */
 | |
| static OldSnapshotTimeMapping *
 | |
| GetOldSnapshotTimeMapping(void)
 | |
| {
 | |
| 	OldSnapshotTimeMapping *mapping;
 | |
| 
 | |
| 	mapping = palloc(offsetof(OldSnapshotTimeMapping, xid_by_minute)
 | |
| 					 + sizeof(TransactionId) * OLD_SNAPSHOT_TIME_MAP_ENTRIES);
 | |
| 	mapping->current_index = 0;
 | |
| 
 | |
| 	LWLockAcquire(OldSnapshotTimeMapLock, LW_SHARED);
 | |
| 	mapping->head_offset = oldSnapshotControl->head_offset;
 | |
| 	mapping->head_timestamp = oldSnapshotControl->head_timestamp;
 | |
| 	mapping->count_used = oldSnapshotControl->count_used;
 | |
| 	for (int i = 0; i < OLD_SNAPSHOT_TIME_MAP_ENTRIES; ++i)
 | |
| 		mapping->xid_by_minute[i] = oldSnapshotControl->xid_by_minute[i];
 | |
| 	LWLockRelease(OldSnapshotTimeMapLock);
 | |
| 
 | |
| 	return mapping;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Build a tuple descriptor for the pg_old_snapshot_time_mapping() SRF.
 | |
|  */
 | |
| static TupleDesc
 | |
| MakeOldSnapshotTimeMappingTupleDesc(void)
 | |
| {
 | |
| 	TupleDesc	tupdesc;
 | |
| 
 | |
| 	tupdesc = CreateTemplateTupleDesc(NUM_TIME_MAPPING_COLUMNS);
 | |
| 
 | |
| 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "array_offset",
 | |
| 					   INT4OID, -1, 0);
 | |
| 	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "end_timestamp",
 | |
| 					   TIMESTAMPTZOID, -1, 0);
 | |
| 	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "newest_xmin",
 | |
| 					   XIDOID, -1, 0);
 | |
| 
 | |
| 	return BlessTupleDesc(tupdesc);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Convert one entry from the old snapshot time mapping to a HeapTuple.
 | |
|  */
 | |
| static HeapTuple
 | |
| MakeOldSnapshotTimeMappingTuple(TupleDesc tupdesc, OldSnapshotTimeMapping *mapping)
 | |
| {
 | |
| 	Datum		values[NUM_TIME_MAPPING_COLUMNS];
 | |
| 	bool		nulls[NUM_TIME_MAPPING_COLUMNS];
 | |
| 	int			array_position;
 | |
| 	TimestampTz timestamp;
 | |
| 
 | |
| 	/*
 | |
| 	 * Figure out the array position corresponding to the current index.
 | |
| 	 *
 | |
| 	 * Index 0 means the oldest entry in the mapping, which is stored at
 | |
| 	 * mapping->head_offset. Index 1 means the next-oldest entry, which is a
 | |
| 	 * the following index, and so on. We wrap around when we reach the end of
 | |
| 	 * the array.
 | |
| 	 */
 | |
| 	array_position = (mapping->head_offset + mapping->current_index)
 | |
| 		% OLD_SNAPSHOT_TIME_MAP_ENTRIES;
 | |
| 
 | |
| 	/*
 | |
| 	 * No explicit timestamp is stored for any entry other than the oldest
 | |
| 	 * one, but each entry corresponds to 1-minute period, so we can just add.
 | |
| 	 */
 | |
| 	timestamp = TimestampTzPlusMilliseconds(mapping->head_timestamp,
 | |
| 											mapping->current_index * 60000);
 | |
| 
 | |
| 	/* Initialize nulls and values arrays. */
 | |
| 	memset(nulls, 0, sizeof(nulls));
 | |
| 	values[0] = Int32GetDatum(array_position);
 | |
| 	values[1] = TimestampTzGetDatum(timestamp);
 | |
| 	values[2] = TransactionIdGetDatum(mapping->xid_by_minute[array_position]);
 | |
| 
 | |
| 	return heap_form_tuple(tupdesc, values, nulls);
 | |
| }
 |