LCOV - code coverage report
Current view: top level - src/backend/utils/adt - txid.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13devel Lines: 191 233 82.0 %
Date: 2019-11-22 08:06:54 Functions: 21 23 91.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  * txid.c
       3             :  *
       4             :  *  Export internal transaction IDs to user level.
       5             :  *
       6             :  * Note that only top-level transaction IDs are ever converted to TXID.
       7             :  * This is important because TXIDs 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             :  *
      13             :  *  Copyright (c) 2003-2019, PostgreSQL Global Development Group
      14             :  *  Author: Jan Wieck, Afilias USA INC.
      15             :  *  64-bit txids: Marko Kreen, Skype Technologies
      16             :  *
      17             :  *  src/backend/utils/adt/txid.c
      18             :  *
      19             :  *-------------------------------------------------------------------------
      20             :  */
      21             : 
      22             : #include "postgres.h"
      23             : 
      24             : #include "access/clog.h"
      25             : #include "access/transam.h"
      26             : #include "access/xact.h"
      27             : #include "access/xlog.h"
      28             : #include "funcapi.h"
      29             : #include "lib/qunique.h"
      30             : #include "libpq/pqformat.h"
      31             : #include "miscadmin.h"
      32             : #include "postmaster/postmaster.h"
      33             : #include "storage/lwlock.h"
      34             : #include "utils/builtins.h"
      35             : #include "utils/memutils.h"
      36             : #include "utils/snapmgr.h"
      37             : 
      38             : /* txid will be signed int8 in database, so must limit to 63 bits */
      39             : #define MAX_TXID   ((uint64) PG_INT64_MAX)
      40             : 
      41             : /* Use unsigned variant internally */
      42             : typedef uint64 txid;
      43             : 
      44             : /* sprintf format code for uint64 */
      45             : #define TXID_FMT UINT64_FORMAT
      46             : 
      47             : /*
      48             :  * If defined, use bsearch() function for searching for txids in snapshots
      49             :  * that have more than the specified number of values.
      50             :  */
      51             : #define USE_BSEARCH_IF_NXIP_GREATER 30
      52             : 
      53             : 
      54             : /*
      55             :  * Snapshot containing 8byte txids.
      56             :  */
      57             : typedef struct
      58             : {
      59             :     /*
      60             :      * 4-byte length hdr, should not be touched directly.
      61             :      *
      62             :      * Explicit embedding is ok as we want always correct alignment anyway.
      63             :      */
      64             :     int32       __varsz;
      65             : 
      66             :     uint32      nxip;           /* number of txids in xip array */
      67             :     txid        xmin;
      68             :     txid        xmax;
      69             :     /* in-progress txids, xmin <= xip[i] < xmax: */
      70             :     txid        xip[FLEXIBLE_ARRAY_MEMBER];
      71             : } TxidSnapshot;
      72             : 
      73             : #define TXID_SNAPSHOT_SIZE(nxip) \
      74             :     (offsetof(TxidSnapshot, xip) + sizeof(txid) * (nxip))
      75             : #define TXID_SNAPSHOT_MAX_NXIP \
      76             :     ((MaxAllocSize - offsetof(TxidSnapshot, xip)) / sizeof(txid))
      77             : 
      78             : /*
      79             :  * Epoch values from xact.c
      80             :  */
      81             : typedef struct
      82             : {
      83             :     TransactionId last_xid;
      84             :     uint32      epoch;
      85             : } TxidEpoch;
      86             : 
      87             : 
      88             : /*
      89             :  * Fetch epoch data from xact.c.
      90             :  */
      91             : static void
      92        5088 : load_xid_epoch(TxidEpoch *state)
      93             : {
      94        5088 :     FullTransactionId fullXid = ReadNextFullTransactionId();
      95             : 
      96        5088 :     state->last_xid = XidFromFullTransactionId(fullXid);
      97        5088 :     state->epoch = EpochFromFullTransactionId(fullXid);
      98        5088 : }
      99             : 
     100             : /*
     101             :  * Helper to get a TransactionId from a 64-bit xid with wraparound detection.
     102             :  *
     103             :  * It is an ERROR if the xid is in the future.  Otherwise, returns true if
     104             :  * the transaction is still new enough that we can determine whether it
     105             :  * committed and false otherwise.  If *extracted_xid is not NULL, it is set
     106             :  * to the low 32 bits of the transaction ID (i.e. the actual XID, without the
     107             :  * epoch).
     108             :  *
     109             :  * The caller must hold CLogTruncationLock since it's dealing with arbitrary
     110             :  * XIDs, and must continue to hold it until it's done with any clog lookups
     111             :  * relating to those XIDs.
     112             :  */
     113             : static bool
     114          30 : TransactionIdInRecentPast(uint64 xid_with_epoch, TransactionId *extracted_xid)
     115             : {
     116          30 :     uint32      xid_epoch = (uint32) (xid_with_epoch >> 32);
     117          30 :     TransactionId xid = (TransactionId) xid_with_epoch;
     118             :     uint32      now_epoch;
     119             :     TransactionId now_epoch_next_xid;
     120             :     FullTransactionId now_fullxid;
     121             : 
     122          30 :     now_fullxid = ReadNextFullTransactionId();
     123          30 :     now_epoch_next_xid = XidFromFullTransactionId(now_fullxid);
     124          30 :     now_epoch = EpochFromFullTransactionId(now_fullxid);
     125             : 
     126          30 :     if (extracted_xid != NULL)
     127          30 :         *extracted_xid = xid;
     128             : 
     129          30 :     if (!TransactionIdIsValid(xid))
     130           0 :         return false;
     131             : 
     132             :     /* For non-normal transaction IDs, we can ignore the epoch. */
     133          30 :     if (!TransactionIdIsNormal(xid))
     134           8 :         return true;
     135             : 
     136             :     /* If the transaction ID is in the future, throw an error. */
     137          22 :     if (xid_with_epoch >= U64FromFullTransactionId(now_fullxid))
     138           4 :         ereport(ERROR,
     139             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     140             :                  errmsg("transaction ID %s is in the future",
     141             :                         psprintf(UINT64_FORMAT, xid_with_epoch))));
     142             : 
     143             :     /*
     144             :      * ShmemVariableCache->oldestClogXid is protected by CLogTruncationLock,
     145             :      * but we don't acquire that lock here.  Instead, we require the caller to
     146             :      * acquire it, because the caller is presumably going to look up the
     147             :      * returned XID.  If we took and released the lock within this function, a
     148             :      * CLOG truncation could occur before the caller finished with the XID.
     149             :      */
     150             :     Assert(LWLockHeldByMe(CLogTruncationLock));
     151             : 
     152             :     /*
     153             :      * If the transaction ID has wrapped around, it's definitely too old to
     154             :      * determine the commit status.  Otherwise, we can compare it to
     155             :      * ShmemVariableCache->oldestClogXid to determine whether the relevant
     156             :      * CLOG entry is guaranteed to still exist.
     157             :      */
     158          18 :     if (xid_epoch + 1 < now_epoch
     159          18 :         || (xid_epoch + 1 == now_epoch && xid < now_epoch_next_xid)
     160          18 :         || TransactionIdPrecedes(xid, ShmemVariableCache->oldestClogXid))
     161           4 :         return false;
     162             : 
     163          14 :     return true;
     164             : }
     165             : 
     166             : /*
     167             :  * do a TransactionId -> txid conversion for an XID near the given epoch
     168             :  */
     169             : static txid
     170        5104 : convert_xid(TransactionId xid, const TxidEpoch *state)
     171             : {
     172             :     uint64      epoch;
     173             : 
     174             :     /* return special xid's as-is */
     175        5104 :     if (!TransactionIdIsNormal(xid))
     176           0 :         return (txid) xid;
     177             : 
     178             :     /* xid can be on either side when near wrap-around */
     179        5104 :     epoch = (uint64) state->epoch;
     180        5104 :     if (xid > state->last_xid &&
     181           0 :         TransactionIdPrecedes(xid, state->last_xid))
     182           0 :         epoch--;
     183        6156 :     else if (xid < state->last_xid &&
     184        1052 :              TransactionIdFollows(xid, state->last_xid))
     185           0 :         epoch++;
     186             : 
     187        5104 :     return (epoch << 32) | xid;
     188             : }
     189             : 
     190             : /*
     191             :  * txid comparator for qsort/bsearch
     192             :  */
     193             : static int
     194         904 : cmp_txid(const void *aa, const void *bb)
     195             : {
     196         904 :     txid        a = *(const txid *) aa;
     197         904 :     txid        b = *(const txid *) bb;
     198             : 
     199         904 :     if (a < b)
     200         216 :         return -1;
     201         688 :     if (a > b)
     202         564 :         return 1;
     203         124 :     return 0;
     204             : }
     205             : 
     206             : /*
     207             :  * Sort a snapshot's txids, so we can use bsearch() later.  Also remove
     208             :  * any duplicates.
     209             :  *
     210             :  * For consistency of on-disk representation, we always sort even if bsearch
     211             :  * will not be used.
     212             :  */
     213             : static void
     214           8 : sort_snapshot(TxidSnapshot *snap)
     215             : {
     216           8 :     if (snap->nxip > 1)
     217             :     {
     218           4 :         qsort(snap->xip, snap->nxip, sizeof(txid), cmp_txid);
     219           4 :         snap->nxip = qunique(snap->xip, snap->nxip, sizeof(txid), cmp_txid);
     220             :     }
     221           8 : }
     222             : 
     223             : /*
     224             :  * check txid visibility.
     225             :  */
     226             : static bool
     227         340 : is_visible_txid(txid value, const TxidSnapshot *snap)
     228             : {
     229         340 :     if (value < snap->xmin)
     230          44 :         return true;
     231         296 :     else if (value >= snap->xmax)
     232          56 :         return false;
     233             : #ifdef USE_BSEARCH_IF_NXIP_GREATER
     234         240 :     else if (snap->nxip > USE_BSEARCH_IF_NXIP_GREATER)
     235             :     {
     236             :         void       *res;
     237             : 
     238         200 :         res = bsearch(&value, snap->xip, snap->nxip, sizeof(txid), cmp_txid);
     239             :         /* if found, transaction is still in progress */
     240         200 :         return (res) ? false : true;
     241             :     }
     242             : #endif
     243             :     else
     244             :     {
     245             :         uint32      i;
     246             : 
     247         120 :         for (i = 0; i < snap->nxip; i++)
     248             :         {
     249          96 :             if (value == snap->xip[i])
     250          16 :                 return false;
     251             :         }
     252          24 :         return true;
     253             :     }
     254             : }
     255             : 
     256             : /*
     257             :  * helper functions to use StringInfo for TxidSnapshot creation.
     258             :  */
     259             : 
     260             : static StringInfo
     261          52 : buf_init(txid xmin, txid xmax)
     262             : {
     263             :     TxidSnapshot snap;
     264             :     StringInfo  buf;
     265             : 
     266          52 :     snap.xmin = xmin;
     267          52 :     snap.xmax = xmax;
     268          52 :     snap.nxip = 0;
     269             : 
     270          52 :     buf = makeStringInfo();
     271          52 :     appendBinaryStringInfo(buf, (char *) &snap, TXID_SNAPSHOT_SIZE(0));
     272          52 :     return buf;
     273             : }
     274             : 
     275             : static void
     276         192 : buf_add_txid(StringInfo buf, txid xid)
     277             : {
     278         192 :     TxidSnapshot *snap = (TxidSnapshot *) buf->data;
     279             : 
     280             :     /* do this before possible realloc */
     281         192 :     snap->nxip++;
     282             : 
     283         192 :     appendBinaryStringInfo(buf, (char *) &xid, sizeof(xid));
     284         192 : }
     285             : 
     286             : static TxidSnapshot *
     287          44 : buf_finalize(StringInfo buf)
     288             : {
     289          44 :     TxidSnapshot *snap = (TxidSnapshot *) buf->data;
     290             : 
     291          44 :     SET_VARSIZE(snap, buf->len);
     292             : 
     293             :     /* buf is not needed anymore */
     294          44 :     buf->data = NULL;
     295          44 :     pfree(buf);
     296             : 
     297          44 :     return snap;
     298             : }
     299             : 
     300             : /*
     301             :  * simple number parser.
     302             :  *
     303             :  * We return 0 on error, which is invalid value for txid.
     304             :  */
     305             : static txid
     306         332 : str2txid(const char *s, const char **endp)
     307             : {
     308         332 :     txid        val = 0;
     309         332 :     txid        cutoff = MAX_TXID / 10;
     310         332 :     txid        cutlim = MAX_TXID % 10;
     311             : 
     312        1988 :     for (; *s; s++)
     313             :     {
     314             :         unsigned    d;
     315             : 
     316        1944 :         if (*s < '0' || *s > '9')
     317             :             break;
     318        1660 :         d = *s - '0';
     319             : 
     320             :         /*
     321             :          * check for overflow
     322             :          */
     323        1660 :         if (val > cutoff || (val == cutoff && d > cutlim))
     324             :         {
     325           4 :             val = 0;
     326           4 :             break;
     327             :         }
     328             : 
     329        1656 :         val = val * 10 + d;
     330             :     }
     331         332 :     if (endp)
     332         332 :         *endp = s;
     333         332 :     return val;
     334             : }
     335             : 
     336             : /*
     337             :  * parse snapshot from cstring
     338             :  */
     339             : static TxidSnapshot *
     340          64 : parse_snapshot(const char *str)
     341             : {
     342             :     txid        xmin;
     343             :     txid        xmax;
     344          64 :     txid        last_val = 0,
     345             :                 val;
     346          64 :     const char *str_start = str;
     347             :     const char *endp;
     348             :     StringInfo  buf;
     349             : 
     350          64 :     xmin = str2txid(str, &endp);
     351          64 :     if (*endp != ':')
     352           0 :         goto bad_format;
     353          64 :     str = endp + 1;
     354             : 
     355          64 :     xmax = str2txid(str, &endp);
     356          64 :     if (*endp != ':')
     357           4 :         goto bad_format;
     358          60 :     str = endp + 1;
     359             : 
     360             :     /* it should look sane */
     361          60 :     if (xmin == 0 || xmax == 0 || xmin > xmax)
     362             :         goto bad_format;
     363             : 
     364             :     /* allocate buffer */
     365          52 :     buf = buf_init(xmin, xmax);
     366             : 
     367             :     /* loop over values */
     368          52 :     while (*str != '\0')
     369             :     {
     370             :         /* read next value */
     371         204 :         val = str2txid(str, &endp);
     372         204 :         str = endp;
     373             : 
     374             :         /* require the input to be in order */
     375         204 :         if (val < xmin || val >= xmax || val < last_val)
     376             :             goto bad_format;
     377             : 
     378             :         /* skip duplicates */
     379         196 :         if (val != last_val)
     380         192 :             buf_add_txid(buf, val);
     381         196 :         last_val = val;
     382             : 
     383         196 :         if (*str == ',')
     384         160 :             str++;
     385          36 :         else if (*str != '\0')
     386           0 :             goto bad_format;
     387             :     }
     388             : 
     389          44 :     return buf_finalize(buf);
     390             : 
     391             : bad_format:
     392          20 :     ereport(ERROR,
     393             :             (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     394             :              errmsg("invalid input syntax for type %s: \"%s\"",
     395             :                     "txid_snapshot", str_start)));
     396             :     return NULL;                /* keep compiler quiet */
     397             : }
     398             : 
     399             : /*
     400             :  * Public functions.
     401             :  *
     402             :  * txid_current() and txid_current_snapshot() are the only ones that
     403             :  * communicate with core xid machinery.  All the others work on data
     404             :  * returned by them.
     405             :  */
     406             : 
     407             : /*
     408             :  * txid_current() returns int8
     409             :  *
     410             :  *  Return the current toplevel transaction ID as TXID
     411             :  *  If the current transaction does not have one, one is assigned.
     412             :  *
     413             :  *  This value has the epoch as the high 32 bits and the 32-bit xid
     414             :  *  as the low 32 bits.
     415             :  */
     416             : Datum
     417        5076 : txid_current(PG_FUNCTION_ARGS)
     418             : {
     419             :     txid        val;
     420             :     TxidEpoch   state;
     421             : 
     422             :     /*
     423             :      * Must prevent during recovery because if an xid is not assigned we try
     424             :      * to assign one, which would fail. Programs already rely on this function
     425             :      * to always return a valid current xid, so we should not change this to
     426             :      * return NULL or similar invalid xid.
     427             :      */
     428        5076 :     PreventCommandDuringRecovery("txid_current()");
     429             : 
     430        5076 :     load_xid_epoch(&state);
     431             : 
     432        5076 :     val = convert_xid(GetTopTransactionId(), &state);
     433             : 
     434        5076 :     PG_RETURN_INT64(val);
     435             : }
     436             : 
     437             : /*
     438             :  * Same as txid_current() but doesn't assign a new xid if there isn't one
     439             :  * yet.
     440             :  */
     441             : Datum
     442           8 : txid_current_if_assigned(PG_FUNCTION_ARGS)
     443             : {
     444             :     txid        val;
     445             :     TxidEpoch   state;
     446           8 :     TransactionId topxid = GetTopTransactionIdIfAny();
     447             : 
     448           8 :     if (topxid == InvalidTransactionId)
     449           4 :         PG_RETURN_NULL();
     450             : 
     451           4 :     load_xid_epoch(&state);
     452             : 
     453           4 :     val = convert_xid(topxid, &state);
     454             : 
     455           4 :     PG_RETURN_INT64(val);
     456             : }
     457             : 
     458             : /*
     459             :  * txid_current_snapshot() returns txid_snapshot
     460             :  *
     461             :  *      Return current snapshot in TXID format
     462             :  *
     463             :  * Note that only top-transaction XIDs are included in the snapshot.
     464             :  */
     465             : Datum
     466           8 : txid_current_snapshot(PG_FUNCTION_ARGS)
     467             : {
     468             :     TxidSnapshot *snap;
     469             :     uint32      nxip,
     470             :                 i;
     471             :     TxidEpoch   state;
     472             :     Snapshot    cur;
     473             : 
     474           8 :     cur = GetActiveSnapshot();
     475           8 :     if (cur == NULL)
     476           0 :         elog(ERROR, "no active snapshot set");
     477             : 
     478           8 :     load_xid_epoch(&state);
     479             : 
     480             :     /*
     481             :      * Compile-time limits on the procarray (MAX_BACKENDS processes plus
     482             :      * MAX_BACKENDS prepared transactions) guarantee nxip won't be too large.
     483             :      */
     484             :     StaticAssertStmt(MAX_BACKENDS * 2 <= TXID_SNAPSHOT_MAX_NXIP,
     485             :                      "possible overflow in txid_current_snapshot()");
     486             : 
     487             :     /* allocate */
     488           8 :     nxip = cur->xcnt;
     489           8 :     snap = palloc(TXID_SNAPSHOT_SIZE(nxip));
     490             : 
     491             :     /* fill */
     492           8 :     snap->xmin = convert_xid(cur->xmin, &state);
     493           8 :     snap->xmax = convert_xid(cur->xmax, &state);
     494           8 :     snap->nxip = nxip;
     495          16 :     for (i = 0; i < nxip; i++)
     496           8 :         snap->xip[i] = convert_xid(cur->xip[i], &state);
     497             : 
     498             :     /*
     499             :      * We want them guaranteed to be in ascending order.  This also removes
     500             :      * any duplicate xids.  Normally, an XID can only be assigned to one
     501             :      * backend, but when preparing a transaction for two-phase commit, there
     502             :      * is a transient state when both the original backend and the dummy
     503             :      * PGPROC entry reserved for the prepared transaction hold the same XID.
     504             :      */
     505           8 :     sort_snapshot(snap);
     506             : 
     507             :     /* set size after sorting, because it may have removed duplicate xips */
     508           8 :     SET_VARSIZE(snap, TXID_SNAPSHOT_SIZE(snap->nxip));
     509             : 
     510           8 :     PG_RETURN_POINTER(snap);
     511             : }
     512             : 
     513             : /*
     514             :  * txid_snapshot_in(cstring) returns txid_snapshot
     515             :  *
     516             :  *      input function for type txid_snapshot
     517             :  */
     518             : Datum
     519          64 : txid_snapshot_in(PG_FUNCTION_ARGS)
     520             : {
     521          64 :     char       *str = PG_GETARG_CSTRING(0);
     522             :     TxidSnapshot *snap;
     523             : 
     524          64 :     snap = parse_snapshot(str);
     525             : 
     526          44 :     PG_RETURN_POINTER(snap);
     527             : }
     528             : 
     529             : /*
     530             :  * txid_snapshot_out(txid_snapshot) returns cstring
     531             :  *
     532             :  *      output function for type txid_snapshot
     533             :  */
     534             : Datum
     535          36 : txid_snapshot_out(PG_FUNCTION_ARGS)
     536             : {
     537          36 :     TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(0);
     538             :     StringInfoData str;
     539             :     uint32      i;
     540             : 
     541          36 :     initStringInfo(&str);
     542             : 
     543          36 :     appendStringInfo(&str, TXID_FMT ":", snap->xmin);
     544          36 :     appendStringInfo(&str, TXID_FMT ":", snap->xmax);
     545             : 
     546         208 :     for (i = 0; i < snap->nxip; i++)
     547             :     {
     548         172 :         if (i > 0)
     549         144 :             appendStringInfoChar(&str, ',');
     550         172 :         appendStringInfo(&str, TXID_FMT, snap->xip[i]);
     551             :     }
     552             : 
     553          36 :     PG_RETURN_CSTRING(str.data);
     554             : }
     555             : 
     556             : /*
     557             :  * txid_snapshot_recv(internal) returns txid_snapshot
     558             :  *
     559             :  *      binary input function for type txid_snapshot
     560             :  *
     561             :  *      format: int4 nxip, int8 xmin, int8 xmax, int8 xip
     562             :  */
     563             : Datum
     564           0 : txid_snapshot_recv(PG_FUNCTION_ARGS)
     565             : {
     566           0 :     StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
     567             :     TxidSnapshot *snap;
     568           0 :     txid        last = 0;
     569             :     int         nxip;
     570             :     int         i;
     571             :     txid        xmin,
     572             :                 xmax;
     573             : 
     574             :     /* load and validate nxip */
     575           0 :     nxip = pq_getmsgint(buf, 4);
     576           0 :     if (nxip < 0 || nxip > TXID_SNAPSHOT_MAX_NXIP)
     577             :         goto bad_format;
     578             : 
     579           0 :     xmin = pq_getmsgint64(buf);
     580           0 :     xmax = pq_getmsgint64(buf);
     581           0 :     if (xmin == 0 || xmax == 0 || xmin > xmax || xmax > MAX_TXID)
     582             :         goto bad_format;
     583             : 
     584           0 :     snap = palloc(TXID_SNAPSHOT_SIZE(nxip));
     585           0 :     snap->xmin = xmin;
     586           0 :     snap->xmax = xmax;
     587             : 
     588           0 :     for (i = 0; i < nxip; i++)
     589             :     {
     590           0 :         txid        cur = pq_getmsgint64(buf);
     591             : 
     592           0 :         if (cur < last || cur < xmin || cur >= xmax)
     593             :             goto bad_format;
     594             : 
     595             :         /* skip duplicate xips */
     596           0 :         if (cur == last)
     597             :         {
     598           0 :             i--;
     599           0 :             nxip--;
     600           0 :             continue;
     601             :         }
     602             : 
     603           0 :         snap->xip[i] = cur;
     604           0 :         last = cur;
     605             :     }
     606           0 :     snap->nxip = nxip;
     607           0 :     SET_VARSIZE(snap, TXID_SNAPSHOT_SIZE(nxip));
     608           0 :     PG_RETURN_POINTER(snap);
     609             : 
     610             : bad_format:
     611           0 :     ereport(ERROR,
     612             :             (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
     613             :              errmsg("invalid external txid_snapshot data")));
     614             :     PG_RETURN_POINTER(NULL);    /* keep compiler quiet */
     615             : }
     616             : 
     617             : /*
     618             :  * txid_snapshot_send(txid_snapshot) returns bytea
     619             :  *
     620             :  *      binary output function for type txid_snapshot
     621             :  *
     622             :  *      format: int4 nxip, int8 xmin, int8 xmax, int8 xip
     623             :  */
     624             : Datum
     625           0 : txid_snapshot_send(PG_FUNCTION_ARGS)
     626             : {
     627           0 :     TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(0);
     628             :     StringInfoData buf;
     629             :     uint32      i;
     630             : 
     631           0 :     pq_begintypsend(&buf);
     632           0 :     pq_sendint32(&buf, snap->nxip);
     633           0 :     pq_sendint64(&buf, snap->xmin);
     634           0 :     pq_sendint64(&buf, snap->xmax);
     635           0 :     for (i = 0; i < snap->nxip; i++)
     636           0 :         pq_sendint64(&buf, snap->xip[i]);
     637           0 :     PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
     638             : }
     639             : 
     640             : /*
     641             :  * txid_visible_in_snapshot(int8, txid_snapshot) returns bool
     642             :  *
     643             :  *      is txid visible in snapshot ?
     644             :  */
     645             : Datum
     646         340 : txid_visible_in_snapshot(PG_FUNCTION_ARGS)
     647             : {
     648         340 :     txid        value = PG_GETARG_INT64(0);
     649         340 :     TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(1);
     650             : 
     651         340 :     PG_RETURN_BOOL(is_visible_txid(value, snap));
     652             : }
     653             : 
     654             : /*
     655             :  * txid_snapshot_xmin(txid_snapshot) returns int8
     656             :  *
     657             :  *      return snapshot's xmin
     658             :  */
     659             : Datum
     660          20 : txid_snapshot_xmin(PG_FUNCTION_ARGS)
     661             : {
     662          20 :     TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(0);
     663             : 
     664          20 :     PG_RETURN_INT64(snap->xmin);
     665             : }
     666             : 
     667             : /*
     668             :  * txid_snapshot_xmax(txid_snapshot) returns int8
     669             :  *
     670             :  *      return snapshot's xmax
     671             :  */
     672             : Datum
     673          16 : txid_snapshot_xmax(PG_FUNCTION_ARGS)
     674             : {
     675          16 :     TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(0);
     676             : 
     677          16 :     PG_RETURN_INT64(snap->xmax);
     678             : }
     679             : 
     680             : /*
     681             :  * txid_snapshot_xip(txid_snapshot) returns setof int8
     682             :  *
     683             :  *      return in-progress TXIDs in snapshot.
     684             :  */
     685             : Datum
     686         164 : txid_snapshot_xip(PG_FUNCTION_ARGS)
     687             : {
     688             :     FuncCallContext *fctx;
     689             :     TxidSnapshot *snap;
     690             :     txid        value;
     691             : 
     692             :     /* on first call initialize fctx and get copy of snapshot */
     693         164 :     if (SRF_IS_FIRSTCALL())
     694             :     {
     695          16 :         TxidSnapshot *arg = (TxidSnapshot *) PG_GETARG_VARLENA_P(0);
     696             : 
     697          16 :         fctx = SRF_FIRSTCALL_INIT();
     698             : 
     699             :         /* make a copy of user snapshot */
     700          16 :         snap = MemoryContextAlloc(fctx->multi_call_memory_ctx, VARSIZE(arg));
     701          16 :         memcpy(snap, arg, VARSIZE(arg));
     702             : 
     703          16 :         fctx->user_fctx = snap;
     704             :     }
     705             : 
     706             :     /* return values one-by-one */
     707         164 :     fctx = SRF_PERCALL_SETUP();
     708         164 :     snap = fctx->user_fctx;
     709         164 :     if (fctx->call_cntr < snap->nxip)
     710             :     {
     711         148 :         value = snap->xip[fctx->call_cntr];
     712         148 :         SRF_RETURN_NEXT(fctx, Int64GetDatum(value));
     713             :     }
     714             :     else
     715             :     {
     716          16 :         SRF_RETURN_DONE(fctx);
     717             :     }
     718             : }
     719             : 
     720             : /*
     721             :  * Report the status of a recent transaction ID, or null for wrapped,
     722             :  * truncated away or otherwise too old XIDs.
     723             :  *
     724             :  * The passed epoch-qualified xid is treated as a normal xid, not a
     725             :  * multixact id.
     726             :  *
     727             :  * If it points to a committed subxact the result is the subxact status even
     728             :  * though the parent xact may still be in progress or may have aborted.
     729             :  */
     730             : Datum
     731          30 : txid_status(PG_FUNCTION_ARGS)
     732             : {
     733             :     const char *status;
     734          30 :     uint64      xid_with_epoch = PG_GETARG_INT64(0);
     735             :     TransactionId xid;
     736             : 
     737             :     /*
     738             :      * We must protect against concurrent truncation of clog entries to avoid
     739             :      * an I/O error on SLRU lookup.
     740             :      */
     741          30 :     LWLockAcquire(CLogTruncationLock, LW_SHARED);
     742          30 :     if (TransactionIdInRecentPast(xid_with_epoch, &xid))
     743             :     {
     744             :         Assert(TransactionIdIsValid(xid));
     745             : 
     746          22 :         if (TransactionIdIsCurrentTransactionId(xid))
     747           4 :             status = "in progress";
     748          18 :         else if (TransactionIdDidCommit(xid))
     749          12 :             status = "committed";
     750           6 :         else if (TransactionIdDidAbort(xid))
     751           4 :             status = "aborted";
     752             :         else
     753             :         {
     754             :             /*
     755             :              * The xact is not marked as either committed or aborted in clog.
     756             :              *
     757             :              * It could be a transaction that ended without updating clog or
     758             :              * writing an abort record due to a crash. We can safely assume
     759             :              * it's aborted if it isn't committed and is older than our
     760             :              * snapshot xmin.
     761             :              *
     762             :              * Otherwise it must be in-progress (or have been at the time we
     763             :              * checked commit/abort status).
     764             :              */
     765           2 :             if (TransactionIdPrecedes(xid, GetActiveSnapshot()->xmin))
     766           2 :                 status = "aborted";
     767             :             else
     768           0 :                 status = "in progress";
     769             :         }
     770             :     }
     771             :     else
     772             :     {
     773           4 :         status = NULL;
     774             :     }
     775          26 :     LWLockRelease(CLogTruncationLock);
     776             : 
     777          26 :     if (status == NULL)
     778           4 :         PG_RETURN_NULL();
     779             :     else
     780          22 :         PG_RETURN_TEXT_P(cstring_to_text(status));
     781             : }

Generated by: LCOV version 1.13