summaryrefslogtreecommitdiffstats
path: root/contrib/old_snapshot
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/old_snapshot')
-rw-r--r--contrib/old_snapshot/Makefile21
-rw-r--r--contrib/old_snapshot/old_snapshot--1.0.sql14
-rw-r--r--contrib/old_snapshot/old_snapshot.control5
-rw-r--r--contrib/old_snapshot/time_mapping.c160
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);
+}