LCOV - code coverage report
Current view: top level - src/backend/utils/adt - xid8funcs.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 77.6 % 205 159
Test Date: 2026-02-28 13:14:45 Functions: 90.0 % 20 18
Legend: Lines:     hit not hit

            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-2026, 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
      98           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              : 
     105           42 :     now_fullxid = ReadNextFullTransactionId();
     106              : 
     107           42 :     if (extracted_xid != NULL)
     108           42 :         *extracted_xid = xid;
     109              : 
     110           42 :     if (!TransactionIdIsValid(xid))
     111            0 :         return false;
     112              : 
     113              :     /* For non-normal transaction IDs, we can ignore the epoch. */
     114           42 :     if (!TransactionIdIsNormal(xid))
     115           12 :         return true;
     116              : 
     117              :     /* If the transaction ID is in the future, throw an error. */
     118           30 :     if (!FullTransactionIdPrecedes(fxid, now_fullxid))
     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              :      */
     131              :     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              :      */
     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
     154         1378 : cmp_fxid(const void *aa, const void *bb)
     155              : {
     156         1378 :     FullTransactionId a = *(const FullTransactionId *) aa;
     157         1378 :     FullTransactionId b = *(const FullTransactionId *) bb;
     158              : 
     159         1378 :     if (FullTransactionIdPrecedes(a, b))
     160          329 :         return -1;
     161         1049 :     if (FullTransactionIdPrecedes(b, a))
     162          863 :         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
     174           20 : sort_snapshot(pg_snapshot *snap)
     175              : {
     176           20 :     if (snap->nxip > 1)
     177              :     {
     178            6 :         qsort(snap->xip, snap->nxip, sizeof(FullTransactionId), cmp_fxid);
     179            6 :         snap->nxip = qunique(snap->xip, snap->nxip, sizeof(FullTransactionId),
     180              :                              cmp_fxid);
     181              :     }
     182           20 : }
     183              : 
     184              : /*
     185              :  * check fxid visibility.
     186              :  */
     187              : static bool
     188          510 : is_visible_fxid(FullTransactionId value, const pg_snapshot *snap)
     189              : {
     190          510 :     if (FullTransactionIdPrecedes(value, snap->xmin))
     191           66 :         return true;
     192          444 :     else if (!FullTransactionIdPrecedes(value, snap->xmax))
     193           84 :         return false;
     194              : #ifdef USE_BSEARCH_IF_NXIP_GREATER
     195          360 :     else if (snap->nxip > USE_BSEARCH_IF_NXIP_GREATER)
     196              :     {
     197              :         const void *res;
     198              : 
     199          300 :         res = bsearch(&value, snap->xip, snap->nxip, sizeof(FullTransactionId),
     200              :                       cmp_fxid);
     201              :         /* if found, transaction is still in progress */
     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              :         {
     211          144 :             if (FullTransactionIdEquals(value, snap->xip[i]))
     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
     223           93 : buf_init(FullTransactionId xmin, FullTransactionId xmax)
     224              : {
     225              :     pg_snapshot snap;
     226              :     StringInfo  buf;
     227              : 
     228           93 :     snap.xmin = xmin;
     229           93 :     snap.xmax = xmax;
     230           93 :     snap.nxip = 0;
     231              : 
     232           93 :     buf = makeStringInfo();
     233           93 :     appendBinaryStringInfo(buf, &snap, PG_SNAPSHOT_SIZE(0));
     234           93 :     return buf;
     235              : }
     236              : 
     237              : static void
     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 */
     243          312 :     snap->nxip++;
     244              : 
     245          312 :     appendBinaryStringInfo(buf, &fxid, sizeof(fxid));
     246          312 : }
     247              : 
     248              : static pg_snapshot *
     249           75 : buf_finalize(StringInfo buf)
     250              : {
     251           75 :     pg_snapshot *snap = (pg_snapshot *) buf->data;
     252              : 
     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 *
     266          117 : parse_snapshot(const char *str, Node *escontext)
     267              : {
     268              :     FullTransactionId xmin;
     269              :     FullTransactionId xmax;
     270          117 :     FullTransactionId last_val = InvalidFullTransactionId;
     271              :     FullTransactionId val;
     272          117 :     const char *str_start = str;
     273              :     char       *endp;
     274              :     StringInfo  buf;
     275              : 
     276          117 :     xmin = FullTransactionIdFromU64(strtou64(str, &endp, 10));
     277          117 :     if (*endp != ':')
     278            0 :         goto bad_format;
     279          117 :     str = endp + 1;
     280              : 
     281          117 :     xmax = FullTransactionIdFromU64(strtou64(str, &endp, 10));
     282          117 :     if (*endp != ':')
     283            0 :         goto bad_format;
     284          117 :     str = endp + 1;
     285              : 
     286              :     /* it should look sane */
     287          117 :     if (!FullTransactionIdIsValid(xmin) ||
     288          111 :         !FullTransactionIdIsValid(xmax) ||
     289          105 :         FullTransactionIdPrecedes(xmax, xmin))
     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 */
     299          336 :         val = FullTransactionIdFromU64(strtou64(str, &endp, 10));
     300          336 :         str = endp;
     301              : 
     302              :         /* require the input to be in order */
     303          336 :         if (FullTransactionIdPrecedes(val, xmin) ||
     304          330 :             FullTransactionIdFollowsOrEquals(val, xmax) ||
     305          330 :             FullTransactionIdPrecedes(val, last_val))
     306           18 :             goto bad_format;
     307              : 
     308              :         /* skip duplicates */
     309          318 :         if (!FullTransactionIdEquals(val, last_val))
     310          312 :             buf_add_txid(buf, val);
     311          318 :         last_val = val;
     312              : 
     313          318 :         if (*str == ',')
     314          258 :             str++;
     315           60 :         else if (*str != '\0')
     316            0 :             goto bad_format;
     317              :     }
     318              : 
     319           75 :     return buf_finalize(buf);
     320              : 
     321           42 : bad_format:
     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
     335         2873 : 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         2873 :     PreventCommandDuringRecovery("pg_current_xact_id()");
     344              : 
     345         2873 :     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))
     358            6 :         PG_RETURN_NULL();
     359              : 
     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           20 : pg_current_snapshot(PG_FUNCTION_ARGS)
     372              : {
     373              :     pg_snapshot *snap;
     374              :     uint32      nxip,
     375              :                 i;
     376              :     Snapshot    cur;
     377           20 :     FullTransactionId next_fxid = ReadNextFullTransactionId();
     378              : 
     379           20 :     cur = GetActiveSnapshot();
     380           20 :     if (cur == NULL)
     381            0 :         elog(ERROR, "no active snapshot set");
     382              : 
     383              :     /* allocate */
     384           20 :     nxip = cur->xcnt;
     385           20 :     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              :      */
     393           20 :     snap->xmin = FullTransactionIdFromAllowableAt(next_fxid, cur->xmin);
     394           20 :     snap->xmax = FullTransactionIdFromAllowableAt(next_fxid, cur->xmax);
     395           20 :     snap->nxip = nxip;
     396           45 :     for (i = 0; i < nxip; i++)
     397              :         snap->xip[i] =
     398           25 :             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              :      */
     407           20 :     sort_snapshot(snap);
     408              : 
     409              :     /* set size after sorting, because it may have removed duplicate xips */
     410           20 :     SET_VARSIZE(snap, PG_SNAPSHOT_SIZE(snap->nxip));
     411              : 
     412           20 :     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
     421          117 : pg_snapshot_in(PG_FUNCTION_ARGS)
     422              : {
     423          117 :     char       *str = PG_GETARG_CSTRING(0);
     424              :     pg_snapshot *snap;
     425              : 
     426          117 :     snap = parse_snapshot(str, fcinfo->context);
     427              : 
     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
     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              : 
     443           62 :     initStringInfo(&str);
     444              : 
     445           62 :     appendStringInfo(&str, UINT64_FORMAT ":",
     446              :                      U64FromFullTransactionId(snap->xmin));
     447           62 :     appendStringInfo(&str, UINT64_FORMAT ":",
     448              :                      U64FromFullTransactionId(snap->xmax));
     449              : 
     450          344 :     for (i = 0; i < snap->nxip; i++)
     451              :     {
     452          282 :         if (i > 0)
     453          232 :             appendStringInfoChar(&str, ',');
     454          282 :         appendStringInfo(&str, UINT64_FORMAT,
     455              :                          U64FromFullTransactionId(snap->xip[i]));
     456              :     }
     457              : 
     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
     469            0 : pg_snapshot_recv(PG_FUNCTION_ARGS)
     470              : {
     471            0 :     StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
     472              :     pg_snapshot *snap;
     473            0 :     FullTransactionId last = InvalidFullTransactionId;
     474              :     int         nxip;
     475              :     int         i;
     476              :     FullTransactionId xmin;
     477              :     FullTransactionId xmax;
     478              : 
     479              :     /* load and validate nxip */
     480            0 :     nxip = pq_getmsgint(buf, 4);
     481            0 :     if (nxip < 0 || nxip > PG_SNAPSHOT_MAX_NXIP)
     482            0 :         goto bad_format;
     483              : 
     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))
     489            0 :         goto bad_format;
     490              : 
     491            0 :     snap = palloc(PG_SNAPSHOT_SIZE(nxip));
     492            0 :     snap->xmin = xmin;
     493            0 :     snap->xmax = xmax;
     494              : 
     495            0 :     for (i = 0; i < nxip; i++)
     496              :     {
     497              :         FullTransactionId cur =
     498            0 :             FullTransactionIdFromU64((uint64) pq_getmsgint64(buf));
     499              : 
     500            0 :         if (FullTransactionIdPrecedes(cur, last) ||
     501            0 :             FullTransactionIdPrecedes(cur, xmin) ||
     502            0 :             FullTransactionIdPrecedes(xmax, cur))
     503            0 :             goto bad_format;
     504              : 
     505              :         /* skip duplicate xips */
     506            0 :         if (FullTransactionIdEquals(cur, last))
     507              :         {
     508            0 :             i--;
     509            0 :             nxip--;
     510            0 :             continue;
     511              :         }
     512              : 
     513            0 :         snap->xip[i] = cur;
     514            0 :         last = cur;
     515              :     }
     516            0 :     snap->nxip = nxip;
     517            0 :     SET_VARSIZE(snap, PG_SNAPSHOT_SIZE(nxip));
     518            0 :     PG_RETURN_POINTER(snap);
     519              : 
     520            0 : bad_format:
     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
     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              : 
     541            0 :     pq_begintypsend(&buf);
     542            0 :     pq_sendint32(&buf, snap->nxip);
     543            0 :     pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xmin));
     544            0 :     pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xmax));
     545            0 :     for (i = 0; i < snap->nxip; i++)
     546            0 :         pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xip[i]));
     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
     556          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           38 : pg_snapshot_xmin(PG_FUNCTION_ARGS)
     571              : {
     572           38 :     pg_snapshot *snap = (pg_snapshot *) PG_GETARG_VARLENA_P(0);
     573              : 
     574           38 :     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 */
     603          246 :     if (SRF_IS_FIRSTCALL())
     604              :     {
     605           24 :         pg_snapshot *arg = (pg_snapshot *) PG_GETARG_VARLENA_P(0);
     606              : 
     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;
     619          246 :     if (fctx->call_cntr < snap->nxip)
     620              :     {
     621          222 :         value = snap->xip[fctx->call_cntr];
     622          222 :         SRF_RETURN_NEXT(fctx, FullTransactionIdGetDatum(value));
     623              :     }
     624              :     else
     625              :     {
     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
     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              :      */
     651           42 :     LWLockAcquire(XactTruncationLock, LW_SHARED);
     652           42 :     if (TransactionIdInRecentPast(fxid, &xid))
     653              :     {
     654              :         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              :          */
     664           30 :         if (TransactionIdIsInProgress(xid))
     665            6 :             status = "in progress";
     666           24 :         else if (TransactionIdDidCommit(xid))
     667           18 :             status = "committed";
     668              :         else
     669              :         {
     670              :             /* it must have aborted or crashed */
     671            6 :             status = "aborted";
     672              :         }
     673              :     }
     674              :     else
     675              :     {
     676            6 :         status = NULL;
     677              :     }
     678           36 :     LWLockRelease(XactTruncationLock);
     679              : 
     680           36 :     if (status == NULL)
     681            6 :         PG_RETURN_NULL();
     682              :     else
     683           30 :         PG_RETURN_TEXT_P(cstring_to_text(status));
     684              : }
        

Generated by: LCOV version 2.0-1