LCOV - code coverage report
Current view: top level - src/backend/utils/adt - xid8funcs.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 77.6 % 205 159
Test Date: 2026-07-22 12:15:41 Functions: 90.0 % 20 18
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 66.0 % 100 66

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

Generated by: LCOV version 2.0-1