summaryrefslogtreecommitdiff
path: root/contrib/old_snapshot/time_mapping.c
blob: 352308cd49b48c63b90fd02a0f0645ecb26d6b5e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/*-------------------------------------------------------------------------
 *
 * time_mapping.c
 *	  time to XID mapping information
 *
 * Copyright (c) 2020-2023, 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 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;
		TupleDesc	tupdesc;

		funcctx = SRF_FIRSTCALL_INIT();
		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
		mapping = GetOldSnapshotTimeMapping();
		funcctx->user_fctx = mapping;
		if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
			elog(ERROR, "return type must be a row type");
		funcctx->tuple_desc = tupdesc;
		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;
}

/*
 * 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);
}