LCOV - code coverage report
Current view: top level - src/backend/utils/adt - xid8funcs.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 77.2 % 267 206
Test Date: 2026-01-26 10:56:24 Functions: 90.0 % 20 18
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 62.1 % 116 72

             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                 :          14 : TransactionIdInRecentPast(FullTransactionId fxid, TransactionId *extracted_xid)
      99                 :             : {
     100                 :          14 :         TransactionId xid = XidFromFullTransactionId(fxid);
     101                 :          14 :         FullTransactionId now_fullxid;
     102                 :          14 :         TransactionId oldest_clog_xid;
     103                 :          14 :         FullTransactionId oldest_clog_fxid;
     104                 :             : 
     105                 :          14 :         now_fullxid = ReadNextFullTransactionId();
     106                 :             : 
     107         [ -  + ]:          14 :         if (extracted_xid != NULL)
     108                 :          14 :                 *extracted_xid = xid;
     109                 :             : 
     110         [ +  - ]:          14 :         if (!TransactionIdIsValid(xid))
     111                 :           0 :                 return false;
     112                 :             : 
     113                 :             :         /* For non-normal transaction IDs, we can ignore the epoch. */
     114         [ +  + ]:          14 :         if (!TransactionIdIsNormal(xid))
     115                 :           4 :                 return true;
     116                 :             : 
     117                 :             :         /* If the transaction ID is in the future, throw an error. */
     118         [ +  + ]:          10 :         if (!FullTransactionIdPrecedes(fxid, now_fullxid))
     119   [ +  -  +  - ]:           2 :                 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         [ +  - ]:           8 :         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                 :           8 :         oldest_clog_xid = TransamVariables->oldestClogXid;
     145                 :             :         oldest_clog_fxid =
     146                 :           8 :                 FullTransactionIdFromAllowableAt(now_fullxid, oldest_clog_xid);
     147                 :           8 :         return !FullTransactionIdPrecedes(fxid, oldest_clog_fxid);
     148                 :          12 : }
     149                 :             : 
     150                 :             : /*
     151                 :             :  * txid comparator for qsort/bsearch
     152                 :             :  */
     153                 :             : static int
     154                 :         448 : cmp_fxid(const void *aa, const void *bb)
     155                 :             : {
     156                 :         448 :         FullTransactionId a = *(const FullTransactionId *) aa;
     157                 :         448 :         FullTransactionId b = *(const FullTransactionId *) bb;
     158                 :             : 
     159         [ +  + ]:         448 :         if (FullTransactionIdPrecedes(a, b))
     160                 :         108 :                 return -1;
     161         [ +  + ]:         340 :         if (FullTransactionIdPrecedes(b, a))
     162                 :         278 :                 return 1;
     163                 :          62 :         return 0;
     164                 :         448 : }
     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                 :           4 : sort_snapshot(pg_snapshot *snap)
     175                 :             : {
     176         [ +  - ]:           4 :         if (snap->nxip > 1)
     177                 :             :         {
     178                 :           0 :                 qsort(snap->xip, snap->nxip, sizeof(FullTransactionId), cmp_fxid);
     179                 :           0 :                 snap->nxip = qunique(snap->xip, snap->nxip, sizeof(FullTransactionId),
     180                 :             :                                                          cmp_fxid);
     181                 :           0 :         }
     182                 :           4 : }
     183                 :             : 
     184                 :             : /*
     185                 :             :  * check fxid visibility.
     186                 :             :  */
     187                 :             : static bool
     188                 :         170 : is_visible_fxid(FullTransactionId value, const pg_snapshot *snap)
     189                 :             : {
     190         [ +  + ]:         170 :         if (FullTransactionIdPrecedes(value, snap->xmin))
     191                 :          22 :                 return true;
     192         [ +  + ]:         148 :         else if (!FullTransactionIdPrecedes(value, snap->xmax))
     193                 :          28 :                 return false;
     194                 :             : #ifdef USE_BSEARCH_IF_NXIP_GREATER
     195         [ +  + ]:         120 :         else if (snap->nxip > USE_BSEARCH_IF_NXIP_GREATER)
     196                 :             :         {
     197                 :         100 :                 const void *res;
     198                 :             : 
     199                 :         100 :                 res = bsearch(&value, snap->xip, snap->nxip, sizeof(FullTransactionId),
     200                 :             :                                           cmp_fxid);
     201                 :             :                 /* if found, transaction is still in progress */
     202                 :         100 :                 return (res) ? false : true;
     203                 :         100 :         }
     204                 :             : #endif
     205                 :             :         else
     206                 :             :         {
     207                 :          20 :                 uint32          i;
     208                 :             : 
     209         [ +  + ]:          60 :                 for (i = 0; i < snap->nxip; i++)
     210                 :             :                 {
     211         [ +  + ]:          48 :                         if (FullTransactionIdEquals(value, snap->xip[i]))
     212                 :           8 :                                 return false;
     213                 :          40 :                 }
     214                 :          12 :                 return true;
     215                 :          20 :         }
     216                 :         170 : }
     217                 :             : 
     218                 :             : /*
     219                 :             :  * helper functions to use StringInfo for pg_snapshot creation.
     220                 :             :  */
     221                 :             : 
     222                 :             : static StringInfo
     223                 :          31 : buf_init(FullTransactionId xmin, FullTransactionId xmax)
     224                 :             : {
     225                 :          31 :         pg_snapshot snap;
     226                 :          31 :         StringInfo      buf;
     227                 :             : 
     228                 :          31 :         snap.xmin = xmin;
     229                 :          31 :         snap.xmax = xmax;
     230                 :          31 :         snap.nxip = 0;
     231                 :             : 
     232                 :          31 :         buf = makeStringInfo();
     233                 :          31 :         appendBinaryStringInfo(buf, &snap, PG_SNAPSHOT_SIZE(0));
     234                 :          62 :         return buf;
     235                 :          31 : }
     236                 :             : 
     237                 :             : static void
     238                 :         104 : buf_add_txid(StringInfo buf, FullTransactionId fxid)
     239                 :             : {
     240                 :         104 :         pg_snapshot *snap = (pg_snapshot *) buf->data;
     241                 :             : 
     242                 :             :         /* do this before possible realloc */
     243                 :         104 :         snap->nxip++;
     244                 :             : 
     245                 :         104 :         appendBinaryStringInfo(buf, &fxid, sizeof(fxid));
     246                 :         104 : }
     247                 :             : 
     248                 :             : static pg_snapshot *
     249                 :          25 : buf_finalize(StringInfo buf)
     250                 :             : {
     251                 :          25 :         pg_snapshot *snap = (pg_snapshot *) buf->data;
     252                 :             : 
     253                 :          25 :         SET_VARSIZE(snap, buf->len);
     254                 :             : 
     255                 :             :         /* buf is not needed anymore */
     256                 :          25 :         buf->data = NULL;
     257                 :          25 :         pfree(buf);
     258                 :             : 
     259                 :          50 :         return snap;
     260                 :          25 : }
     261                 :             : 
     262                 :             : /*
     263                 :             :  * parse snapshot from cstring
     264                 :             :  */
     265                 :             : static pg_snapshot *
     266                 :          39 : parse_snapshot(const char *str, Node *escontext)
     267                 :             : {
     268                 :          39 :         FullTransactionId xmin;
     269                 :          39 :         FullTransactionId xmax;
     270                 :          39 :         FullTransactionId last_val = InvalidFullTransactionId;
     271                 :          39 :         FullTransactionId val;
     272                 :          39 :         const char *str_start = str;
     273                 :          39 :         char       *endp;
     274                 :          39 :         StringInfo      buf;
     275                 :             : 
     276                 :          39 :         xmin = FullTransactionIdFromU64(strtou64(str, &endp, 10));
     277         [ -  + ]:          39 :         if (*endp != ':')
     278                 :           0 :                 goto bad_format;
     279                 :          39 :         str = endp + 1;
     280                 :             : 
     281                 :          39 :         xmax = FullTransactionIdFromU64(strtou64(str, &endp, 10));
     282         [ -  + ]:          39 :         if (*endp != ':')
     283                 :           0 :                 goto bad_format;
     284                 :          39 :         str = endp + 1;
     285                 :             : 
     286                 :             :         /* it should look sane */
     287         [ +  + ]:          39 :         if (!FullTransactionIdIsValid(xmin) ||
     288   [ +  +  +  + ]:          37 :                 !FullTransactionIdIsValid(xmax) ||
     289                 :          35 :                 FullTransactionIdPrecedes(xmax, xmin))
     290                 :           8 :                 goto bad_format;
     291                 :             : 
     292                 :             :         /* allocate buffer */
     293                 :          31 :         buf = buf_init(xmin, xmax);
     294                 :             : 
     295                 :             :         /* loop over values */
     296         [ +  + ]:         137 :         while (*str != '\0')
     297                 :             :         {
     298                 :             :                 /* read next value */
     299                 :         112 :                 val = FullTransactionIdFromU64(strtou64(str, &endp, 10));
     300                 :         112 :                 str = endp;
     301                 :             : 
     302                 :             :                 /* require the input to be in order */
     303         [ +  + ]:         112 :                 if (FullTransactionIdPrecedes(val, xmin) ||
     304   [ +  -  +  + ]:         110 :                         FullTransactionIdFollowsOrEquals(val, xmax) ||
     305                 :         110 :                         FullTransactionIdPrecedes(val, last_val))
     306                 :           6 :                         goto bad_format;
     307                 :             : 
     308                 :             :                 /* skip duplicates */
     309         [ +  + ]:         106 :                 if (!FullTransactionIdEquals(val, last_val))
     310                 :         104 :                         buf_add_txid(buf, val);
     311                 :         106 :                 last_val = val;
     312                 :             : 
     313         [ +  + ]:         106 :                 if (*str == ',')
     314                 :          86 :                         str++;
     315         [ +  - ]:          20 :                 else if (*str != '\0')
     316                 :           0 :                         goto bad_format;
     317                 :             :         }
     318                 :             : 
     319                 :          25 :         return buf_finalize(buf);
     320                 :             : 
     321                 :             : bad_format:
     322         [ +  + ]:          14 :         ereturn(escontext, NULL,
     323                 :             :                         (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     324                 :             :                          errmsg("invalid input syntax for type %s: \"%s\"",
     325                 :             :                                         "pg_snapshot", str_start)));
     326         [ -  + ]:          39 : }
     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                 :         277 : 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                 :         277 :         PreventCommandDuringRecovery("pg_current_xact_id()");
     344                 :             : 
     345                 :         277 :         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                 :           4 : pg_current_xact_id_if_assigned(PG_FUNCTION_ARGS)
     354                 :             : {
     355                 :           4 :         FullTransactionId topfxid = GetTopFullTransactionIdIfAny();
     356                 :             : 
     357         [ +  + ]:           4 :         if (!FullTransactionIdIsValid(topfxid))
     358                 :           2 :                 PG_RETURN_NULL();
     359                 :             : 
     360                 :           2 :         PG_RETURN_FULLTRANSACTIONID(topfxid);
     361                 :           4 : }
     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                 :           4 : pg_current_snapshot(PG_FUNCTION_ARGS)
     372                 :             : {
     373                 :           4 :         pg_snapshot *snap;
     374                 :           4 :         uint32          nxip,
     375                 :             :                                 i;
     376                 :           4 :         Snapshot        cur;
     377                 :           4 :         FullTransactionId next_fxid = ReadNextFullTransactionId();
     378                 :             : 
     379                 :           4 :         cur = GetActiveSnapshot();
     380         [ +  - ]:           4 :         if (cur == NULL)
     381   [ #  #  #  # ]:           0 :                 elog(ERROR, "no active snapshot set");
     382                 :             : 
     383                 :             :         /* allocate */
     384                 :           4 :         nxip = cur->xcnt;
     385                 :           4 :         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                 :           4 :         snap->xmin = FullTransactionIdFromAllowableAt(next_fxid, cur->xmin);
     394                 :           4 :         snap->xmax = FullTransactionIdFromAllowableAt(next_fxid, cur->xmax);
     395                 :           4 :         snap->nxip = nxip;
     396         [ +  + ]:           8 :         for (i = 0; i < nxip; i++)
     397                 :           8 :                 snap->xip[i] =
     398                 :           4 :                         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                 :           4 :         sort_snapshot(snap);
     408                 :             : 
     409                 :             :         /* set size after sorting, because it may have removed duplicate xips */
     410                 :           4 :         SET_VARSIZE(snap, PG_SNAPSHOT_SIZE(snap->nxip));
     411                 :             : 
     412                 :           8 :         PG_RETURN_POINTER(snap);
     413                 :           4 : }
     414                 :             : 
     415                 :             : /*
     416                 :             :  * pg_snapshot_in(cstring) returns pg_snapshot
     417                 :             :  *
     418                 :             :  *              input function for type pg_snapshot
     419                 :             :  */
     420                 :             : Datum
     421                 :          39 : pg_snapshot_in(PG_FUNCTION_ARGS)
     422                 :             : {
     423                 :          39 :         char       *str = PG_GETARG_CSTRING(0);
     424                 :          39 :         pg_snapshot *snap;
     425                 :             : 
     426                 :          39 :         snap = parse_snapshot(str, fcinfo->context);
     427                 :             : 
     428                 :          78 :         PG_RETURN_POINTER(snap);
     429                 :          39 : }
     430                 :             : 
     431                 :             : /*
     432                 :             :  * pg_snapshot_out(pg_snapshot) returns cstring
     433                 :             :  *
     434                 :             :  *              output function for type pg_snapshot
     435                 :             :  */
     436                 :             : Datum
     437                 :          18 : pg_snapshot_out(PG_FUNCTION_ARGS)
     438                 :             : {
     439                 :          18 :         pg_snapshot *snap = (pg_snapshot *) PG_GETARG_VARLENA_P(0);
     440                 :          18 :         StringInfoData str;
     441                 :          18 :         uint32          i;
     442                 :             : 
     443                 :          18 :         initStringInfo(&str);
     444                 :             : 
     445                 :          18 :         appendStringInfo(&str, UINT64_FORMAT ":",
     446                 :          18 :                                          U64FromFullTransactionId(snap->xmin));
     447                 :          18 :         appendStringInfo(&str, UINT64_FORMAT ":",
     448                 :          18 :                                          U64FromFullTransactionId(snap->xmax));
     449                 :             : 
     450         [ +  + ]:         104 :         for (i = 0; i < snap->nxip; i++)
     451                 :             :         {
     452         [ +  + ]:          86 :                 if (i > 0)
     453                 :          72 :                         appendStringInfoChar(&str, ',');
     454                 :          86 :                 appendStringInfo(&str, UINT64_FORMAT,
     455                 :          86 :                                                  U64FromFullTransactionId(snap->xip[i]));
     456                 :          86 :         }
     457                 :             : 
     458                 :          36 :         PG_RETURN_CSTRING(str.data);
     459                 :          18 : }
     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                 :           0 :         pg_snapshot *snap;
     473                 :           0 :         FullTransactionId last = InvalidFullTransactionId;
     474                 :           0 :         int                     nxip;
     475                 :           0 :         int                     i;
     476                 :           0 :         FullTransactionId xmin;
     477                 :           0 :         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                 :           0 :                 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   [ #  #  #  # ]:           0 :         }
     516                 :           0 :         snap->nxip = nxip;
     517                 :           0 :         SET_VARSIZE(snap, PG_SNAPSHOT_SIZE(nxip));
     518                 :           0 :         PG_RETURN_POINTER(snap);
     519                 :             : 
     520                 :             : bad_format:
     521   [ #  #  #  # ]:           0 :         ereport(ERROR,
     522                 :             :                         (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
     523                 :             :                          errmsg("invalid external pg_snapshot data")));
     524                 :           0 :         PG_RETURN_POINTER(NULL);        /* keep compiler quiet */
     525                 :           0 : }
     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                 :           0 :         StringInfoData buf;
     539                 :           0 :         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                 :           0 : }
     549                 :             : 
     550                 :             : /*
     551                 :             :  * pg_visible_in_snapshot(xid8, pg_snapshot) returns bool
     552                 :             :  *
     553                 :             :  *              is txid visible in snapshot ?
     554                 :             :  */
     555                 :             : Datum
     556                 :         170 : pg_visible_in_snapshot(PG_FUNCTION_ARGS)
     557                 :             : {
     558                 :         170 :         FullTransactionId value = PG_GETARG_FULLTRANSACTIONID(0);
     559                 :         170 :         pg_snapshot *snap = (pg_snapshot *) PG_GETARG_VARLENA_P(1);
     560                 :             : 
     561                 :         340 :         PG_RETURN_BOOL(is_visible_fxid(value, snap));
     562                 :         170 : }
     563                 :             : 
     564                 :             : /*
     565                 :             :  * pg_snapshot_xmin(pg_snapshot) returns xid8
     566                 :             :  *
     567                 :             :  *              return snapshot's xmin
     568                 :             :  */
     569                 :             : Datum
     570                 :          10 : pg_snapshot_xmin(PG_FUNCTION_ARGS)
     571                 :             : {
     572                 :          10 :         pg_snapshot *snap = (pg_snapshot *) PG_GETARG_VARLENA_P(0);
     573                 :             : 
     574                 :          20 :         PG_RETURN_FULLTRANSACTIONID(snap->xmin);
     575                 :          10 : }
     576                 :             : 
     577                 :             : /*
     578                 :             :  * pg_snapshot_xmax(pg_snapshot) returns xid8
     579                 :             :  *
     580                 :             :  *              return snapshot's xmax
     581                 :             :  */
     582                 :             : Datum
     583                 :           8 : pg_snapshot_xmax(PG_FUNCTION_ARGS)
     584                 :             : {
     585                 :           8 :         pg_snapshot *snap = (pg_snapshot *) PG_GETARG_VARLENA_P(0);
     586                 :             : 
     587                 :          16 :         PG_RETURN_FULLTRANSACTIONID(snap->xmax);
     588                 :           8 : }
     589                 :             : 
     590                 :             : /*
     591                 :             :  * pg_snapshot_xip(pg_snapshot) returns setof xid8
     592                 :             :  *
     593                 :             :  *              return in-progress xid8s in snapshot.
     594                 :             :  */
     595                 :             : Datum
     596                 :          82 : pg_snapshot_xip(PG_FUNCTION_ARGS)
     597                 :             : {
     598                 :          82 :         FuncCallContext *fctx;
     599                 :          82 :         pg_snapshot *snap;
     600                 :          82 :         FullTransactionId value;
     601                 :             : 
     602                 :             :         /* on first call initialize fctx and get copy of snapshot */
     603         [ +  + ]:          82 :         if (SRF_IS_FIRSTCALL())
     604                 :             :         {
     605                 :           8 :                 pg_snapshot *arg = (pg_snapshot *) PG_GETARG_VARLENA_P(0);
     606                 :             : 
     607                 :           8 :                 fctx = SRF_FIRSTCALL_INIT();
     608                 :             : 
     609                 :             :                 /* make a copy of user snapshot */
     610                 :           8 :                 snap = MemoryContextAlloc(fctx->multi_call_memory_ctx, VARSIZE(arg));
     611                 :           8 :                 memcpy(snap, arg, VARSIZE(arg));
     612                 :             : 
     613                 :           8 :                 fctx->user_fctx = snap;
     614                 :           8 :         }
     615                 :             : 
     616                 :             :         /* return values one-by-one */
     617                 :          82 :         fctx = SRF_PERCALL_SETUP();
     618                 :          82 :         snap = fctx->user_fctx;
     619         [ +  + ]:          82 :         if (fctx->call_cntr < snap->nxip)
     620                 :             :         {
     621                 :          74 :                 value = snap->xip[fctx->call_cntr];
     622                 :          74 :                 SRF_RETURN_NEXT(fctx, FullTransactionIdGetDatum(value));
     623                 :           0 :         }
     624                 :             :         else
     625                 :             :         {
     626         [ +  - ]:           8 :                 SRF_RETURN_DONE(fctx);
     627                 :             :         }
     628         [ -  + ]:          82 : }
     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                 :          12 : pg_xact_status(PG_FUNCTION_ARGS)
     642                 :             : {
     643                 :          12 :         const char *status;
     644                 :          12 :         FullTransactionId fxid = PG_GETARG_FULLTRANSACTIONID(0);
     645                 :          12 :         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                 :          12 :         LWLockAcquire(XactTruncationLock, LW_SHARED);
     652         [ +  + ]:          12 :         if (TransactionIdInRecentPast(fxid, &xid))
     653                 :             :         {
     654         [ +  - ]:          10 :                 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         [ +  + ]:          10 :                 if (TransactionIdIsInProgress(xid))
     665                 :           2 :                         status = "in progress";
     666         [ +  + ]:           8 :                 else if (TransactionIdDidCommit(xid))
     667                 :           6 :                         status = "committed";
     668                 :             :                 else
     669                 :             :                 {
     670                 :             :                         /* it must have aborted or crashed */
     671                 :           2 :                         status = "aborted";
     672                 :             :                 }
     673                 :          10 :         }
     674                 :             :         else
     675                 :             :         {
     676                 :           2 :                 status = NULL;
     677                 :             :         }
     678                 :          12 :         LWLockRelease(XactTruncationLock);
     679                 :             : 
     680         [ +  + ]:          12 :         if (status == NULL)
     681                 :           2 :                 PG_RETURN_NULL();
     682                 :             :         else
     683                 :          10 :                 PG_RETURN_TEXT_P(cstring_to_text(status));
     684         [ -  + ]:          12 : }
        

Generated by: LCOV version 2.3.2-1