diff options
Diffstat (limited to 'contrib/old_snapshot')
-rw-r--r-- | contrib/old_snapshot/Makefile | 21 | ||||
-rw-r--r-- | contrib/old_snapshot/old_snapshot--1.0.sql | 14 | ||||
-rw-r--r-- | contrib/old_snapshot/old_snapshot.control | 5 | ||||
-rw-r--r-- | contrib/old_snapshot/time_mapping.c | 160 |
4 files changed, 200 insertions, 0 deletions
diff --git a/contrib/old_snapshot/Makefile b/contrib/old_snapshot/Makefile new file mode 100644 index 0000000..adb5575 --- /dev/null +++ b/contrib/old_snapshot/Makefile @@ -0,0 +1,21 @@ +# contrib/old_snapshot/Makefile + +MODULE_big = old_snapshot +OBJS = \ + $(WIN32RES) \ + time_mapping.o + +EXTENSION = old_snapshot +DATA = old_snapshot--1.0.sql +PGFILEDESC = "old_snapshot - utilities in support of old_snapshot_threshold" + +ifdef USE_PGXS +PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) +else +subdir = contrib/old_snapshot +top_builddir = ../.. +include $(top_builddir)/src/Makefile.global +include $(top_srcdir)/contrib/contrib-global.mk +endif diff --git a/contrib/old_snapshot/old_snapshot--1.0.sql b/contrib/old_snapshot/old_snapshot--1.0.sql new file mode 100644 index 0000000..9ebb882 --- /dev/null +++ b/contrib/old_snapshot/old_snapshot--1.0.sql @@ -0,0 +1,14 @@ +/* contrib/old_snapshot/old_snapshot--1.0.sql */ + +-- complain if script is sourced in psql, rather than via CREATE EXTENSION +\echo Use "CREATE EXTENSION old_snapshot" to load this file. \quit + +-- Show visibility map and page-level visibility information for each block. +CREATE FUNCTION pg_old_snapshot_time_mapping(array_offset OUT int4, + end_timestamp OUT timestamptz, + newest_xmin OUT xid) +RETURNS SETOF record +AS 'MODULE_PATHNAME', 'pg_old_snapshot_time_mapping' +LANGUAGE C STRICT; + +-- XXX. Do we want REVOKE commands here? diff --git a/contrib/old_snapshot/old_snapshot.control b/contrib/old_snapshot/old_snapshot.control new file mode 100644 index 0000000..491eec5 --- /dev/null +++ b/contrib/old_snapshot/old_snapshot.control @@ -0,0 +1,5 @@ +# old_snapshot extension +comment = 'utilities in support of old_snapshot_threshold' +default_version = '1.0' +module_pathname = '$libdir/old_snapshot' +relocatable = true diff --git a/contrib/old_snapshot/time_mapping.c b/contrib/old_snapshot/time_mapping.c new file mode 100644 index 0000000..02acf77 --- /dev/null +++ b/contrib/old_snapshot/time_mapping.c @@ -0,0 +1,160 @@ +/*------------------------------------------------------------------------- + * + * time_mapping.c + * time to XID mapping information + * + * Copyright (c) 2020-2021, 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); +} |