LCOV - code coverage report
Current view: top level - src/backend/utils/adt - xid8funcs.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 159 205 77.6 %
Date: 2025-10-24 12:17:48 Functions: 18 20 90.0 %
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-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
      98          84 : TransactionIdInRecentPast(FullTransactionId fxid, TransactionId *extracted_xid)
      99             : {
     100          84 :     TransactionId xid = XidFromFullTransactionId(fxid);
     101             :     FullTransactionId now_fullxid;
     102             :     TransactionId oldest_clog_xid;
     103             :     FullTransactionId oldest_clog_fxid;
     104             : 
     105          84 :     now_fullxid = ReadNextFullTransactionId();
     106             : 
     107          84 :     if (extracted_xid != NULL)
     108          84 :         *extracted_xid = xid;
     109             : 
     110          84 :     if (!TransactionIdIsValid(xid))
     111           0 :         return false;
     112             : 
     113             :     /* For non-normal transaction IDs, we can ignore the epoch. */
     114          84 :     if (!TransactionIdIsNormal(xid))
     115          24 :         return true;
     116             : 
     117             :     /* If the transaction ID is in the future, throw an error. */
     118          60 :     if (!FullTransactionIdPrecedes(fxid, now_fullxid))
     119          12 :         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          48 :     oldest_clog_xid = TransamVariables->oldestClogXid;
     145             :     oldest_clog_fxid =
     146          48 :         FullTransactionIdFromAllowableAt(now_fullxid, oldest_clog_xid);
     147          48 :     return !FullTransactionIdPrecedes(fxid, oldest_clog_fxid);
     148             : }
     149             : 
     150             : /*
     151             :  * txid comparator for qsort/bsearch
     152             :  */
     153             : static int
     154        2786 : cmp_fxid(const void *aa, const void *bb)
     155             : {
     156        2786 :     FullTransactionId a = *(const FullTransactionId *) aa;
     157        2786 :     FullTransactionId b = *(const FullTransactionId *) bb;
     158             : 
     159        2786 :     if (FullTransactionIdPrecedes(a, b))
     160         678 :         return -1;
     161        2108 :     if (FullTransactionIdPrecedes(b, a))
     162        1736 :         return 1;
     163         372 :     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          40 : sort_snapshot(pg_snapshot *snap)
     175             : {
     176          40 :     if (snap->nxip > 1)
     177             :     {
     178          18 :         qsort(snap->xip, snap->nxip, sizeof(FullTransactionId), cmp_fxid);
     179          18 :         snap->nxip = qunique(snap->xip, snap->nxip, sizeof(FullTransactionId),
     180             :                              cmp_fxid);
     181             :     }
     182          40 : }
     183             : 
     184             : /*
     185             :  * check fxid visibility.
     186             :  */
     187             : static bool
     188        1020 : is_visible_fxid(FullTransactionId value, const pg_snapshot *snap)
     189             : {
     190        1020 :     if (FullTransactionIdPrecedes(value, snap->xmin))
     191         132 :         return true;
     192         888 :     else if (!FullTransactionIdPrecedes(value, snap->xmax))
     193         168 :         return false;
     194             : #ifdef USE_BSEARCH_IF_NXIP_GREATER
     195         720 :     else if (snap->nxip > USE_BSEARCH_IF_NXIP_GREATER)
     196             :     {
     197             :         void       *res;
     198             : 
     199         600 :         res = bsearch(&value, snap->xip, snap->nxip, sizeof(FullTransactionId),
     200             :                       cmp_fxid);
     201             :         /* if found, transaction is still in progress */
     202         600 :         return (res) ? false : true;
     203             :     }
     204             : #endif
     205             :     else
     206             :     {
     207             :         uint32      i;
     208             : 
     209         360 :         for (i = 0; i < snap->nxip; i++)
     210             :         {
     211         288 :             if (FullTransactionIdEquals(value, snap->xip[i]))
     212          48 :                 return false;
     213             :         }
     214          72 :         return true;
     215             :     }
     216             : }
     217             : 
     218             : /*
     219             :  * helper functions to use StringInfo for pg_snapshot creation.
     220             :  */
     221             : 
     222             : static StringInfo
     223         186 : buf_init(FullTransactionId xmin, FullTransactionId xmax)
     224             : {
     225             :     pg_snapshot snap;
     226             :     StringInfo  buf;
     227             : 
     228         186 :     snap.xmin = xmin;
     229         186 :     snap.xmax = xmax;
     230         186 :     snap.nxip = 0;
     231             : 
     232         186 :     buf = makeStringInfo();
     233         186 :     appendBinaryStringInfo(buf, &snap, PG_SNAPSHOT_SIZE(0));
     234         186 :     return buf;
     235             : }
     236             : 
     237             : static void
     238         624 : buf_add_txid(StringInfo buf, FullTransactionId fxid)
     239             : {
     240         624 :     pg_snapshot *snap = (pg_snapshot *) buf->data;
     241             : 
     242             :     /* do this before possible realloc */
     243         624 :     snap->nxip++;
     244             : 
     245         624 :     appendBinaryStringInfo(buf, &fxid, sizeof(fxid));
     246         624 : }
     247             : 
     248             : static pg_snapshot *
     249         150 : buf_finalize(StringInfo buf)
     250             : {
     251         150 :     pg_snapshot *snap = (pg_snapshot *) buf->data;
     252             : 
     253         150 :     SET_VARSIZE(snap, buf->len);
     254             : 
     255             :     /* buf is not needed anymore */
     256         150 :     buf->data = NULL;
     257         150 :     pfree(buf);
     258             : 
     259         150 :     return snap;
     260             : }
     261             : 
     262             : /*
     263             :  * parse snapshot from cstring
     264             :  */
     265             : static pg_snapshot *
     266         234 : parse_snapshot(const char *str, Node *escontext)
     267             : {
     268             :     FullTransactionId xmin;
     269             :     FullTransactionId xmax;
     270         234 :     FullTransactionId last_val = InvalidFullTransactionId;
     271             :     FullTransactionId val;
     272         234 :     const char *str_start = str;
     273             :     char       *endp;
     274             :     StringInfo  buf;
     275             : 
     276         234 :     xmin = FullTransactionIdFromU64(strtou64(str, &endp, 10));
     277         234 :     if (*endp != ':')
     278           0 :         goto bad_format;
     279         234 :     str = endp + 1;
     280             : 
     281         234 :     xmax = FullTransactionIdFromU64(strtou64(str, &endp, 10));
     282         234 :     if (*endp != ':')
     283           0 :         goto bad_format;
     284         234 :     str = endp + 1;
     285             : 
     286             :     /* it should look sane */
     287         234 :     if (!FullTransactionIdIsValid(xmin) ||
     288         222 :         !FullTransactionIdIsValid(xmax) ||
     289         210 :         FullTransactionIdPrecedes(xmax, xmin))
     290          48 :         goto bad_format;
     291             : 
     292             :     /* allocate buffer */
     293         186 :     buf = buf_init(xmin, xmax);
     294             : 
     295             :     /* loop over values */
     296         822 :     while (*str != '\0')
     297             :     {
     298             :         /* read next value */
     299         672 :         val = FullTransactionIdFromU64(strtou64(str, &endp, 10));
     300         672 :         str = endp;
     301             : 
     302             :         /* require the input to be in order */
     303         672 :         if (FullTransactionIdPrecedes(val, xmin) ||
     304         660 :             FullTransactionIdFollowsOrEquals(val, xmax) ||
     305         660 :             FullTransactionIdPrecedes(val, last_val))
     306          36 :             goto bad_format;
     307             : 
     308             :         /* skip duplicates */
     309         636 :         if (!FullTransactionIdEquals(val, last_val))
     310         624 :             buf_add_txid(buf, val);
     311         636 :         last_val = val;
     312             : 
     313         636 :         if (*str == ',')
     314         516 :             str++;
     315         120 :         else if (*str != '\0')
     316           0 :             goto bad_format;
     317             :     }
     318             : 
     319         150 :     return buf_finalize(buf);
     320             : 
     321          84 : bad_format:
     322          84 :     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        5742 : 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        5742 :     PreventCommandDuringRecovery("pg_current_xact_id()");
     344             : 
     345        5742 :     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          24 : pg_current_xact_id_if_assigned(PG_FUNCTION_ARGS)
     354             : {
     355          24 :     FullTransactionId topfxid = GetTopFullTransactionIdIfAny();
     356             : 
     357          24 :     if (!FullTransactionIdIsValid(topfxid))
     358          12 :         PG_RETURN_NULL();
     359             : 
     360          12 :     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          40 : pg_current_snapshot(PG_FUNCTION_ARGS)
     372             : {
     373             :     pg_snapshot *snap;
     374             :     uint32      nxip,
     375             :                 i;
     376             :     Snapshot    cur;
     377          40 :     FullTransactionId next_fxid = ReadNextFullTransactionId();
     378             : 
     379          40 :     cur = GetActiveSnapshot();
     380          40 :     if (cur == NULL)
     381           0 :         elog(ERROR, "no active snapshot set");
     382             : 
     383             :     /* allocate */
     384          40 :     nxip = cur->xcnt;
     385          40 :     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          40 :     snap->xmin = FullTransactionIdFromAllowableAt(next_fxid, cur->xmin);
     394          40 :     snap->xmax = FullTransactionIdFromAllowableAt(next_fxid, cur->xmax);
     395          40 :     snap->nxip = nxip;
     396         104 :     for (i = 0; i < nxip; i++)
     397             :         snap->xip[i] =
     398          64 :             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          40 :     sort_snapshot(snap);
     408             : 
     409             :     /* set size after sorting, because it may have removed duplicate xips */
     410          40 :     SET_VARSIZE(snap, PG_SNAPSHOT_SIZE(snap->nxip));
     411             : 
     412          40 :     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         234 : pg_snapshot_in(PG_FUNCTION_ARGS)
     422             : {
     423         234 :     char       *str = PG_GETARG_CSTRING(0);
     424             :     pg_snapshot *snap;
     425             : 
     426         234 :     snap = parse_snapshot(str, fcinfo->context);
     427             : 
     428         174 :     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         124 : pg_snapshot_out(PG_FUNCTION_ARGS)
     438             : {
     439         124 :     pg_snapshot *snap = (pg_snapshot *) PG_GETARG_VARLENA_P(0);
     440             :     StringInfoData str;
     441             :     uint32      i;
     442             : 
     443         124 :     initStringInfo(&str);
     444             : 
     445         124 :     appendStringInfo(&str, UINT64_FORMAT ":",
     446             :                      U64FromFullTransactionId(snap->xmin));
     447         124 :     appendStringInfo(&str, UINT64_FORMAT ":",
     448             :                      U64FromFullTransactionId(snap->xmax));
     449             : 
     450         688 :     for (i = 0; i < snap->nxip; i++)
     451             :     {
     452         564 :         if (i > 0)
     453         464 :             appendStringInfoChar(&str, ',');
     454         564 :         appendStringInfo(&str, UINT64_FORMAT,
     455             :                          U64FromFullTransactionId(snap->xip[i]));
     456             :     }
     457             : 
     458         124 :     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        1020 : pg_visible_in_snapshot(PG_FUNCTION_ARGS)
     557             : {
     558        1020 :     FullTransactionId value = PG_GETARG_FULLTRANSACTIONID(0);
     559        1020 :     pg_snapshot *snap = (pg_snapshot *) PG_GETARG_VARLENA_P(1);
     560             : 
     561        1020 :     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          76 : pg_snapshot_xmin(PG_FUNCTION_ARGS)
     571             : {
     572          76 :     pg_snapshot *snap = (pg_snapshot *) PG_GETARG_VARLENA_P(0);
     573             : 
     574          76 :     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          48 : pg_snapshot_xmax(PG_FUNCTION_ARGS)
     584             : {
     585          48 :     pg_snapshot *snap = (pg_snapshot *) PG_GETARG_VARLENA_P(0);
     586             : 
     587          48 :     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         492 : 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         492 :     if (SRF_IS_FIRSTCALL())
     604             :     {
     605          48 :         pg_snapshot *arg = (pg_snapshot *) PG_GETARG_VARLENA_P(0);
     606             : 
     607          48 :         fctx = SRF_FIRSTCALL_INIT();
     608             : 
     609             :         /* make a copy of user snapshot */
     610          48 :         snap = MemoryContextAlloc(fctx->multi_call_memory_ctx, VARSIZE(arg));
     611          48 :         memcpy(snap, arg, VARSIZE(arg));
     612             : 
     613          48 :         fctx->user_fctx = snap;
     614             :     }
     615             : 
     616             :     /* return values one-by-one */
     617         492 :     fctx = SRF_PERCALL_SETUP();
     618         492 :     snap = fctx->user_fctx;
     619         492 :     if (fctx->call_cntr < snap->nxip)
     620             :     {
     621         444 :         value = snap->xip[fctx->call_cntr];
     622         444 :         SRF_RETURN_NEXT(fctx, FullTransactionIdGetDatum(value));
     623             :     }
     624             :     else
     625             :     {
     626          48 :         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          84 : pg_xact_status(PG_FUNCTION_ARGS)
     642             : {
     643             :     const char *status;
     644          84 :     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          84 :     LWLockAcquire(XactTruncationLock, LW_SHARED);
     652          84 :     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          60 :         if (TransactionIdIsInProgress(xid))
     665          12 :             status = "in progress";
     666          48 :         else if (TransactionIdDidCommit(xid))
     667          36 :             status = "committed";
     668             :         else
     669             :         {
     670             :             /* it must have aborted or crashed */
     671          12 :             status = "aborted";
     672             :         }
     673             :     }
     674             :     else
     675             :     {
     676          12 :         status = NULL;
     677             :     }
     678          72 :     LWLockRelease(XactTruncationLock);
     679             : 
     680          72 :     if (status == NULL)
     681          12 :         PG_RETURN_NULL();
     682             :     else
     683          60 :         PG_RETURN_TEXT_P(cstring_to_text(status));
     684             : }

Generated by: LCOV version 1.16