|
| 1 | +/*------------------------------------------------------------------------- |
| 2 | + * |
| 3 | + * time_mapping.c |
| 4 | + * time to XID mapping information |
| 5 | + * |
| 6 | + * Copyright (c) 2020, PostgreSQL Global Development Group |
| 7 | + * |
| 8 | + * contrib/old_snapshot/time_mapping.c |
| 9 | + *------------------------------------------------------------------------- |
| 10 | + */ |
| 11 | +#include "postgres.h" |
| 12 | + |
| 13 | +#include "funcapi.h" |
| 14 | +#include "storage/lwlock.h" |
| 15 | +#include "utils/old_snapshot.h" |
| 16 | +#include "utils/snapmgr.h" |
| 17 | +#include "utils/timestamp.h" |
| 18 | + |
| 19 | +/* |
| 20 | + * Backend-private copy of the information from oldSnapshotControl which relates |
| 21 | + * to the time to XID mapping, plus an index so that we can iterate. |
| 22 | + * |
| 23 | + * Note that the length of the xid_by_minute array is given by |
| 24 | + * OLD_SNAPSHOT_TIME_MAP_ENTRIES (which is not a compile-time constant). |
| 25 | + */ |
| 26 | +typedef struct |
| 27 | +{ |
| 28 | + int current_index; |
| 29 | + int head_offset; |
| 30 | + TimestampTz head_timestamp; |
| 31 | + int count_used; |
| 32 | + TransactionId xid_by_minute[FLEXIBLE_ARRAY_MEMBER]; |
| 33 | +} OldSnapshotTimeMapping; |
| 34 | + |
| 35 | +#define NUM_TIME_MAPPING_COLUMNS 3 |
| 36 | + |
| 37 | +PG_MODULE_MAGIC; |
| 38 | +PG_FUNCTION_INFO_V1(pg_old_snapshot_time_mapping); |
| 39 | + |
| 40 | +static OldSnapshotTimeMapping *GetOldSnapshotTimeMapping(void); |
| 41 | +static TupleDesc MakeOldSnapshotTimeMappingTupleDesc(void); |
| 42 | +static HeapTuple MakeOldSnapshotTimeMappingTuple(TupleDesc tupdesc, |
| 43 | + OldSnapshotTimeMapping *mapping); |
| 44 | + |
| 45 | +/* |
| 46 | + * SQL-callable set-returning function. |
| 47 | + */ |
| 48 | +Datum |
| 49 | +pg_old_snapshot_time_mapping(PG_FUNCTION_ARGS) |
| 50 | +{ |
| 51 | + FuncCallContext *funcctx; |
| 52 | + OldSnapshotTimeMapping *mapping; |
| 53 | + |
| 54 | + if (SRF_IS_FIRSTCALL()) |
| 55 | + { |
| 56 | + MemoryContext oldcontext; |
| 57 | + |
| 58 | + funcctx = SRF_FIRSTCALL_INIT(); |
| 59 | + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); |
| 60 | + mapping = GetOldSnapshotTimeMapping(); |
| 61 | + funcctx->user_fctx = mapping; |
| 62 | + funcctx->tuple_desc = MakeOldSnapshotTimeMappingTupleDesc(); |
| 63 | + MemoryContextSwitchTo(oldcontext); |
| 64 | + } |
| 65 | + |
| 66 | + funcctx = SRF_PERCALL_SETUP(); |
| 67 | + mapping = (OldSnapshotTimeMapping *) funcctx->user_fctx; |
| 68 | + |
| 69 | + while (mapping->current_index < mapping->count_used) |
| 70 | + { |
| 71 | + HeapTuple tuple; |
| 72 | + |
| 73 | + tuple = MakeOldSnapshotTimeMappingTuple(funcctx->tuple_desc, mapping); |
| 74 | + ++mapping->current_index; |
| 75 | + SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple)); |
| 76 | + } |
| 77 | + |
| 78 | + SRF_RETURN_DONE(funcctx); |
| 79 | +} |
| 80 | + |
| 81 | +/* |
| 82 | + * Get the old snapshot time mapping data from shared memory. |
| 83 | + */ |
| 84 | +static OldSnapshotTimeMapping * |
| 85 | +GetOldSnapshotTimeMapping(void) |
| 86 | +{ |
| 87 | + OldSnapshotTimeMapping *mapping; |
| 88 | + |
| 89 | + mapping = palloc(offsetof(OldSnapshotTimeMapping, xid_by_minute) |
| 90 | + + sizeof(TransactionId) * OLD_SNAPSHOT_TIME_MAP_ENTRIES); |
| 91 | + mapping->current_index = 0; |
| 92 | + |
| 93 | + LWLockAcquire(OldSnapshotTimeMapLock, LW_SHARED); |
| 94 | + mapping->head_offset = oldSnapshotControl->head_offset; |
| 95 | + mapping->head_timestamp = oldSnapshotControl->head_timestamp; |
| 96 | + mapping->count_used = oldSnapshotControl->count_used; |
| 97 | + for (int i = 0; i < OLD_SNAPSHOT_TIME_MAP_ENTRIES; ++i) |
| 98 | + mapping->xid_by_minute[i] = oldSnapshotControl->xid_by_minute[i]; |
| 99 | + LWLockRelease(OldSnapshotTimeMapLock); |
| 100 | + |
| 101 | + return mapping; |
| 102 | +} |
| 103 | + |
| 104 | +/* |
| 105 | + * Build a tuple descriptor for the pg_old_snapshot_time_mapping() SRF. |
| 106 | + */ |
| 107 | +static TupleDesc |
| 108 | +MakeOldSnapshotTimeMappingTupleDesc(void) |
| 109 | +{ |
| 110 | + TupleDesc tupdesc; |
| 111 | + |
| 112 | + tupdesc = CreateTemplateTupleDesc(NUM_TIME_MAPPING_COLUMNS); |
| 113 | + |
| 114 | + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "array_offset", |
| 115 | + INT4OID, -1, 0); |
| 116 | + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "end_timestamp", |
| 117 | + TIMESTAMPTZOID, -1, 0); |
| 118 | + TupleDescInitEntry(tupdesc, (AttrNumber) 3, "newest_xmin", |
| 119 | + XIDOID, -1, 0); |
| 120 | + |
| 121 | + return BlessTupleDesc(tupdesc); |
| 122 | +} |
| 123 | + |
| 124 | +/* |
| 125 | + * Convert one entry from the old snapshot time mapping to a HeapTuple. |
| 126 | + */ |
| 127 | +static HeapTuple |
| 128 | +MakeOldSnapshotTimeMappingTuple(TupleDesc tupdesc, OldSnapshotTimeMapping *mapping) |
| 129 | +{ |
| 130 | + Datum values[NUM_TIME_MAPPING_COLUMNS]; |
| 131 | + bool nulls[NUM_TIME_MAPPING_COLUMNS]; |
| 132 | + int array_position; |
| 133 | + TimestampTz timestamp; |
| 134 | + |
| 135 | + /* |
| 136 | + * Figure out the array position corresponding to the current index. |
| 137 | + * |
| 138 | + * Index 0 means the oldest entry in the mapping, which is stored at |
| 139 | + * mapping->head_offset. Index 1 means the next-oldest entry, which is a the |
| 140 | + * following index, and so on. We wrap around when we reach the end of the array. |
| 141 | + */ |
| 142 | + array_position = (mapping->head_offset + mapping->current_index) |
| 143 | + % OLD_SNAPSHOT_TIME_MAP_ENTRIES; |
| 144 | + |
| 145 | + /* |
| 146 | + * No explicit timestamp is stored for any entry other than the oldest one, |
| 147 | + * but each entry corresponds to 1-minute period, so we can just add. |
| 148 | + */ |
| 149 | + timestamp = TimestampTzPlusMilliseconds(mapping->head_timestamp, |
| 150 | + mapping->current_index * 60000); |
| 151 | + |
| 152 | + /* Initialize nulls and values arrays. */ |
| 153 | + memset(nulls, 0, sizeof(nulls)); |
| 154 | + values[0] = Int32GetDatum(array_position); |
| 155 | + values[1] = TimestampTzGetDatum(timestamp); |
| 156 | + values[2] = TransactionIdGetDatum(mapping->xid_by_minute[array_position]); |
| 157 | + |
| 158 | + return heap_form_tuple(tupdesc, values, nulls); |
| 159 | +} |
0 commit comments