LCOV - differential code coverage report
Current view: top level - src/backend/utils/adt - xid8funcs.c (source / functions) Coverage Total Hit LBC UIC UBC GBC GIC GNC CBC EUB ECB DUB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 78.2 % 220 172 2 5 41 1 77 5 89 5 76 1 5
Current Date: 2023-04-08 15:15:32 Functions: 90.5 % 21 19 2 11 2 6 12
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           TLA  Line data    Source code
       1                 : /*-------------------------------------------------------------------------
       2                 :  * xid8funcs.c
       3                 :  *
       4                 :  *  Export internal transaction IDs to user level.
       5                 :  *
       6                 :  * Note that only top-level transaction IDs are exposed to user sessions.
       7                 :  * This is important because xid8s frequently persist beyond the global
       8                 :  * xmin horizon, or may even be shipped to other machines, so we cannot
       9                 :  * rely on being able to correlate subtransaction IDs with their parents
      10                 :  * via functions such as SubTransGetTopmostTransaction().
      11                 :  *
      12                 :  * These functions are used to support the txid_XXX functions and the newer
      13                 :  * pg_current_xact_id, pg_current_snapshot and related fmgr functions, since
      14                 :  * the only difference between them is whether they expose xid8 or int8 values
      15                 :  * to users.  The txid_XXX variants should eventually be dropped.
      16                 :  *
      17                 :  *
      18                 :  *  Copyright (c) 2003-2023, PostgreSQL Global Development Group
      19                 :  *  Author: Jan Wieck, Afilias USA INC.
      20                 :  *  64-bit txids: Marko Kreen, Skype Technologies
      21                 :  *
      22                 :  *  src/backend/utils/adt/xid8funcs.c
      23                 :  *
      24                 :  *-------------------------------------------------------------------------
      25                 :  */
      26                 : 
      27                 : #include "postgres.h"
      28                 : 
      29                 : #include "access/clog.h"
      30                 : #include "access/transam.h"
      31                 : #include "access/xact.h"
      32                 : #include "access/xlog.h"
      33                 : #include "funcapi.h"
      34                 : #include "lib/qunique.h"
      35                 : #include "libpq/pqformat.h"
      36                 : #include "miscadmin.h"
      37                 : #include "postmaster/postmaster.h"
      38                 : #include "storage/lwlock.h"
      39                 : #include "storage/procarray.h"
      40                 : #include "utils/builtins.h"
      41                 : #include "utils/memutils.h"
      42                 : #include "utils/snapmgr.h"
      43                 : #include "utils/xid8.h"
      44                 : 
      45                 : 
      46                 : /*
      47                 :  * If defined, use bsearch() function for searching for xid8s in snapshots
      48                 :  * that have more than the specified number of values.
      49                 :  */
      50                 : #define USE_BSEARCH_IF_NXIP_GREATER 30
      51                 : 
      52                 : 
      53                 : /*
      54                 :  * Snapshot containing FullTransactionIds.
      55                 :  */
      56                 : typedef struct
      57                 : {
      58                 :     /*
      59                 :      * 4-byte length hdr, should not be touched directly.
      60                 :      *
      61                 :      * Explicit embedding is ok as we want always correct alignment anyway.
      62                 :      */
      63                 :     int32       __varsz;
      64                 : 
      65                 :     uint32      nxip;           /* number of fxids in xip array */
      66                 :     FullTransactionId xmin;
      67                 :     FullTransactionId xmax;
      68                 :     /* in-progress fxids, xmin <= xip[i] < xmax: */
      69                 :     FullTransactionId xip[FLEXIBLE_ARRAY_MEMBER];
      70                 : } pg_snapshot;
      71                 : 
      72                 : #define PG_SNAPSHOT_SIZE(nxip) \
      73                 :     (offsetof(pg_snapshot, xip) + sizeof(FullTransactionId) * (nxip))
      74                 : #define PG_SNAPSHOT_MAX_NXIP \
      75                 :     ((MaxAllocSize - offsetof(pg_snapshot, xip)) / sizeof(FullTransactionId))
      76                 : 
      77                 : /*
      78                 :  * Compile-time limits on the procarray (MAX_BACKENDS processes plus
      79                 :  * MAX_BACKENDS prepared transactions) guarantee nxip won't be too large.
      80                 :  */
      81                 : StaticAssertDecl(MAX_BACKENDS * 2 <= PG_SNAPSHOT_MAX_NXIP,
      82                 :                  "possible overflow in pg_current_snapshot()");
      83                 : 
      84                 : 
      85                 : /*
      86                 :  * Helper to get a TransactionId from a 64-bit xid with wraparound detection.
      87                 :  *
      88                 :  * It is an ERROR if the xid is in the future.  Otherwise, returns true if
      89                 :  * the transaction is still new enough that we can determine whether it
      90                 :  * committed and false otherwise.  If *extracted_xid is not NULL, it is set
      91                 :  * to the low 32 bits of the transaction ID (i.e. the actual XID, without the
      92                 :  * epoch).
      93                 :  *
      94                 :  * The caller must hold XactTruncationLock since it's dealing with arbitrary
      95                 :  * XIDs, and must continue to hold it until it's done with any clog lookups
      96                 :  * relating to those XIDs.
      97                 :  */
      98                 : static bool
      99 GIC          42 : TransactionIdInRecentPast(FullTransactionId fxid, TransactionId *extracted_xid)
     100                 : {
     101              42 :     uint32      xid_epoch = EpochFromFullTransactionId(fxid);
     102              42 :     TransactionId xid = XidFromFullTransactionId(fxid);
     103                 :     uint32      now_epoch;
     104                 :     TransactionId now_epoch_next_xid;
     105                 :     FullTransactionId now_fullxid;
     106                 : 
     107 CBC          42 :     now_fullxid = ReadNextFullTransactionId();
     108 GIC          42 :     now_epoch_next_xid = XidFromFullTransactionId(now_fullxid);
     109 CBC          42 :     now_epoch = EpochFromFullTransactionId(now_fullxid);
     110 ECB             : 
     111 GIC          42 :     if (extracted_xid != NULL)
     112              42 :         *extracted_xid = xid;
     113                 : 
     114              42 :     if (!TransactionIdIsValid(xid))
     115 LBC           0 :         return false;
     116 ECB             : 
     117                 :     /* For non-normal transaction IDs, we can ignore the epoch. */
     118 GIC          42 :     if (!TransactionIdIsNormal(xid))
     119 CBC          12 :         return true;
     120 ECB             : 
     121                 :     /* If the transaction ID is in the future, throw an error. */
     122 CBC          30 :     if (!FullTransactionIdPrecedes(fxid, now_fullxid))
     123 GBC           6 :         ereport(ERROR,
     124                 :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     125                 :                  errmsg("transaction ID %llu is in the future",
     126 ECB             :                         (unsigned long long) U64FromFullTransactionId(fxid))));
     127                 : 
     128                 :     /*
     129                 :      * ShmemVariableCache->oldestClogXid is protected by XactTruncationLock,
     130                 :      * but we don't acquire that lock here.  Instead, we require the caller to
     131                 :      * acquire it, because the caller is presumably going to look up the
     132                 :      * returned XID.  If we took and released the lock within this function, a
     133                 :      * CLOG truncation could occur before the caller finished with the XID.
     134                 :      */
     135 GIC          24 :     Assert(LWLockHeldByMe(XactTruncationLock));
     136                 : 
     137                 :     /*
     138                 :      * If the transaction ID has wrapped around, it's definitely too old to
     139                 :      * determine the commit status.  Otherwise, we can compare it to
     140                 :      * ShmemVariableCache->oldestClogXid to determine whether the relevant
     141                 :      * CLOG entry is guaranteed to still exist.
     142                 :      */
     143 CBC          24 :     if (xid_epoch + 1 < now_epoch
     144 GIC          24 :         || (xid_epoch + 1 == now_epoch && xid < now_epoch_next_xid)
     145              24 :         || TransactionIdPrecedes(xid, ShmemVariableCache->oldestClogXid))
     146               6 :         return false;
     147                 : 
     148              18 :     return true;
     149                 : }
     150                 : 
     151 ECB             : /*
     152                 :  * Convert a TransactionId obtained from a snapshot held by the caller to a
     153                 :  * FullTransactionId.  Use next_fxid as a reference FullTransactionId, so that
     154                 :  * we can compute the high order bits.  It must have been obtained by the
     155                 :  * caller with ReadNextFullTransactionId() after the snapshot was created.
     156                 :  */
     157                 : static FullTransactionId
     158 GIC          45 : widen_snapshot_xid(TransactionId xid, FullTransactionId next_fxid)
     159                 : {
     160              45 :     TransactionId next_xid = XidFromFullTransactionId(next_fxid);
     161              45 :     uint32      epoch = EpochFromFullTransactionId(next_fxid);
     162                 : 
     163                 :     /* Special transaction ID. */
     164              45 :     if (!TransactionIdIsNormal(xid))
     165 UIC           0 :         return FullTransactionIdFromEpochAndXid(0, xid);
     166 ECB             : 
     167                 :     /*
     168                 :      * The 64 bit result must be <= next_fxid, since next_fxid hadn't been
     169                 :      * issued yet when the snapshot was created.  Every TransactionId in the
     170                 :      * snapshot must therefore be from the same epoch as next_fxid, or the
     171                 :      * epoch before.  We know this because next_fxid is never allow to get
     172                 :      * more than one epoch ahead of the TransactionIds in any snapshot.
     173 EUB             :      */
     174 GIC          45 :     if (xid > next_xid)
     175 UIC           0 :         epoch--;
     176                 : 
     177 GIC          45 :     return FullTransactionIdFromEpochAndXid(epoch, xid);
     178                 : }
     179                 : 
     180                 : /*
     181                 :  * txid comparator for qsort/bsearch
     182 ECB             :  */
     183 EUB             : static int
     184 GIC        1367 : cmp_fxid(const void *aa, const void *bb)
     185 ECB             : {
     186 GIC        1367 :     FullTransactionId a = *(const FullTransactionId *) aa;
     187            1367 :     FullTransactionId b = *(const FullTransactionId *) bb;
     188                 : 
     189            1367 :     if (FullTransactionIdPrecedes(a, b))
     190             329 :         return -1;
     191            1038 :     if (FullTransactionIdPrecedes(b, a))
     192 CBC         852 :         return 1;
     193 GIC         186 :     return 0;
     194 ECB             : }
     195                 : 
     196                 : /*
     197                 :  * Sort a snapshot's txids, so we can use bsearch() later.  Also remove
     198                 :  * any duplicates.
     199                 :  *
     200                 :  * For consistency of on-disk representation, we always sort even if bsearch
     201                 :  * will not be used.
     202                 :  */
     203                 : static void
     204 GIC          12 : sort_snapshot(pg_snapshot *snap)
     205                 : {
     206              12 :     if (snap->nxip > 1)
     207                 :     {
     208              10 :         qsort(snap->xip, snap->nxip, sizeof(FullTransactionId), cmp_fxid);
     209              10 :         snap->nxip = qunique(snap->xip, snap->nxip, sizeof(FullTransactionId),
     210                 :                              cmp_fxid);
     211                 :     }
     212 CBC          12 : }
     213                 : 
     214 ECB             : /*
     215                 :  * check fxid visibility.
     216                 :  */
     217                 : static bool
     218 GIC         510 : is_visible_fxid(FullTransactionId value, const pg_snapshot *snap)
     219                 : {
     220 CBC         510 :     if (FullTransactionIdPrecedes(value, snap->xmin))
     221 GIC          66 :         return true;
     222             444 :     else if (!FullTransactionIdPrecedes(value, snap->xmax))
     223              84 :         return false;
     224                 : #ifdef USE_BSEARCH_IF_NXIP_GREATER
     225             360 :     else if (snap->nxip > USE_BSEARCH_IF_NXIP_GREATER)
     226 ECB             :     {
     227                 :         void       *res;
     228                 : 
     229 CBC         300 :         res = bsearch(&value, snap->xip, snap->nxip, sizeof(FullTransactionId),
     230 ECB             :                       cmp_fxid);
     231                 :         /* if found, transaction is still in progress */
     232 GIC         300 :         return (res) ? false : true;
     233 ECB             :     }
     234                 : #endif
     235                 :     else
     236                 :     {
     237                 :         uint32      i;
     238                 : 
     239 GIC         180 :         for (i = 0; i < snap->nxip; i++)
     240 ECB             :         {
     241 GIC         144 :             if (FullTransactionIdEquals(value, snap->xip[i]))
     242              24 :                 return false;
     243                 :         }
     244              36 :         return true;
     245                 :     }
     246                 : }
     247 ECB             : 
     248                 : /*
     249                 :  * helper functions to use StringInfo for pg_snapshot creation.
     250                 :  */
     251                 : 
     252                 : static StringInfo
     253 GIC          93 : buf_init(FullTransactionId xmin, FullTransactionId xmax)
     254                 : {
     255                 :     pg_snapshot snap;
     256                 :     StringInfo  buf;
     257                 : 
     258              93 :     snap.xmin = xmin;
     259              93 :     snap.xmax = xmax;
     260              93 :     snap.nxip = 0;
     261 ECB             : 
     262 GIC          93 :     buf = makeStringInfo();
     263 GNC          93 :     appendBinaryStringInfo(buf, &snap, PG_SNAPSHOT_SIZE(0));
     264 GIC          93 :     return buf;
     265                 : }
     266 ECB             : 
     267                 : static void
     268 CBC         312 : buf_add_txid(StringInfo buf, FullTransactionId fxid)
     269                 : {
     270             312 :     pg_snapshot *snap = (pg_snapshot *) buf->data;
     271 ECB             : 
     272                 :     /* do this before possible realloc */
     273 GIC         312 :     snap->nxip++;
     274                 : 
     275 GNC         312 :     appendBinaryStringInfo(buf, &fxid, sizeof(fxid));
     276 CBC         312 : }
     277                 : 
     278 ECB             : static pg_snapshot *
     279 GIC          75 : buf_finalize(StringInfo buf)
     280                 : {
     281 CBC          75 :     pg_snapshot *snap = (pg_snapshot *) buf->data;
     282                 : 
     283              75 :     SET_VARSIZE(snap, buf->len);
     284 ECB             : 
     285                 :     /* buf is not needed anymore */
     286 GIC          75 :     buf->data = NULL;
     287 CBC          75 :     pfree(buf);
     288                 : 
     289              75 :     return snap;
     290                 : }
     291 ECB             : 
     292                 : /*
     293                 :  * parse snapshot from cstring
     294                 :  */
     295                 : static pg_snapshot *
     296 GNC         117 : parse_snapshot(const char *str, Node *escontext)
     297 ECB             : {
     298                 :     FullTransactionId xmin;
     299                 :     FullTransactionId xmax;
     300 GIC         117 :     FullTransactionId last_val = InvalidFullTransactionId;
     301                 :     FullTransactionId val;
     302             117 :     const char *str_start = str;
     303                 :     char       *endp;
     304 ECB             :     StringInfo  buf;
     305                 : 
     306 GIC         117 :     xmin = FullTransactionIdFromU64(strtou64(str, &endp, 10));
     307             117 :     if (*endp != ':')
     308 LBC           0 :         goto bad_format;
     309 GIC         117 :     str = endp + 1;
     310 ECB             : 
     311 GIC         117 :     xmax = FullTransactionIdFromU64(strtou64(str, &endp, 10));
     312             117 :     if (*endp != ':')
     313 UIC           0 :         goto bad_format;
     314 CBC         117 :     str = endp + 1;
     315 ECB             : 
     316 EUB             :     /* it should look sane */
     317 CBC         117 :     if (!FullTransactionIdIsValid(xmin) ||
     318 GIC         111 :         !FullTransactionIdIsValid(xmax) ||
     319 CBC         105 :         FullTransactionIdPrecedes(xmax, xmin))
     320              24 :         goto bad_format;
     321 EUB             : 
     322 ECB             :     /* allocate buffer */
     323 GIC          93 :     buf = buf_init(xmin, xmax);
     324                 : 
     325 ECB             :     /* loop over values */
     326 CBC         411 :     while (*str != '\0')
     327 ECB             :     {
     328                 :         /* read next value */
     329 GIC         336 :         val = FullTransactionIdFromU64(strtou64(str, &endp, 10));
     330             336 :         str = endp;
     331 ECB             : 
     332                 :         /* require the input to be in order */
     333 GIC         336 :         if (FullTransactionIdPrecedes(val, xmin) ||
     334 CBC         330 :             FullTransactionIdFollowsOrEquals(val, xmax) ||
     335 GIC         330 :             FullTransactionIdPrecedes(val, last_val))
     336              18 :             goto bad_format;
     337 ECB             : 
     338                 :         /* skip duplicates */
     339 GIC         318 :         if (!FullTransactionIdEquals(val, last_val))
     340             312 :             buf_add_txid(buf, val);
     341 CBC         318 :         last_val = val;
     342 ECB             : 
     343 CBC         318 :         if (*str == ',')
     344             258 :             str++;
     345 GIC          60 :         else if (*str != '\0')
     346 UIC           0 :             goto bad_format;
     347 ECB             :     }
     348                 : 
     349 CBC          75 :     return buf_finalize(buf);
     350                 : 
     351              42 : bad_format:
     352 GNC          42 :     ereturn(escontext, NULL,
     353 ECB             :             (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     354 EUB             :              errmsg("invalid input syntax for type %s: \"%s\"",
     355                 :                     "pg_snapshot", str_start)));
     356 ECB             : }
     357                 : 
     358                 : /*
     359                 :  * pg_current_xact_id() returns xid8
     360                 :  *
     361                 :  *  Return the current toplevel full transaction ID.
     362                 :  *  If the current transaction does not have one, one is assigned.
     363                 :  */
     364                 : Datum
     365 GIC        2865 : pg_current_xact_id(PG_FUNCTION_ARGS)
     366                 : {
     367                 :     /*
     368                 :      * Must prevent during recovery because if an xid is not assigned we try
     369                 :      * to assign one, which would fail. Programs already rely on this function
     370                 :      * to always return a valid current xid, so we should not change this to
     371                 :      * return NULL or similar invalid xid.
     372 ECB             :      */
     373 GIC        2865 :     PreventCommandDuringRecovery("pg_current_xact_id()");
     374                 : 
     375            2865 :     PG_RETURN_FULLTRANSACTIONID(GetTopFullTransactionId());
     376                 : }
     377                 : 
     378                 : /*
     379                 :  * Same as pg_current_xact_id() but doesn't assign a new xid if there
     380 ECB             :  * isn't one yet.
     381                 :  */
     382                 : Datum
     383 GIC          12 : pg_current_xact_id_if_assigned(PG_FUNCTION_ARGS)
     384                 : {
     385              12 :     FullTransactionId topfxid = GetTopFullTransactionIdIfAny();
     386                 : 
     387              12 :     if (!FullTransactionIdIsValid(topfxid))
     388               6 :         PG_RETURN_NULL();
     389                 : 
     390 CBC           6 :     PG_RETURN_FULLTRANSACTIONID(topfxid);
     391                 : }
     392 ECB             : 
     393                 : /*
     394                 :  * pg_current_snapshot() returns pg_snapshot
     395                 :  *
     396                 :  *      Return current snapshot
     397                 :  *
     398                 :  * Note that only top-transaction XIDs are included in the snapshot.
     399                 :  */
     400                 : Datum
     401 GIC          12 : pg_current_snapshot(PG_FUNCTION_ARGS)
     402                 : {
     403                 :     pg_snapshot *snap;
     404                 :     uint32      nxip,
     405                 :                 i;
     406                 :     Snapshot    cur;
     407              12 :     FullTransactionId next_fxid = ReadNextFullTransactionId();
     408 ECB             : 
     409 GIC          12 :     cur = GetActiveSnapshot();
     410              12 :     if (cur == NULL)
     411 UIC           0 :         elog(ERROR, "no active snapshot set");
     412                 : 
     413                 :     /* allocate */
     414 CBC          12 :     nxip = cur->xcnt;
     415              12 :     snap = palloc(PG_SNAPSHOT_SIZE(nxip));
     416                 : 
     417                 :     /* fill */
     418              12 :     snap->xmin = widen_snapshot_xid(cur->xmin, next_fxid);
     419              12 :     snap->xmax = widen_snapshot_xid(cur->xmax, next_fxid);
     420              12 :     snap->nxip = nxip;
     421              33 :     for (i = 0; i < nxip; i++)
     422              21 :         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              12 :     sort_snapshot(snap);
     432                 : 
     433                 :     /* set size after sorting, because it may have removed duplicate xips */
     434              12 :     SET_VARSIZE(snap, PG_SNAPSHOT_SIZE(snap->nxip));
     435                 : 
     436              12 :     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             117 : pg_snapshot_in(PG_FUNCTION_ARGS)
     446                 : {
     447             117 :     char       *str = PG_GETARG_CSTRING(0);
     448                 :     pg_snapshot *snap;
     449                 : 
     450 GNC         117 :     snap = parse_snapshot(str, fcinfo->context);
     451                 : 
     452 CBC          87 :     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              62 : pg_snapshot_out(PG_FUNCTION_ARGS)
     462                 : {
     463              62 :     pg_snapshot *snap = (pg_snapshot *) PG_GETARG_VARLENA_P(0);
     464                 :     StringInfoData str;
     465                 :     uint32      i;
     466                 : 
     467              62 :     initStringInfo(&str);
     468                 : 
     469              62 :     appendStringInfo(&str, UINT64_FORMAT ":",
     470                 :                      U64FromFullTransactionId(snap->xmin));
     471              62 :     appendStringInfo(&str, UINT64_FORMAT ":",
     472                 :                      U64FromFullTransactionId(snap->xmax));
     473                 : 
     474             344 :     for (i = 0; i < snap->nxip; i++)
     475                 :     {
     476             282 :         if (i > 0)
     477             232 :             appendStringInfoChar(&str, ',');
     478             282 :         appendStringInfo(&str, UINT64_FORMAT,
     479                 :                          U64FromFullTransactionId(snap->xip[i]));
     480                 :     }
     481                 : 
     482              62 :     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 UBC           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 CBC         510 : pg_visible_in_snapshot(PG_FUNCTION_ARGS)
     581                 : {
     582             510 :     FullTransactionId value = PG_GETARG_FULLTRANSACTIONID(0);
     583             510 :     pg_snapshot *snap = (pg_snapshot *) PG_GETARG_VARLENA_P(1);
     584                 : 
     585             510 :     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              30 : pg_snapshot_xmin(PG_FUNCTION_ARGS)
     595                 : {
     596              30 :     pg_snapshot *snap = (pg_snapshot *) PG_GETARG_VARLENA_P(0);
     597                 : 
     598              30 :     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              24 : pg_snapshot_xmax(PG_FUNCTION_ARGS)
     608                 : {
     609              24 :     pg_snapshot *snap = (pg_snapshot *) PG_GETARG_VARLENA_P(0);
     610                 : 
     611              24 :     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             246 : 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             246 :     if (SRF_IS_FIRSTCALL())
     628                 :     {
     629              24 :         pg_snapshot *arg = (pg_snapshot *) PG_GETARG_VARLENA_P(0);
     630                 : 
     631              24 :         fctx = SRF_FIRSTCALL_INIT();
     632                 : 
     633                 :         /* make a copy of user snapshot */
     634              24 :         snap = MemoryContextAlloc(fctx->multi_call_memory_ctx, VARSIZE(arg));
     635              24 :         memcpy(snap, arg, VARSIZE(arg));
     636                 : 
     637              24 :         fctx->user_fctx = snap;
     638                 :     }
     639                 : 
     640                 :     /* return values one-by-one */
     641             246 :     fctx = SRF_PERCALL_SETUP();
     642             246 :     snap = fctx->user_fctx;
     643             246 :     if (fctx->call_cntr < snap->nxip)
     644                 :     {
     645             222 :         value = snap->xip[fctx->call_cntr];
     646             222 :         SRF_RETURN_NEXT(fctx, FullTransactionIdGetDatum(value));
     647                 :     }
     648                 :     else
     649                 :     {
     650              24 :         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              42 : pg_xact_status(PG_FUNCTION_ARGS)
     666                 : {
     667                 :     const char *status;
     668              42 :     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              42 :     LWLockAcquire(XactTruncationLock, LW_SHARED);
     676              42 :     if (TransactionIdInRecentPast(fxid, &xid))
     677                 :     {
     678              30 :         Assert(TransactionIdIsValid(xid));
     679                 : 
     680                 :         /*
     681                 :          * Like when doing visiblity checks on a row, check whether the
     682                 :          * transaction is still in progress before looking into the CLOG.
     683                 :          * Otherwise we would incorrectly return "committed" for a transaction
     684                 :          * that is committing and has already updated the CLOG, but hasn't
     685                 :          * removed its XID from the proc array yet. (See comment on that race
     686                 :          * condition at the top of heapam_visibility.c)
     687                 :          */
     688              30 :         if (TransactionIdIsInProgress(xid))
     689               6 :             status = "in progress";
     690              24 :         else if (TransactionIdDidCommit(xid))
     691              18 :             status = "committed";
     692                 :         else
     693                 :         {
     694                 :             /* it must have aborted or crashed */
     695               6 :             status = "aborted";
     696                 :         }
     697                 :     }
     698                 :     else
     699                 :     {
     700               6 :         status = NULL;
     701                 :     }
     702              36 :     LWLockRelease(XactTruncationLock);
     703                 : 
     704              36 :     if (status == NULL)
     705               6 :         PG_RETURN_NULL();
     706                 :     else
     707              30 :         PG_RETURN_TEXT_P(cstring_to_text(status));
     708                 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a