LCOV - code coverage report
Current view: top level - src/backend/utils/adt - xid8funcs.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 168 218 77.1 %
Date: 2023-12-10 05:11:07 Functions: 19 21 90.5 %
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-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
      99          84 : TransactionIdInRecentPast(FullTransactionId fxid, TransactionId *extracted_xid)
     100             : {
     101          84 :     uint32      xid_epoch = EpochFromFullTransactionId(fxid);
     102          84 :     TransactionId xid = XidFromFullTransactionId(fxid);
     103             :     uint32      now_epoch;
     104             :     TransactionId now_epoch_next_xid;
     105             :     FullTransactionId now_fullxid;
     106             : 
     107          84 :     now_fullxid = ReadNextFullTransactionId();
     108          84 :     now_epoch_next_xid = XidFromFullTransactionId(now_fullxid);
     109          84 :     now_epoch = EpochFromFullTransactionId(now_fullxid);
     110             : 
     111          84 :     if (extracted_xid != NULL)
     112          84 :         *extracted_xid = xid;
     113             : 
     114          84 :     if (!TransactionIdIsValid(xid))
     115           0 :         return false;
     116             : 
     117             :     /* For non-normal transaction IDs, we can ignore the epoch. */
     118          84 :     if (!TransactionIdIsNormal(xid))
     119          24 :         return true;
     120             : 
     121             :     /* If the transaction ID is in the future, throw an error. */
     122          60 :     if (!FullTransactionIdPrecedes(fxid, now_fullxid))
     123          12 :         ereport(ERROR,
     124             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     125             :                  errmsg("transaction ID %llu is in the future",
     126             :                         (unsigned long long) U64FromFullTransactionId(fxid))));
     127             : 
     128             :     /*
     129             :      * TransamVariables->oldestClogXid is protected by XactTruncationLock, but
     130             :      * 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             :      */
     135             :     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             :      * TransamVariables->oldestClogXid to determine whether the relevant CLOG
     141             :      * entry is guaranteed to still exist.
     142             :      */
     143          48 :     if (xid_epoch + 1 < now_epoch
     144          48 :         || (xid_epoch + 1 == now_epoch && xid < now_epoch_next_xid)
     145          48 :         || TransactionIdPrecedes(xid, TransamVariables->oldestClogXid))
     146          12 :         return false;
     147             : 
     148          36 :     return true;
     149             : }
     150             : 
     151             : /*
     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.
     156             :  */
     157             : static FullTransactionId
     158          60 : widen_snapshot_xid(TransactionId xid, FullTransactionId next_fxid)
     159             : {
     160          60 :     TransactionId next_xid = XidFromFullTransactionId(next_fxid);
     161          60 :     uint32      epoch = EpochFromFullTransactionId(next_fxid);
     162             : 
     163             :     /* Special transaction ID. */
     164          60 :     if (!TransactionIdIsNormal(xid))
     165           0 :         return FullTransactionIdFromEpochAndXid(0, xid);
     166             : 
     167             :     /*
     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.
     173             :      */
     174          60 :     if (xid > next_xid)
     175           0 :         epoch--;
     176             : 
     177          60 :     return FullTransactionIdFromEpochAndXid(epoch, xid);
     178             : }
     179             : 
     180             : /*
     181             :  * txid comparator for qsort/bsearch
     182             :  */
     183             : static int
     184        2688 : cmp_fxid(const void *aa, const void *bb)
     185             : {
     186        2688 :     FullTransactionId a = *(const FullTransactionId *) aa;
     187        2688 :     FullTransactionId b = *(const FullTransactionId *) bb;
     188             : 
     189        2688 :     if (FullTransactionIdPrecedes(a, b))
     190         648 :         return -1;
     191        2040 :     if (FullTransactionIdPrecedes(b, a))
     192        1668 :         return 1;
     193         372 :     return 0;
     194             : }
     195             : 
     196             : /*
     197             :  * Sort a snapshot's txids, so we can use bsearch() later.  Also remove
     198             :  * any duplicates.
     199             :  *
     200             :  * For consistency of on-disk representation, we always sort even if bsearch
     201             :  * will not be used.
     202             :  */
     203             : static void
     204          24 : sort_snapshot(pg_snapshot *snap)
     205             : {
     206          24 :     if (snap->nxip > 1)
     207             :     {
     208           0 :         qsort(snap->xip, snap->nxip, sizeof(FullTransactionId), cmp_fxid);
     209           0 :         snap->nxip = qunique(snap->xip, snap->nxip, sizeof(FullTransactionId),
     210             :                              cmp_fxid);
     211             :     }
     212          24 : }
     213             : 
     214             : /*
     215             :  * check fxid visibility.
     216             :  */
     217             : static bool
     218        1020 : is_visible_fxid(FullTransactionId value, const pg_snapshot *snap)
     219             : {
     220        1020 :     if (FullTransactionIdPrecedes(value, snap->xmin))
     221         132 :         return true;
     222         888 :     else if (!FullTransactionIdPrecedes(value, snap->xmax))
     223         168 :         return false;
     224             : #ifdef USE_BSEARCH_IF_NXIP_GREATER
     225         720 :     else if (snap->nxip > USE_BSEARCH_IF_NXIP_GREATER)
     226             :     {
     227             :         void       *res;
     228             : 
     229         600 :         res = bsearch(&value, snap->xip, snap->nxip, sizeof(FullTransactionId),
     230             :                       cmp_fxid);
     231             :         /* if found, transaction is still in progress */
     232         600 :         return (res) ? false : true;
     233             :     }
     234             : #endif
     235             :     else
     236             :     {
     237             :         uint32      i;
     238             : 
     239         360 :         for (i = 0; i < snap->nxip; i++)
     240             :         {
     241         288 :             if (FullTransactionIdEquals(value, snap->xip[i]))
     242          48 :                 return false;
     243             :         }
     244          72 :         return true;
     245             :     }
     246             : }
     247             : 
     248             : /*
     249             :  * helper functions to use StringInfo for pg_snapshot creation.
     250             :  */
     251             : 
     252             : static StringInfo
     253         186 : buf_init(FullTransactionId xmin, FullTransactionId xmax)
     254             : {
     255             :     pg_snapshot snap;
     256             :     StringInfo  buf;
     257             : 
     258         186 :     snap.xmin = xmin;
     259         186 :     snap.xmax = xmax;
     260         186 :     snap.nxip = 0;
     261             : 
     262         186 :     buf = makeStringInfo();
     263         186 :     appendBinaryStringInfo(buf, &snap, PG_SNAPSHOT_SIZE(0));
     264         186 :     return buf;
     265             : }
     266             : 
     267             : static void
     268         624 : buf_add_txid(StringInfo buf, FullTransactionId fxid)
     269             : {
     270         624 :     pg_snapshot *snap = (pg_snapshot *) buf->data;
     271             : 
     272             :     /* do this before possible realloc */
     273         624 :     snap->nxip++;
     274             : 
     275         624 :     appendBinaryStringInfo(buf, &fxid, sizeof(fxid));
     276         624 : }
     277             : 
     278             : static pg_snapshot *
     279         150 : buf_finalize(StringInfo buf)
     280             : {
     281         150 :     pg_snapshot *snap = (pg_snapshot *) buf->data;
     282             : 
     283         150 :     SET_VARSIZE(snap, buf->len);
     284             : 
     285             :     /* buf is not needed anymore */
     286         150 :     buf->data = NULL;
     287         150 :     pfree(buf);
     288             : 
     289         150 :     return snap;
     290             : }
     291             : 
     292             : /*
     293             :  * parse snapshot from cstring
     294             :  */
     295             : static pg_snapshot *
     296         234 : parse_snapshot(const char *str, Node *escontext)
     297             : {
     298             :     FullTransactionId xmin;
     299             :     FullTransactionId xmax;
     300         234 :     FullTransactionId last_val = InvalidFullTransactionId;
     301             :     FullTransactionId val;
     302         234 :     const char *str_start = str;
     303             :     char       *endp;
     304             :     StringInfo  buf;
     305             : 
     306         234 :     xmin = FullTransactionIdFromU64(strtou64(str, &endp, 10));
     307         234 :     if (*endp != ':')
     308           0 :         goto bad_format;
     309         234 :     str = endp + 1;
     310             : 
     311         234 :     xmax = FullTransactionIdFromU64(strtou64(str, &endp, 10));
     312         234 :     if (*endp != ':')
     313           0 :         goto bad_format;
     314         234 :     str = endp + 1;
     315             : 
     316             :     /* it should look sane */
     317         234 :     if (!FullTransactionIdIsValid(xmin) ||
     318         222 :         !FullTransactionIdIsValid(xmax) ||
     319         210 :         FullTransactionIdPrecedes(xmax, xmin))
     320          48 :         goto bad_format;
     321             : 
     322             :     /* allocate buffer */
     323         186 :     buf = buf_init(xmin, xmax);
     324             : 
     325             :     /* loop over values */
     326         822 :     while (*str != '\0')
     327             :     {
     328             :         /* read next value */
     329         672 :         val = FullTransactionIdFromU64(strtou64(str, &endp, 10));
     330         672 :         str = endp;
     331             : 
     332             :         /* require the input to be in order */
     333         672 :         if (FullTransactionIdPrecedes(val, xmin) ||
     334         660 :             FullTransactionIdFollowsOrEquals(val, xmax) ||
     335         660 :             FullTransactionIdPrecedes(val, last_val))
     336          36 :             goto bad_format;
     337             : 
     338             :         /* skip duplicates */
     339         636 :         if (!FullTransactionIdEquals(val, last_val))
     340         624 :             buf_add_txid(buf, val);
     341         636 :         last_val = val;
     342             : 
     343         636 :         if (*str == ',')
     344         516 :             str++;
     345         120 :         else if (*str != '\0')
     346           0 :             goto bad_format;
     347             :     }
     348             : 
     349         150 :     return buf_finalize(buf);
     350             : 
     351          84 : bad_format:
     352          84 :     ereturn(escontext, NULL,
     353             :             (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     354             :              errmsg("invalid input syntax for type %s: \"%s\"",
     355             :                     "pg_snapshot", str_start)));
     356             : }
     357             : 
     358             : /*
     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
     365        5706 : 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.
     372             :      */
     373        5706 :     PreventCommandDuringRecovery("pg_current_xact_id()");
     374             : 
     375        5706 :     PG_RETURN_FULLTRANSACTIONID(GetTopFullTransactionId());
     376             : }
     377             : 
     378             : /*
     379             :  * Same as pg_current_xact_id() but doesn't assign a new xid if there
     380             :  * isn't one yet.
     381             :  */
     382             : Datum
     383          24 : pg_current_xact_id_if_assigned(PG_FUNCTION_ARGS)
     384             : {
     385          24 :     FullTransactionId topfxid = GetTopFullTransactionIdIfAny();
     386             : 
     387          24 :     if (!FullTransactionIdIsValid(topfxid))
     388          12 :         PG_RETURN_NULL();
     389             : 
     390          12 :     PG_RETURN_FULLTRANSACTIONID(topfxid);
     391             : }
     392             : 
     393             : /*
     394             :  * pg_current_snapshot() returns pg_snapshot
     395             :  *
     396             :  *      Return current snapshot
     397             :  *
     398             :  * Note that only top-transaction XIDs are included in the snapshot.
     399             :  */
     400             : Datum
     401          24 : pg_current_snapshot(PG_FUNCTION_ARGS)
     402             : {
     403             :     pg_snapshot *snap;
     404             :     uint32      nxip,
     405             :                 i;
     406             :     Snapshot    cur;
     407          24 :     FullTransactionId next_fxid = ReadNextFullTransactionId();
     408             : 
     409          24 :     cur = GetActiveSnapshot();
     410          24 :     if (cur == NULL)
     411           0 :         elog(ERROR, "no active snapshot set");
     412             : 
     413             :     /* allocate */
     414          24 :     nxip = cur->xcnt;
     415          24 :     snap = palloc(PG_SNAPSHOT_SIZE(nxip));
     416             : 
     417             :     /* fill */
     418          24 :     snap->xmin = widen_snapshot_xid(cur->xmin, next_fxid);
     419          24 :     snap->xmax = widen_snapshot_xid(cur->xmax, next_fxid);
     420          24 :     snap->nxip = nxip;
     421          36 :     for (i = 0; i < nxip; i++)
     422          12 :         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             :      */
     431          24 :     sort_snapshot(snap);
     432             : 
     433             :     /* set size after sorting, because it may have removed duplicate xips */
     434          24 :     SET_VARSIZE(snap, PG_SNAPSHOT_SIZE(snap->nxip));
     435             : 
     436          24 :     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
     445         234 : pg_snapshot_in(PG_FUNCTION_ARGS)
     446             : {
     447         234 :     char       *str = PG_GETARG_CSTRING(0);
     448             :     pg_snapshot *snap;
     449             : 
     450         234 :     snap = parse_snapshot(str, fcinfo->context);
     451             : 
     452         174 :     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
     461         124 : pg_snapshot_out(PG_FUNCTION_ARGS)
     462             : {
     463         124 :     pg_snapshot *snap = (pg_snapshot *) PG_GETARG_VARLENA_P(0);
     464             :     StringInfoData str;
     465             :     uint32      i;
     466             : 
     467         124 :     initStringInfo(&str);
     468             : 
     469         124 :     appendStringInfo(&str, UINT64_FORMAT ":",
     470             :                      U64FromFullTransactionId(snap->xmin));
     471         124 :     appendStringInfo(&str, UINT64_FORMAT ":",
     472             :                      U64FromFullTransactionId(snap->xmax));
     473             : 
     474         688 :     for (i = 0; i < snap->nxip; i++)
     475             :     {
     476         564 :         if (i > 0)
     477         464 :             appendStringInfoChar(&str, ',');
     478         564 :         appendStringInfo(&str, UINT64_FORMAT,
     479             :                          U64FromFullTransactionId(snap->xip[i]));
     480             :     }
     481             : 
     482         124 :     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
     493           0 : pg_snapshot_recv(PG_FUNCTION_ARGS)
     494             : {
     495           0 :     StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
     496             :     pg_snapshot *snap;
     497           0 :     FullTransactionId last = InvalidFullTransactionId;
     498             :     int         nxip;
     499             :     int         i;
     500             :     FullTransactionId xmin;
     501             :     FullTransactionId xmax;
     502             : 
     503             :     /* load and validate nxip */
     504           0 :     nxip = pq_getmsgint(buf, 4);
     505           0 :     if (nxip < 0 || nxip > PG_SNAPSHOT_MAX_NXIP)
     506           0 :         goto bad_format;
     507             : 
     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))
     513           0 :         goto bad_format;
     514             : 
     515           0 :     snap = palloc(PG_SNAPSHOT_SIZE(nxip));
     516           0 :     snap->xmin = xmin;
     517           0 :     snap->xmax = xmax;
     518             : 
     519           0 :     for (i = 0; i < nxip; i++)
     520             :     {
     521             :         FullTransactionId cur =
     522           0 :             FullTransactionIdFromU64((uint64) pq_getmsgint64(buf));
     523             : 
     524           0 :         if (FullTransactionIdPrecedes(cur, last) ||
     525           0 :             FullTransactionIdPrecedes(cur, xmin) ||
     526           0 :             FullTransactionIdPrecedes(xmax, cur))
     527           0 :             goto bad_format;
     528             : 
     529             :         /* skip duplicate xips */
     530           0 :         if (FullTransactionIdEquals(cur, last))
     531             :         {
     532           0 :             i--;
     533           0 :             nxip--;
     534           0 :             continue;
     535             :         }
     536             : 
     537           0 :         snap->xip[i] = cur;
     538           0 :         last = cur;
     539             :     }
     540           0 :     snap->nxip = nxip;
     541           0 :     SET_VARSIZE(snap, PG_SNAPSHOT_SIZE(nxip));
     542           0 :     PG_RETURN_POINTER(snap);
     543             : 
     544           0 : bad_format:
     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
     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             : 
     565           0 :     pq_begintypsend(&buf);
     566           0 :     pq_sendint32(&buf, snap->nxip);
     567           0 :     pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xmin));
     568           0 :     pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xmax));
     569           0 :     for (i = 0; i < snap->nxip; i++)
     570           0 :         pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xip[i]));
     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
     580        1020 : pg_visible_in_snapshot(PG_FUNCTION_ARGS)
     581             : {
     582        1020 :     FullTransactionId value = PG_GETARG_FULLTRANSACTIONID(0);
     583        1020 :     pg_snapshot *snap = (pg_snapshot *) PG_GETARG_VARLENA_P(1);
     584             : 
     585        1020 :     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          60 : pg_snapshot_xmin(PG_FUNCTION_ARGS)
     595             : {
     596          60 :     pg_snapshot *snap = (pg_snapshot *) PG_GETARG_VARLENA_P(0);
     597             : 
     598          60 :     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          48 : pg_snapshot_xmax(PG_FUNCTION_ARGS)
     608             : {
     609          48 :     pg_snapshot *snap = (pg_snapshot *) PG_GETARG_VARLENA_P(0);
     610             : 
     611          48 :     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         492 : 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 */
     627         492 :     if (SRF_IS_FIRSTCALL())
     628             :     {
     629          48 :         pg_snapshot *arg = (pg_snapshot *) PG_GETARG_VARLENA_P(0);
     630             : 
     631          48 :         fctx = SRF_FIRSTCALL_INIT();
     632             : 
     633             :         /* make a copy of user snapshot */
     634          48 :         snap = MemoryContextAlloc(fctx->multi_call_memory_ctx, VARSIZE(arg));
     635          48 :         memcpy(snap, arg, VARSIZE(arg));
     636             : 
     637          48 :         fctx->user_fctx = snap;
     638             :     }
     639             : 
     640             :     /* return values one-by-one */
     641         492 :     fctx = SRF_PERCALL_SETUP();
     642         492 :     snap = fctx->user_fctx;
     643         492 :     if (fctx->call_cntr < snap->nxip)
     644             :     {
     645         444 :         value = snap->xip[fctx->call_cntr];
     646         444 :         SRF_RETURN_NEXT(fctx, FullTransactionIdGetDatum(value));
     647             :     }
     648             :     else
     649             :     {
     650          48 :         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
     665          84 : pg_xact_status(PG_FUNCTION_ARGS)
     666             : {
     667             :     const char *status;
     668          84 :     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             :      */
     675          84 :     LWLockAcquire(XactTruncationLock, LW_SHARED);
     676          84 :     if (TransactionIdInRecentPast(fxid, &xid))
     677             :     {
     678             :         Assert(TransactionIdIsValid(xid));
     679             : 
     680             :         /*
     681             :          * Like when doing visibility 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             :          */
     688          60 :         if (TransactionIdIsInProgress(xid))
     689          12 :             status = "in progress";
     690          48 :         else if (TransactionIdDidCommit(xid))
     691          36 :             status = "committed";
     692             :         else
     693             :         {
     694             :             /* it must have aborted or crashed */
     695          12 :             status = "aborted";
     696             :         }
     697             :     }
     698             :     else
     699             :     {
     700          12 :         status = NULL;
     701             :     }
     702          72 :     LWLockRelease(XactTruncationLock);
     703             : 
     704          72 :     if (status == NULL)
     705          12 :         PG_RETURN_NULL();
     706             :     else
     707          60 :         PG_RETURN_TEXT_P(cstring_to_text(status));
     708             : }

Generated by: LCOV version 1.14