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

Generated by: LCOV version 1.14