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