Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : * xid8funcs.c
3 : : *
4 : : * Export internal transaction IDs to user level.
5 : : *
6 : : * Note that only top-level transaction IDs are exposed to user sessions.
7 : : * This is important because xid8s frequently persist beyond the global
8 : : * xmin horizon, or may even be shipped to other machines, so we cannot
9 : : * rely on being able to correlate subtransaction IDs with their parents
10 : : * via functions such as SubTransGetTopmostTransaction().
11 : : *
12 : : * These functions are used to support the txid_XXX functions and the newer
13 : : * pg_current_xact_id, pg_current_snapshot and related fmgr functions, since
14 : : * the only difference between them is whether they expose xid8 or int8 values
15 : : * to users. The txid_XXX variants should eventually be dropped.
16 : : *
17 : : *
18 : : * Copyright (c) 2003-2025, PostgreSQL Global Development Group
19 : : * Author: Jan Wieck, Afilias USA INC.
20 : : * 64-bit txids: Marko Kreen, Skype Technologies
21 : : *
22 : : * src/backend/utils/adt/xid8funcs.c
23 : : *
24 : : *-------------------------------------------------------------------------
25 : : */
26 : :
27 : : #include "postgres.h"
28 : :
29 : : #include "access/transam.h"
30 : : #include "access/xact.h"
31 : : #include "funcapi.h"
32 : : #include "lib/qunique.h"
33 : : #include "libpq/pqformat.h"
34 : : #include "miscadmin.h"
35 : : #include "storage/lwlock.h"
36 : : #include "storage/procarray.h"
37 : : #include "storage/procnumber.h"
38 : : #include "utils/builtins.h"
39 : : #include "utils/memutils.h"
40 : : #include "utils/snapmgr.h"
41 : : #include "utils/xid8.h"
42 : : #include "varatt.h"
43 : :
44 : :
45 : : /*
46 : : * If defined, use bsearch() function for searching for xid8s in snapshots
47 : : * that have more than the specified number of values.
48 : : */
49 : : #define USE_BSEARCH_IF_NXIP_GREATER 30
50 : :
51 : :
52 : : /*
53 : : * Snapshot containing FullTransactionIds.
54 : : */
55 : : typedef struct
56 : : {
57 : : /*
58 : : * 4-byte length hdr, should not be touched directly.
59 : : *
60 : : * Explicit embedding is ok as we want always correct alignment anyway.
61 : : */
62 : : int32 __varsz;
63 : :
64 : : uint32 nxip; /* number of fxids in xip array */
65 : : FullTransactionId xmin;
66 : : FullTransactionId xmax;
67 : : /* in-progress fxids, xmin <= xip[i] < xmax: */
68 : : FullTransactionId xip[FLEXIBLE_ARRAY_MEMBER];
69 : : } pg_snapshot;
70 : :
71 : : #define PG_SNAPSHOT_SIZE(nxip) \
72 : : (offsetof(pg_snapshot, xip) + sizeof(FullTransactionId) * (nxip))
73 : : #define PG_SNAPSHOT_MAX_NXIP \
74 : : ((MaxAllocSize - offsetof(pg_snapshot, xip)) / sizeof(FullTransactionId))
75 : :
76 : : /*
77 : : * Compile-time limits on the procarray (MAX_BACKENDS processes plus
78 : : * MAX_BACKENDS prepared transactions) guarantee nxip won't be too large.
79 : : */
80 : : StaticAssertDecl(MAX_BACKENDS * 2 <= PG_SNAPSHOT_MAX_NXIP,
81 : : "possible overflow in pg_current_snapshot()");
82 : :
83 : :
84 : : /*
85 : : * Helper to get a TransactionId from a 64-bit xid with wraparound detection.
86 : : *
87 : : * It is an ERROR if the xid is in the future. Otherwise, returns true if
88 : : * the transaction is still new enough that we can determine whether it
89 : : * committed and false otherwise. If *extracted_xid is not NULL, it is set
90 : : * to the low 32 bits of the transaction ID (i.e. the actual XID, without the
91 : : * epoch).
92 : : *
93 : : * The caller must hold XactTruncationLock since it's dealing with arbitrary
94 : : * XIDs, and must continue to hold it until it's done with any clog lookups
95 : : * relating to those XIDs.
96 : : */
97 : : static bool
2030 tmunro@postgresql.or 98 :CBC 42 : TransactionIdInRecentPast(FullTransactionId fxid, TransactionId *extracted_xid)
99 : : {
100 : 42 : TransactionId xid = XidFromFullTransactionId(fxid);
101 : : FullTransactionId now_fullxid;
102 : : TransactionId oldest_clog_xid;
103 : : FullTransactionId oldest_clog_fxid;
104 : :
2406 105 : 42 : now_fullxid = ReadNextFullTransactionId();
106 : :
3140 rhaas@postgresql.org 107 [ + - ]: 42 : if (extracted_xid != NULL)
108 : 42 : *extracted_xid = xid;
109 : :
110 [ - + ]: 42 : if (!TransactionIdIsValid(xid))
3140 rhaas@postgresql.org 111 :UBC 0 : return false;
112 : :
113 : : /* For non-normal transaction IDs, we can ignore the epoch. */
3140 rhaas@postgresql.org 114 [ + + ]:CBC 42 : if (!TransactionIdIsNormal(xid))
115 : 12 : return true;
116 : :
117 : : /* If the transaction ID is in the future, throw an error. */
2030 tmunro@postgresql.or 118 [ + + ]: 30 : if (!FullTransactionIdPrecedes(fxid, now_fullxid))
3140 rhaas@postgresql.org 119 [ + - ]: 6 : ereport(ERROR,
120 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
121 : : errmsg("transaction ID %" PRIu64 " is in the future",
122 : : U64FromFullTransactionId(fxid))));
123 : :
124 : : /*
125 : : * TransamVariables->oldestClogXid is protected by XactTruncationLock, but
126 : : * we don't acquire that lock here. Instead, we require the caller to
127 : : * acquire it, because the caller is presumably going to look up the
128 : : * returned XID. If we took and released the lock within this function, a
129 : : * CLOG truncation could occur before the caller finished with the XID.
130 : : */
1992 tgl@sss.pgh.pa.us 131 [ - + ]: 24 : Assert(LWLockHeldByMe(XactTruncationLock));
132 : :
133 : : /*
134 : : * If fxid is not older than TransamVariables->oldestClogXid, the relevant
135 : : * CLOG entry is guaranteed to still exist.
136 : : *
137 : : * TransamVariables->oldestXid governs allowable XIDs. Usually,
138 : : * oldestClogXid==oldestXid. It's also possible for oldestClogXid to
139 : : * follow oldestXid, in which case oldestXid might advance after our
140 : : * ReadNextFullTransactionId() call. If oldestXid has advanced, that
141 : : * advancement reinstated the usual oldestClogXid==oldestXid. Whether or
142 : : * not that happened, oldestClogXid is allowable relative to now_fullxid.
143 : : */
276 noah@leadboat.com 144 : 24 : oldest_clog_xid = TransamVariables->oldestClogXid;
145 : : oldest_clog_fxid =
146 : 24 : FullTransactionIdFromAllowableAt(now_fullxid, oldest_clog_xid);
147 : 24 : return !FullTransactionIdPrecedes(fxid, oldest_clog_fxid);
148 : : }
149 : :
150 : : /*
151 : : * txid comparator for qsort/bsearch
152 : : */
153 : : static int
2030 tmunro@postgresql.or 154 : 1384 : cmp_fxid(const void *aa, const void *bb)
155 : : {
156 : 1384 : FullTransactionId a = *(const FullTransactionId *) aa;
157 : 1384 : FullTransactionId b = *(const FullTransactionId *) bb;
158 : :
159 [ + + ]: 1384 : if (FullTransactionIdPrecedes(a, b))
6590 tgl@sss.pgh.pa.us 160 : 336 : return -1;
2030 tmunro@postgresql.or 161 [ + + ]: 1048 : if (FullTransactionIdPrecedes(b, a))
6590 tgl@sss.pgh.pa.us 162 : 862 : return 1;
163 : 186 : return 0;
164 : : }
165 : :
166 : : /*
167 : : * Sort a snapshot's txids, so we can use bsearch() later. Also remove
168 : : * any duplicates.
169 : : *
170 : : * For consistency of on-disk representation, we always sort even if bsearch
171 : : * will not be used.
172 : : */
173 : : static void
2030 tmunro@postgresql.or 174 : 12 : sort_snapshot(pg_snapshot *snap)
175 : : {
6590 tgl@sss.pgh.pa.us 176 [ + + ]: 12 : if (snap->nxip > 1)
177 : : {
2030 tmunro@postgresql.or 178 : 10 : qsort(snap->xip, snap->nxip, sizeof(FullTransactionId), cmp_fxid);
179 : 10 : snap->nxip = qunique(snap->xip, snap->nxip, sizeof(FullTransactionId),
180 : : cmp_fxid);
181 : : }
6590 tgl@sss.pgh.pa.us 182 : 12 : }
183 : :
184 : : /*
185 : : * check fxid visibility.
186 : : */
187 : : static bool
2030 tmunro@postgresql.or 188 : 510 : is_visible_fxid(FullTransactionId value, const pg_snapshot *snap)
189 : : {
190 [ + + ]: 510 : if (FullTransactionIdPrecedes(value, snap->xmin))
6590 tgl@sss.pgh.pa.us 191 : 66 : return true;
2030 tmunro@postgresql.or 192 [ + + ]: 444 : else if (!FullTransactionIdPrecedes(value, snap->xmax))
6590 tgl@sss.pgh.pa.us 193 : 84 : return false;
194 : : #ifdef USE_BSEARCH_IF_NXIP_GREATER
195 [ + + ]: 360 : else if (snap->nxip > USE_BSEARCH_IF_NXIP_GREATER)
196 : : {
197 : : void *res;
198 : :
2030 tmunro@postgresql.or 199 : 300 : res = bsearch(&value, snap->xip, snap->nxip, sizeof(FullTransactionId),
200 : : cmp_fxid);
201 : : /* if found, transaction is still in progress */
6590 tgl@sss.pgh.pa.us 202 : 300 : return (res) ? false : true;
203 : : }
204 : : #endif
205 : : else
206 : : {
207 : : uint32 i;
208 : :
209 [ + + ]: 180 : for (i = 0; i < snap->nxip; i++)
210 : : {
2030 tmunro@postgresql.or 211 [ + + ]: 144 : if (FullTransactionIdEquals(value, snap->xip[i]))
6590 tgl@sss.pgh.pa.us 212 : 24 : return false;
213 : : }
214 : 36 : return true;
215 : : }
216 : : }
217 : :
218 : : /*
219 : : * helper functions to use StringInfo for pg_snapshot creation.
220 : : */
221 : :
222 : : static StringInfo
2030 tmunro@postgresql.or 223 : 93 : buf_init(FullTransactionId xmin, FullTransactionId xmax)
224 : : {
225 : : pg_snapshot snap;
226 : : StringInfo buf;
227 : :
6590 tgl@sss.pgh.pa.us 228 : 93 : snap.xmin = xmin;
229 : 93 : snap.xmax = xmax;
230 : 93 : snap.nxip = 0;
231 : :
232 : 93 : buf = makeStringInfo();
1033 peter@eisentraut.org 233 : 93 : appendBinaryStringInfo(buf, &snap, PG_SNAPSHOT_SIZE(0));
6590 tgl@sss.pgh.pa.us 234 : 93 : return buf;
235 : : }
236 : :
237 : : static void
2030 tmunro@postgresql.or 238 : 312 : buf_add_txid(StringInfo buf, FullTransactionId fxid)
239 : : {
240 : 312 : pg_snapshot *snap = (pg_snapshot *) buf->data;
241 : :
242 : : /* do this before possible realloc */
6590 tgl@sss.pgh.pa.us 243 : 312 : snap->nxip++;
244 : :
1033 peter@eisentraut.org 245 : 312 : appendBinaryStringInfo(buf, &fxid, sizeof(fxid));
6590 tgl@sss.pgh.pa.us 246 : 312 : }
247 : :
248 : : static pg_snapshot *
249 : 75 : buf_finalize(StringInfo buf)
250 : : {
2030 tmunro@postgresql.or 251 : 75 : pg_snapshot *snap = (pg_snapshot *) buf->data;
252 : :
6590 tgl@sss.pgh.pa.us 253 : 75 : SET_VARSIZE(snap, buf->len);
254 : :
255 : : /* buf is not needed anymore */
256 : 75 : buf->data = NULL;
257 : 75 : pfree(buf);
258 : :
259 : 75 : return snap;
260 : : }
261 : :
262 : : /*
263 : : * parse snapshot from cstring
264 : : */
265 : : static pg_snapshot *
1049 266 : 117 : parse_snapshot(const char *str, Node *escontext)
267 : : {
268 : : FullTransactionId xmin;
269 : : FullTransactionId xmax;
2030 tmunro@postgresql.or 270 : 117 : FullTransactionId last_val = InvalidFullTransactionId;
271 : : FullTransactionId val;
6590 tgl@sss.pgh.pa.us 272 : 117 : const char *str_start = str;
273 : : char *endp;
274 : : StringInfo buf;
275 : :
1411 peter@eisentraut.org 276 : 117 : xmin = FullTransactionIdFromU64(strtou64(str, &endp, 10));
6590 tgl@sss.pgh.pa.us 277 [ - + ]: 117 : if (*endp != ':')
6590 tgl@sss.pgh.pa.us 278 :UBC 0 : goto bad_format;
6590 tgl@sss.pgh.pa.us 279 :CBC 117 : str = endp + 1;
280 : :
1411 peter@eisentraut.org 281 : 117 : xmax = FullTransactionIdFromU64(strtou64(str, &endp, 10));
6590 tgl@sss.pgh.pa.us 282 [ - + ]: 117 : if (*endp != ':')
6590 tgl@sss.pgh.pa.us 283 :UBC 0 : goto bad_format;
6590 tgl@sss.pgh.pa.us 284 :CBC 117 : str = endp + 1;
285 : :
286 : : /* it should look sane */
2030 tmunro@postgresql.or 287 [ + + ]: 117 : if (!FullTransactionIdIsValid(xmin) ||
288 [ + + ]: 111 : !FullTransactionIdIsValid(xmax) ||
289 [ + + ]: 105 : FullTransactionIdPrecedes(xmax, xmin))
6590 tgl@sss.pgh.pa.us 290 : 24 : goto bad_format;
291 : :
292 : : /* allocate buffer */
293 : 93 : buf = buf_init(xmin, xmax);
294 : :
295 : : /* loop over values */
296 [ + + ]: 411 : while (*str != '\0')
297 : : {
298 : : /* read next value */
1411 peter@eisentraut.org 299 : 336 : val = FullTransactionIdFromU64(strtou64(str, &endp, 10));
6590 tgl@sss.pgh.pa.us 300 : 336 : str = endp;
301 : :
302 : : /* require the input to be in order */
2030 tmunro@postgresql.or 303 [ + + ]: 336 : if (FullTransactionIdPrecedes(val, xmin) ||
304 [ + - ]: 330 : FullTransactionIdFollowsOrEquals(val, xmax) ||
305 [ + + ]: 330 : FullTransactionIdPrecedes(val, last_val))
6590 tgl@sss.pgh.pa.us 306 : 18 : goto bad_format;
307 : :
308 : : /* skip duplicates */
2030 tmunro@postgresql.or 309 [ + + ]: 318 : if (!FullTransactionIdEquals(val, last_val))
4184 heikki.linnakangas@i 310 : 312 : buf_add_txid(buf, val);
6590 tgl@sss.pgh.pa.us 311 : 318 : last_val = val;
312 : :
313 [ + + ]: 318 : if (*str == ',')
314 : 258 : str++;
315 [ - + ]: 60 : else if (*str != '\0')
6590 tgl@sss.pgh.pa.us 316 :UBC 0 : goto bad_format;
317 : : }
318 : :
6590 tgl@sss.pgh.pa.us 319 :CBC 75 : return buf_finalize(buf);
320 : :
321 : 42 : bad_format:
1049 322 [ + + ]: 42 : ereturn(escontext, NULL,
323 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
324 : : errmsg("invalid input syntax for type %s: \"%s\"",
325 : : "pg_snapshot", str_start)));
326 : : }
327 : :
328 : : /*
329 : : * pg_current_xact_id() returns xid8
330 : : *
331 : : * Return the current toplevel full transaction ID.
332 : : * If the current transaction does not have one, one is assigned.
333 : : */
334 : : Datum
2030 tmunro@postgresql.or 335 : 2969 : pg_current_xact_id(PG_FUNCTION_ARGS)
336 : : {
337 : : /*
338 : : * Must prevent during recovery because if an xid is not assigned we try
339 : : * to assign one, which would fail. Programs already rely on this function
340 : : * to always return a valid current xid, so we should not change this to
341 : : * return NULL or similar invalid xid.
342 : : */
343 : 2969 : PreventCommandDuringRecovery("pg_current_xact_id()");
344 : :
345 : 2969 : PG_RETURN_FULLTRANSACTIONID(GetTopFullTransactionId());
346 : : }
347 : :
348 : : /*
349 : : * Same as pg_current_xact_id() but doesn't assign a new xid if there
350 : : * isn't one yet.
351 : : */
352 : : Datum
353 : 12 : pg_current_xact_id_if_assigned(PG_FUNCTION_ARGS)
354 : : {
355 : 12 : FullTransactionId topfxid = GetTopFullTransactionIdIfAny();
356 : :
357 [ + + ]: 12 : if (!FullTransactionIdIsValid(topfxid))
3353 rhaas@postgresql.org 358 : 6 : PG_RETURN_NULL();
359 : :
2030 tmunro@postgresql.or 360 : 6 : PG_RETURN_FULLTRANSACTIONID(topfxid);
361 : : }
362 : :
363 : : /*
364 : : * pg_current_snapshot() returns pg_snapshot
365 : : *
366 : : * Return current snapshot
367 : : *
368 : : * Note that only top-transaction XIDs are included in the snapshot.
369 : : */
370 : : Datum
371 : 12 : pg_current_snapshot(PG_FUNCTION_ARGS)
372 : : {
373 : : pg_snapshot *snap;
374 : : uint32 nxip,
375 : : i;
376 : : Snapshot cur;
377 : 12 : FullTransactionId next_fxid = ReadNextFullTransactionId();
378 : :
6378 alvherre@alvh.no-ip. 379 : 12 : cur = GetActiveSnapshot();
6590 tgl@sss.pgh.pa.us 380 [ - + ]: 12 : if (cur == NULL)
6378 alvherre@alvh.no-ip. 381 [ # # ]:UBC 0 : elog(ERROR, "no active snapshot set");
382 : :
383 : : /* allocate */
6590 tgl@sss.pgh.pa.us 384 :CBC 12 : nxip = cur->xcnt;
2030 tmunro@postgresql.or 385 : 12 : snap = palloc(PG_SNAPSHOT_SIZE(nxip));
386 : :
387 : : /*
388 : : * Fill. This is the current backend's active snapshot, so MyProc->xmin
389 : : * is <= all these XIDs. As long as that remains so, oldestXid can't
390 : : * advance past any of these XIDs. Hence, these XIDs remain allowable
391 : : * relative to next_fxid.
392 : : */
276 noah@leadboat.com 393 : 12 : snap->xmin = FullTransactionIdFromAllowableAt(next_fxid, cur->xmin);
394 : 12 : snap->xmax = FullTransactionIdFromAllowableAt(next_fxid, cur->xmax);
6590 tgl@sss.pgh.pa.us 395 : 12 : snap->nxip = nxip;
396 [ + + ]: 41 : for (i = 0; i < nxip; i++)
397 : : snap->xip[i] =
276 noah@leadboat.com 398 : 29 : FullTransactionIdFromAllowableAt(next_fxid, cur->xip[i]);
399 : :
400 : : /*
401 : : * We want them guaranteed to be in ascending order. This also removes
402 : : * any duplicate xids. Normally, an XID can only be assigned to one
403 : : * backend, but when preparing a transaction for two-phase commit, there
404 : : * is a transient state when both the original backend and the dummy
405 : : * PGPROC entry reserved for the prepared transaction hold the same XID.
406 : : */
6590 tgl@sss.pgh.pa.us 407 : 12 : sort_snapshot(snap);
408 : :
409 : : /* set size after sorting, because it may have removed duplicate xips */
2030 tmunro@postgresql.or 410 : 12 : SET_VARSIZE(snap, PG_SNAPSHOT_SIZE(snap->nxip));
411 : :
6590 tgl@sss.pgh.pa.us 412 : 12 : PG_RETURN_POINTER(snap);
413 : : }
414 : :
415 : : /*
416 : : * pg_snapshot_in(cstring) returns pg_snapshot
417 : : *
418 : : * input function for type pg_snapshot
419 : : */
420 : : Datum
2030 tmunro@postgresql.or 421 : 117 : pg_snapshot_in(PG_FUNCTION_ARGS)
422 : : {
6590 tgl@sss.pgh.pa.us 423 : 117 : char *str = PG_GETARG_CSTRING(0);
424 : : pg_snapshot *snap;
425 : :
1049 426 : 117 : snap = parse_snapshot(str, fcinfo->context);
427 : :
6590 428 : 87 : PG_RETURN_POINTER(snap);
429 : : }
430 : :
431 : : /*
432 : : * pg_snapshot_out(pg_snapshot) returns cstring
433 : : *
434 : : * output function for type pg_snapshot
435 : : */
436 : : Datum
2030 tmunro@postgresql.or 437 : 62 : pg_snapshot_out(PG_FUNCTION_ARGS)
438 : : {
439 : 62 : pg_snapshot *snap = (pg_snapshot *) PG_GETARG_VARLENA_P(0);
440 : : StringInfoData str;
441 : : uint32 i;
442 : :
6590 tgl@sss.pgh.pa.us 443 : 62 : initStringInfo(&str);
444 : :
2030 tmunro@postgresql.or 445 : 62 : appendStringInfo(&str, UINT64_FORMAT ":",
446 : : U64FromFullTransactionId(snap->xmin));
447 : 62 : appendStringInfo(&str, UINT64_FORMAT ":",
448 : : U64FromFullTransactionId(snap->xmax));
449 : :
6590 tgl@sss.pgh.pa.us 450 [ + + ]: 344 : for (i = 0; i < snap->nxip; i++)
451 : : {
452 [ + + ]: 282 : if (i > 0)
453 : 232 : appendStringInfoChar(&str, ',');
2030 tmunro@postgresql.or 454 : 282 : appendStringInfo(&str, UINT64_FORMAT,
455 : : U64FromFullTransactionId(snap->xip[i]));
456 : : }
457 : :
6590 tgl@sss.pgh.pa.us 458 : 62 : PG_RETURN_CSTRING(str.data);
459 : : }
460 : :
461 : : /*
462 : : * pg_snapshot_recv(internal) returns pg_snapshot
463 : : *
464 : : * binary input function for type pg_snapshot
465 : : *
466 : : * format: int4 nxip, int8 xmin, int8 xmax, int8 xip
467 : : */
468 : : Datum
2030 tmunro@postgresql.or 469 :UBC 0 : pg_snapshot_recv(PG_FUNCTION_ARGS)
470 : : {
6557 bruce@momjian.us 471 : 0 : StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
472 : : pg_snapshot *snap;
2030 tmunro@postgresql.or 473 : 0 : FullTransactionId last = InvalidFullTransactionId;
474 : : int nxip;
475 : : int i;
476 : : FullTransactionId xmin;
477 : : FullTransactionId xmax;
478 : :
479 : : /* load and validate nxip */
6590 tgl@sss.pgh.pa.us 480 : 0 : nxip = pq_getmsgint(buf, 4);
2030 tmunro@postgresql.or 481 [ # # # # ]: 0 : if (nxip < 0 || nxip > PG_SNAPSHOT_MAX_NXIP)
6590 tgl@sss.pgh.pa.us 482 : 0 : goto bad_format;
483 : :
2030 tmunro@postgresql.or 484 : 0 : xmin = FullTransactionIdFromU64((uint64) pq_getmsgint64(buf));
485 : 0 : xmax = FullTransactionIdFromU64((uint64) pq_getmsgint64(buf));
486 [ # # ]: 0 : if (!FullTransactionIdIsValid(xmin) ||
487 [ # # ]: 0 : !FullTransactionIdIsValid(xmax) ||
488 [ # # ]: 0 : FullTransactionIdPrecedes(xmax, xmin))
6590 tgl@sss.pgh.pa.us 489 : 0 : goto bad_format;
490 : :
2030 tmunro@postgresql.or 491 : 0 : snap = palloc(PG_SNAPSHOT_SIZE(nxip));
6590 tgl@sss.pgh.pa.us 492 : 0 : snap->xmin = xmin;
493 : 0 : snap->xmax = xmax;
494 : :
495 [ # # ]: 0 : for (i = 0; i < nxip; i++)
496 : : {
497 : : FullTransactionId cur =
893 498 : 0 : FullTransactionIdFromU64((uint64) pq_getmsgint64(buf));
499 : :
2030 tmunro@postgresql.or 500 [ # # ]: 0 : if (FullTransactionIdPrecedes(cur, last) ||
501 [ # # ]: 0 : FullTransactionIdPrecedes(cur, xmin) ||
502 [ # # ]: 0 : FullTransactionIdPrecedes(xmax, cur))
6590 tgl@sss.pgh.pa.us 503 : 0 : goto bad_format;
504 : :
505 : : /* skip duplicate xips */
2030 tmunro@postgresql.or 506 [ # # ]: 0 : if (FullTransactionIdEquals(cur, last))
507 : : {
4184 heikki.linnakangas@i 508 : 0 : i--;
509 : 0 : nxip--;
510 : 0 : continue;
511 : : }
512 : :
6590 tgl@sss.pgh.pa.us 513 : 0 : snap->xip[i] = cur;
514 : 0 : last = cur;
515 : : }
4184 heikki.linnakangas@i 516 : 0 : snap->nxip = nxip;
2030 tmunro@postgresql.or 517 : 0 : SET_VARSIZE(snap, PG_SNAPSHOT_SIZE(nxip));
6590 tgl@sss.pgh.pa.us 518 : 0 : PG_RETURN_POINTER(snap);
519 : :
520 : 0 : bad_format:
3740 521 [ # # ]: 0 : ereport(ERROR,
522 : : (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
523 : : errmsg("invalid external pg_snapshot data")));
524 : : PG_RETURN_POINTER(NULL); /* keep compiler quiet */
525 : : }
526 : :
527 : : /*
528 : : * pg_snapshot_send(pg_snapshot) returns bytea
529 : : *
530 : : * binary output function for type pg_snapshot
531 : : *
532 : : * format: int4 nxip, u64 xmin, u64 xmax, u64 xip...
533 : : */
534 : : Datum
2030 tmunro@postgresql.or 535 : 0 : pg_snapshot_send(PG_FUNCTION_ARGS)
536 : : {
537 : 0 : pg_snapshot *snap = (pg_snapshot *) PG_GETARG_VARLENA_P(0);
538 : : StringInfoData buf;
539 : : uint32 i;
540 : :
6590 tgl@sss.pgh.pa.us 541 : 0 : pq_begintypsend(&buf);
2939 andres@anarazel.de 542 : 0 : pq_sendint32(&buf, snap->nxip);
2030 tmunro@postgresql.or 543 : 0 : pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xmin));
544 : 0 : pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xmax));
6590 tgl@sss.pgh.pa.us 545 [ # # ]: 0 : for (i = 0; i < snap->nxip; i++)
2030 tmunro@postgresql.or 546 : 0 : pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xip[i]));
6590 tgl@sss.pgh.pa.us 547 : 0 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
548 : : }
549 : :
550 : : /*
551 : : * pg_visible_in_snapshot(xid8, pg_snapshot) returns bool
552 : : *
553 : : * is txid visible in snapshot ?
554 : : */
555 : : Datum
2030 tmunro@postgresql.or 556 :CBC 510 : pg_visible_in_snapshot(PG_FUNCTION_ARGS)
557 : : {
558 : 510 : FullTransactionId value = PG_GETARG_FULLTRANSACTIONID(0);
559 : 510 : pg_snapshot *snap = (pg_snapshot *) PG_GETARG_VARLENA_P(1);
560 : :
561 : 510 : PG_RETURN_BOOL(is_visible_fxid(value, snap));
562 : : }
563 : :
564 : : /*
565 : : * pg_snapshot_xmin(pg_snapshot) returns xid8
566 : : *
567 : : * return snapshot's xmin
568 : : */
569 : : Datum
570 : 30 : pg_snapshot_xmin(PG_FUNCTION_ARGS)
571 : : {
572 : 30 : pg_snapshot *snap = (pg_snapshot *) PG_GETARG_VARLENA_P(0);
573 : :
574 : 30 : PG_RETURN_FULLTRANSACTIONID(snap->xmin);
575 : : }
576 : :
577 : : /*
578 : : * pg_snapshot_xmax(pg_snapshot) returns xid8
579 : : *
580 : : * return snapshot's xmax
581 : : */
582 : : Datum
583 : 24 : pg_snapshot_xmax(PG_FUNCTION_ARGS)
584 : : {
585 : 24 : pg_snapshot *snap = (pg_snapshot *) PG_GETARG_VARLENA_P(0);
586 : :
587 : 24 : PG_RETURN_FULLTRANSACTIONID(snap->xmax);
588 : : }
589 : :
590 : : /*
591 : : * pg_snapshot_xip(pg_snapshot) returns setof xid8
592 : : *
593 : : * return in-progress xid8s in snapshot.
594 : : */
595 : : Datum
596 : 246 : pg_snapshot_xip(PG_FUNCTION_ARGS)
597 : : {
598 : : FuncCallContext *fctx;
599 : : pg_snapshot *snap;
600 : : FullTransactionId value;
601 : :
602 : : /* on first call initialize fctx and get copy of snapshot */
6557 bruce@momjian.us 603 [ + + ]: 246 : if (SRF_IS_FIRSTCALL())
604 : : {
2030 tmunro@postgresql.or 605 : 24 : pg_snapshot *arg = (pg_snapshot *) PG_GETARG_VARLENA_P(0);
606 : :
6590 tgl@sss.pgh.pa.us 607 : 24 : fctx = SRF_FIRSTCALL_INIT();
608 : :
609 : : /* make a copy of user snapshot */
610 : 24 : snap = MemoryContextAlloc(fctx->multi_call_memory_ctx, VARSIZE(arg));
611 : 24 : memcpy(snap, arg, VARSIZE(arg));
612 : :
613 : 24 : fctx->user_fctx = snap;
614 : : }
615 : :
616 : : /* return values one-by-one */
617 : 246 : fctx = SRF_PERCALL_SETUP();
618 : 246 : snap = fctx->user_fctx;
6557 bruce@momjian.us 619 [ + + ]: 246 : if (fctx->call_cntr < snap->nxip)
620 : : {
6590 tgl@sss.pgh.pa.us 621 : 222 : value = snap->xip[fctx->call_cntr];
2030 tmunro@postgresql.or 622 : 222 : SRF_RETURN_NEXT(fctx, FullTransactionIdGetDatum(value));
623 : : }
624 : : else
625 : : {
6590 tgl@sss.pgh.pa.us 626 : 24 : SRF_RETURN_DONE(fctx);
627 : : }
628 : : }
629 : :
630 : : /*
631 : : * Report the status of a recent transaction ID, or null for wrapped,
632 : : * truncated away or otherwise too old XIDs.
633 : : *
634 : : * The passed epoch-qualified xid is treated as a normal xid, not a
635 : : * multixact id.
636 : : *
637 : : * If it points to a committed subxact the result is the subxact status even
638 : : * though the parent xact may still be in progress or may have aborted.
639 : : */
640 : : Datum
2030 tmunro@postgresql.or 641 : 42 : pg_xact_status(PG_FUNCTION_ARGS)
642 : : {
643 : : const char *status;
644 : 42 : FullTransactionId fxid = PG_GETARG_FULLTRANSACTIONID(0);
645 : : TransactionId xid;
646 : :
647 : : /*
648 : : * We must protect against concurrent truncation of clog entries to avoid
649 : : * an I/O error on SLRU lookup.
650 : : */
1992 tgl@sss.pgh.pa.us 651 : 42 : LWLockAcquire(XactTruncationLock, LW_SHARED);
2030 tmunro@postgresql.or 652 [ + + ]: 42 : if (TransactionIdInRecentPast(fxid, &xid))
653 : : {
3140 rhaas@postgresql.org 654 [ - + ]: 30 : Assert(TransactionIdIsValid(xid));
655 : :
656 : : /*
657 : : * Like when doing visibility checks on a row, check whether the
658 : : * transaction is still in progress before looking into the CLOG.
659 : : * Otherwise we would incorrectly return "committed" for a transaction
660 : : * that is committing and has already updated the CLOG, but hasn't
661 : : * removed its XID from the proc array yet. (See comment on that race
662 : : * condition at the top of heapam_visibility.c)
663 : : */
1219 heikki.linnakangas@i 664 [ + + ]: 30 : if (TransactionIdIsInProgress(xid))
2969 peter_e@gmx.net 665 : 6 : status = "in progress";
3140 rhaas@postgresql.org 666 [ + + ]: 24 : else if (TransactionIdDidCommit(xid))
2969 peter_e@gmx.net 667 : 18 : status = "committed";
668 : : else
669 : : {
670 : : /* it must have aborted or crashed */
1219 heikki.linnakangas@i 671 : 6 : status = "aborted";
672 : : }
673 : : }
674 : : else
675 : : {
3140 rhaas@postgresql.org 676 : 6 : status = NULL;
677 : : }
1992 tgl@sss.pgh.pa.us 678 : 36 : LWLockRelease(XactTruncationLock);
679 : :
3140 rhaas@postgresql.org 680 [ + + ]: 36 : if (status == NULL)
681 : 6 : PG_RETURN_NULL();
682 : : else
683 : 30 : PG_RETURN_TEXT_P(cstring_to_text(status));
684 : : }
|