LCOV - code coverage report
Current view: top level - src/backend/utils/adt - xid8funcs.c (source / functions) Hit Total Coverage
Test: PostgreSQL 14devel Lines: 171 222 77.0 %
Date: 2020-07-11 21:06:30 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, pg_current_snapshot and related fmgr functions, since the
      14             :  * only difference between them is whether they expose xid8 or int8 values to
      15             :  * users.  The txid_XXX variants should eventually be dropped.
      16             :  *
      17             :  *
      18             :  *  Copyright (c) 2003-2020, 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 "utils/builtins.h"
      40             : #include "utils/memutils.h"
      41             : #include "utils/snapmgr.h"
      42             : #include "utils/xid8.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             :  * Helper to get a TransactionId from a 64-bit xid with wraparound detection.
      78             :  *
      79             :  * It is an ERROR if the xid is in the future.  Otherwise, returns true if
      80             :  * the transaction is still new enough that we can determine whether it
      81             :  * committed and false otherwise.  If *extracted_xid is not NULL, it is set
      82             :  * to the low 32 bits of the transaction ID (i.e. the actual XID, without the
      83             :  * epoch).
      84             :  *
      85             :  * The caller must hold XactTruncationLock since it's dealing with arbitrary
      86             :  * XIDs, and must continue to hold it until it's done with any clog lookups
      87             :  * relating to those XIDs.
      88             :  */
      89             : static bool
      90          58 : TransactionIdInRecentPast(FullTransactionId fxid, TransactionId *extracted_xid)
      91             : {
      92          58 :     uint32      xid_epoch = EpochFromFullTransactionId(fxid);
      93          58 :     TransactionId xid = XidFromFullTransactionId(fxid);
      94             :     uint32      now_epoch;
      95             :     TransactionId now_epoch_next_xid;
      96             :     FullTransactionId now_fullxid;
      97             : 
      98          58 :     now_fullxid = ReadNextFullTransactionId();
      99          58 :     now_epoch_next_xid = XidFromFullTransactionId(now_fullxid);
     100          58 :     now_epoch = EpochFromFullTransactionId(now_fullxid);
     101             : 
     102          58 :     if (extracted_xid != NULL)
     103          58 :         *extracted_xid = xid;
     104             : 
     105          58 :     if (!TransactionIdIsValid(xid))
     106           0 :         return false;
     107             : 
     108             :     /* For non-normal transaction IDs, we can ignore the epoch. */
     109          58 :     if (!TransactionIdIsNormal(xid))
     110          16 :         return true;
     111             : 
     112             :     /* If the transaction ID is in the future, throw an error. */
     113          42 :     if (!FullTransactionIdPrecedes(fxid, now_fullxid))
     114           8 :         ereport(ERROR,
     115             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     116             :                  errmsg("transaction ID %s is in the future",
     117             :                         psprintf(UINT64_FORMAT,
     118             :                                  U64FromFullTransactionId(fxid)))));
     119             : 
     120             :     /*
     121             :      * ShmemVariableCache->oldestClogXid is protected by XactTruncationLock,
     122             :      * but we don't acquire that lock here.  Instead, we require the caller to
     123             :      * acquire it, because the caller is presumably going to look up the
     124             :      * returned XID.  If we took and released the lock within this function, a
     125             :      * CLOG truncation could occur before the caller finished with the XID.
     126             :      */
     127             :     Assert(LWLockHeldByMe(XactTruncationLock));
     128             : 
     129             :     /*
     130             :      * If the transaction ID has wrapped around, it's definitely too old to
     131             :      * determine the commit status.  Otherwise, we can compare it to
     132             :      * ShmemVariableCache->oldestClogXid to determine whether the relevant
     133             :      * CLOG entry is guaranteed to still exist.
     134             :      */
     135          34 :     if (xid_epoch + 1 < now_epoch
     136          34 :         || (xid_epoch + 1 == now_epoch && xid < now_epoch_next_xid)
     137          34 :         || TransactionIdPrecedes(xid, ShmemVariableCache->oldestClogXid))
     138           8 :         return false;
     139             : 
     140          26 :     return true;
     141             : }
     142             : 
     143             : /*
     144             :  * Convert a TransactionId obtained from a snapshot held by the caller to a
     145             :  * FullTransactionId.  Use next_fxid as a reference FullTransactionId, so that
     146             :  * we can compute the high order bits.  It must have been obtained by the
     147             :  * caller with ReadNextFullTransactionId() after the snapshot was created.
     148             :  */
     149             : static FullTransactionId
     150          40 : widen_snapshot_xid(TransactionId xid, FullTransactionId next_fxid)
     151             : {
     152          40 :     TransactionId next_xid = XidFromFullTransactionId(next_fxid);
     153          40 :     uint32      epoch = EpochFromFullTransactionId(next_fxid);
     154             : 
     155             :     /* Special transaction ID. */
     156          40 :     if (!TransactionIdIsNormal(xid))
     157           0 :         return FullTransactionIdFromEpochAndXid(0, xid);
     158             : 
     159             :     /*
     160             :      * The 64 bit result must be <= next_fxid, since next_fxid hadn't been
     161             :      * issued yet when the snapshot was created.  Every TransactionId in the
     162             :      * snapshot must therefore be from the same epoch as next_fxid, or the
     163             :      * epoch before.  We know this because next_fxid is never allow to get
     164             :      * more than one epoch ahead of the TransactionIds in any snapshot.
     165             :      */
     166          40 :     if (xid > next_xid)
     167           0 :         epoch--;
     168             : 
     169          40 :     return FullTransactionIdFromEpochAndXid(epoch, xid);
     170             : }
     171             : 
     172             : /*
     173             :  * txid comparator for qsort/bsearch
     174             :  */
     175             : static int
     176        1792 : cmp_fxid(const void *aa, const void *bb)
     177             : {
     178        1792 :     FullTransactionId a = *(const FullTransactionId *) aa;
     179        1792 :     FullTransactionId b = *(const FullTransactionId *) bb;
     180             : 
     181        1792 :     if (FullTransactionIdPrecedes(a, b))
     182         432 :         return -1;
     183        1360 :     if (FullTransactionIdPrecedes(b, a))
     184        1112 :         return 1;
     185         248 :     return 0;
     186             : }
     187             : 
     188             : /*
     189             :  * Sort a snapshot's txids, so we can use bsearch() later.  Also remove
     190             :  * any duplicates.
     191             :  *
     192             :  * For consistency of on-disk representation, we always sort even if bsearch
     193             :  * will not be used.
     194             :  */
     195             : static void
     196          16 : sort_snapshot(pg_snapshot *snap)
     197             : {
     198          16 :     if (snap->nxip > 1)
     199             :     {
     200           0 :         qsort(snap->xip, snap->nxip, sizeof(FullTransactionId), cmp_fxid);
     201           0 :         snap->nxip = qunique(snap->xip, snap->nxip, sizeof(FullTransactionId),
     202             :                              cmp_fxid);
     203             :     }
     204          16 : }
     205             : 
     206             : /*
     207             :  * check fxid visibility.
     208             :  */
     209             : static bool
     210         680 : is_visible_fxid(FullTransactionId value, const pg_snapshot *snap)
     211             : {
     212         680 :     if (FullTransactionIdPrecedes(value, snap->xmin))
     213          88 :         return true;
     214         592 :     else if (!FullTransactionIdPrecedes(value, snap->xmax))
     215         112 :         return false;
     216             : #ifdef USE_BSEARCH_IF_NXIP_GREATER
     217         480 :     else if (snap->nxip > USE_BSEARCH_IF_NXIP_GREATER)
     218             :     {
     219             :         void       *res;
     220             : 
     221         400 :         res = bsearch(&value, snap->xip, snap->nxip, sizeof(FullTransactionId),
     222             :                       cmp_fxid);
     223             :         /* if found, transaction is still in progress */
     224         400 :         return (res) ? false : true;
     225             :     }
     226             : #endif
     227             :     else
     228             :     {
     229             :         uint32      i;
     230             : 
     231         240 :         for (i = 0; i < snap->nxip; i++)
     232             :         {
     233         192 :             if (FullTransactionIdEquals(value, snap->xip[i]))
     234          32 :                 return false;
     235             :         }
     236          48 :         return true;
     237             :     }
     238             : }
     239             : 
     240             : /*
     241             :  * helper functions to use StringInfo for pg_snapshot creation.
     242             :  */
     243             : 
     244             : static StringInfo
     245         104 : buf_init(FullTransactionId xmin, FullTransactionId xmax)
     246             : {
     247             :     pg_snapshot snap;
     248             :     StringInfo  buf;
     249             : 
     250         104 :     snap.xmin = xmin;
     251         104 :     snap.xmax = xmax;
     252         104 :     snap.nxip = 0;
     253             : 
     254         104 :     buf = makeStringInfo();
     255         104 :     appendBinaryStringInfo(buf, (char *) &snap, PG_SNAPSHOT_SIZE(0));
     256         104 :     return buf;
     257             : }
     258             : 
     259             : static void
     260         384 : buf_add_txid(StringInfo buf, FullTransactionId fxid)
     261             : {
     262         384 :     pg_snapshot *snap = (pg_snapshot *) buf->data;
     263             : 
     264             :     /* do this before possible realloc */
     265         384 :     snap->nxip++;
     266             : 
     267         384 :     appendBinaryStringInfo(buf, (char *) &fxid, sizeof(fxid));
     268         384 : }
     269             : 
     270             : static pg_snapshot *
     271          88 : buf_finalize(StringInfo buf)
     272             : {
     273          88 :     pg_snapshot *snap = (pg_snapshot *) buf->data;
     274             : 
     275          88 :     SET_VARSIZE(snap, buf->len);
     276             : 
     277             :     /* buf is not needed anymore */
     278          88 :     buf->data = NULL;
     279          88 :     pfree(buf);
     280             : 
     281          88 :     return snap;
     282             : }
     283             : 
     284             : /*
     285             :  * parse snapshot from cstring
     286             :  */
     287             : static pg_snapshot *
     288         128 : parse_snapshot(const char *str)
     289             : {
     290             :     FullTransactionId xmin;
     291             :     FullTransactionId xmax;
     292         128 :     FullTransactionId last_val = InvalidFullTransactionId;
     293             :     FullTransactionId val;
     294         128 :     const char *str_start = str;
     295             :     char       *endp;
     296             :     StringInfo  buf;
     297             : 
     298         128 :     xmin = FullTransactionIdFromU64(pg_strtouint64(str, &endp, 10));
     299         128 :     if (*endp != ':')
     300           0 :         goto bad_format;
     301         128 :     str = endp + 1;
     302             : 
     303         128 :     xmax = FullTransactionIdFromU64(pg_strtouint64(str, &endp, 10));
     304         128 :     if (*endp != ':')
     305           0 :         goto bad_format;
     306         128 :     str = endp + 1;
     307             : 
     308             :     /* it should look sane */
     309         128 :     if (!FullTransactionIdIsValid(xmin) ||
     310         120 :         !FullTransactionIdIsValid(xmax) ||
     311         112 :         FullTransactionIdPrecedes(xmax, xmin))
     312          24 :         goto bad_format;
     313             : 
     314             :     /* allocate buffer */
     315         104 :     buf = buf_init(xmin, xmax);
     316             : 
     317             :     /* loop over values */
     318         496 :     while (*str != '\0')
     319             :     {
     320             :         /* read next value */
     321         408 :         val = FullTransactionIdFromU64(pg_strtouint64(str, &endp, 10));
     322         408 :         str = endp;
     323             : 
     324             :         /* require the input to be in order */
     325         408 :         if (FullTransactionIdPrecedes(val, xmin) ||
     326         400 :             FullTransactionIdFollowsOrEquals(val, xmax) ||
     327         400 :             FullTransactionIdPrecedes(val, last_val))
     328          16 :             goto bad_format;
     329             : 
     330             :         /* skip duplicates */
     331         392 :         if (!FullTransactionIdEquals(val, last_val))
     332         384 :             buf_add_txid(buf, val);
     333         392 :         last_val = val;
     334             : 
     335         392 :         if (*str == ',')
     336         320 :             str++;
     337          72 :         else if (*str != '\0')
     338           0 :             goto bad_format;
     339             :     }
     340             : 
     341          88 :     return buf_finalize(buf);
     342             : 
     343          40 : bad_format:
     344          40 :     ereport(ERROR,
     345             :             (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     346             :              errmsg("invalid input syntax for type %s: \"%s\"",
     347             :                     "pg_snapshot", str_start)));
     348             :     return NULL;                /* keep compiler quiet */
     349             : }
     350             : 
     351             : /*
     352             :  * pg_current_xact_id() returns xid8
     353             :  *
     354             :  *  Return the current toplevel full transaction ID.
     355             :  *  If the current transaction does not have one, one is assigned.
     356             :  */
     357             : Datum
     358        5100 : pg_current_xact_id(PG_FUNCTION_ARGS)
     359             : {
     360             :     /*
     361             :      * Must prevent during recovery because if an xid is not assigned we try
     362             :      * to assign one, which would fail. Programs already rely on this function
     363             :      * to always return a valid current xid, so we should not change this to
     364             :      * return NULL or similar invalid xid.
     365             :      */
     366        5100 :     PreventCommandDuringRecovery("pg_current_xact_id()");
     367             : 
     368        5100 :     PG_RETURN_FULLTRANSACTIONID(GetTopFullTransactionId());
     369             : }
     370             : 
     371             : /*
     372             :  * Same as pg_current_xact_if_assigned() but doesn't assign a new xid if there
     373             :  * isn't one yet.
     374             :  */
     375             : Datum
     376          16 : pg_current_xact_id_if_assigned(PG_FUNCTION_ARGS)
     377             : {
     378          16 :     FullTransactionId topfxid = GetTopFullTransactionIdIfAny();
     379             : 
     380          16 :     if (!FullTransactionIdIsValid(topfxid))
     381           8 :         PG_RETURN_NULL();
     382             : 
     383           8 :     PG_RETURN_FULLTRANSACTIONID(topfxid);
     384             : }
     385             : 
     386             : /*
     387             :  * pg_current_snapshot() returns pg_snapshot
     388             :  *
     389             :  *      Return current snapshot
     390             :  *
     391             :  * Note that only top-transaction XIDs are included in the snapshot.
     392             :  */
     393             : Datum
     394          16 : pg_current_snapshot(PG_FUNCTION_ARGS)
     395             : {
     396             :     pg_snapshot *snap;
     397             :     uint32      nxip,
     398             :                 i;
     399             :     Snapshot    cur;
     400          16 :     FullTransactionId next_fxid = ReadNextFullTransactionId();
     401             : 
     402          16 :     cur = GetActiveSnapshot();
     403          16 :     if (cur == NULL)
     404           0 :         elog(ERROR, "no active snapshot set");
     405             : 
     406             :     /*
     407             :      * Compile-time limits on the procarray (MAX_BACKENDS processes plus
     408             :      * MAX_BACKENDS prepared transactions) guarantee nxip won't be too large.
     409             :      */
     410             :     StaticAssertStmt(MAX_BACKENDS * 2 <= PG_SNAPSHOT_MAX_NXIP,
     411             :                      "possible overflow in pg_current_snapshot()");
     412             : 
     413             :     /* allocate */
     414          16 :     nxip = cur->xcnt;
     415          16 :     snap = palloc(PG_SNAPSHOT_SIZE(nxip));
     416             : 
     417             :     /* fill */
     418          16 :     snap->xmin = widen_snapshot_xid(cur->xmin, next_fxid);
     419          16 :     snap->xmax = widen_snapshot_xid(cur->xmax, next_fxid);
     420          16 :     snap->nxip = nxip;
     421          24 :     for (i = 0; i < nxip; i++)
     422           8 :         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          16 :     sort_snapshot(snap);
     432             : 
     433             :     /* set size after sorting, because it may have removed duplicate xips */
     434          16 :     SET_VARSIZE(snap, PG_SNAPSHOT_SIZE(snap->nxip));
     435             : 
     436          16 :     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         128 : pg_snapshot_in(PG_FUNCTION_ARGS)
     446             : {
     447         128 :     char       *str = PG_GETARG_CSTRING(0);
     448             :     pg_snapshot *snap;
     449             : 
     450         128 :     snap = parse_snapshot(str);
     451             : 
     452          88 :     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          72 : pg_snapshot_out(PG_FUNCTION_ARGS)
     462             : {
     463          72 :     pg_snapshot *snap = (pg_snapshot *) PG_GETARG_VARLENA_P(0);
     464             :     StringInfoData str;
     465             :     uint32      i;
     466             : 
     467          72 :     initStringInfo(&str);
     468             : 
     469          72 :     appendStringInfo(&str, UINT64_FORMAT ":",
     470             :                      U64FromFullTransactionId(snap->xmin));
     471          72 :     appendStringInfo(&str, UINT64_FORMAT ":",
     472             :                      U64FromFullTransactionId(snap->xmax));
     473             : 
     474         416 :     for (i = 0; i < snap->nxip; i++)
     475             :     {
     476         344 :         if (i > 0)
     477         288 :             appendStringInfoChar(&str, ',');
     478         344 :         appendStringInfo(&str, UINT64_FORMAT,
     479             :                          U64FromFullTransactionId(snap->xip[i]));
     480             :     }
     481             : 
     482          72 :     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         680 : pg_visible_in_snapshot(PG_FUNCTION_ARGS)
     581             : {
     582         680 :     FullTransactionId value = PG_GETARG_FULLTRANSACTIONID(0);
     583         680 :     pg_snapshot *snap = (pg_snapshot *) PG_GETARG_VARLENA_P(1);
     584             : 
     585         680 :     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          40 : pg_snapshot_xmin(PG_FUNCTION_ARGS)
     595             : {
     596          40 :     pg_snapshot *snap = (pg_snapshot *) PG_GETARG_VARLENA_P(0);
     597             : 
     598          40 :     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          32 : pg_snapshot_xmax(PG_FUNCTION_ARGS)
     608             : {
     609          32 :     pg_snapshot *snap = (pg_snapshot *) PG_GETARG_VARLENA_P(0);
     610             : 
     611          32 :     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         328 : 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         328 :     if (SRF_IS_FIRSTCALL())
     628             :     {
     629          32 :         pg_snapshot *arg = (pg_snapshot *) PG_GETARG_VARLENA_P(0);
     630             : 
     631          32 :         fctx = SRF_FIRSTCALL_INIT();
     632             : 
     633             :         /* make a copy of user snapshot */
     634          32 :         snap = MemoryContextAlloc(fctx->multi_call_memory_ctx, VARSIZE(arg));
     635          32 :         memcpy(snap, arg, VARSIZE(arg));
     636             : 
     637          32 :         fctx->user_fctx = snap;
     638             :     }
     639             : 
     640             :     /* return values one-by-one */
     641         328 :     fctx = SRF_PERCALL_SETUP();
     642         328 :     snap = fctx->user_fctx;
     643         328 :     if (fctx->call_cntr < snap->nxip)
     644             :     {
     645         296 :         value = snap->xip[fctx->call_cntr];
     646         296 :         SRF_RETURN_NEXT(fctx, FullTransactionIdGetDatum(value));
     647             :     }
     648             :     else
     649             :     {
     650          32 :         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          58 : pg_xact_status(PG_FUNCTION_ARGS)
     666             : {
     667             :     const char *status;
     668          58 :     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          58 :     LWLockAcquire(XactTruncationLock, LW_SHARED);
     676          58 :     if (TransactionIdInRecentPast(fxid, &xid))
     677             :     {
     678             :         Assert(TransactionIdIsValid(xid));
     679             : 
     680          42 :         if (TransactionIdIsCurrentTransactionId(xid))
     681           8 :             status = "in progress";
     682          34 :         else if (TransactionIdDidCommit(xid))
     683          24 :             status = "committed";
     684          10 :         else if (TransactionIdDidAbort(xid))
     685           8 :             status = "aborted";
     686             :         else
     687             :         {
     688             :             /*
     689             :              * The xact is not marked as either committed or aborted in clog.
     690             :              *
     691             :              * It could be a transaction that ended without updating clog or
     692             :              * writing an abort record due to a crash. We can safely assume
     693             :              * it's aborted if it isn't committed and is older than our
     694             :              * snapshot xmin.
     695             :              *
     696             :              * Otherwise it must be in-progress (or have been at the time we
     697             :              * checked commit/abort status).
     698             :              */
     699           2 :             if (TransactionIdPrecedes(xid, GetActiveSnapshot()->xmin))
     700           2 :                 status = "aborted";
     701             :             else
     702           0 :                 status = "in progress";
     703             :         }
     704             :     }
     705             :     else
     706             :     {
     707           8 :         status = NULL;
     708             :     }
     709          50 :     LWLockRelease(XactTruncationLock);
     710             : 
     711          50 :     if (status == NULL)
     712           8 :         PG_RETURN_NULL();
     713             :     else
     714          42 :         PG_RETURN_TEXT_P(cstring_to_text(status));
     715             : }

Generated by: LCOV version 1.13