LCOV - code coverage report
Current view: top level - src/backend/replication/logical - snapbuild.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 85.3 % 558 476
Test Date: 2026-07-17 01:15:41 Functions: 100.0 % 32 32
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 59.3 % 396 235

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * snapbuild.c
       4                 :             :  *
       5                 :             :  *    Infrastructure for building historic catalog snapshots based on contents
       6                 :             :  *    of the WAL, for the purpose of decoding heapam.c style values in the
       7                 :             :  *    WAL.
       8                 :             :  *
       9                 :             :  * NOTES:
      10                 :             :  *
      11                 :             :  * We build snapshots which can *only* be used to read catalog contents and we
      12                 :             :  * do so by reading and interpreting the WAL stream. The aim is to build a
      13                 :             :  * snapshot that behaves the same as a freshly taken MVCC snapshot would have
      14                 :             :  * at the time the XLogRecord was generated.
      15                 :             :  *
      16                 :             :  * To build the snapshots we reuse the infrastructure built for Hot
      17                 :             :  * Standby. The in-memory snapshots we build look different than HS' because
      18                 :             :  * we have different needs. To successfully decode data from the WAL we only
      19                 :             :  * need to access catalog tables and (sys|rel|cat)cache, not the actual user
      20                 :             :  * tables since the data we decode is wholly contained in the WAL
      21                 :             :  * records. Also, our snapshots need to be different in comparison to normal
      22                 :             :  * MVCC ones because in contrast to those we cannot fully rely on the clog and
      23                 :             :  * pg_subtrans for information about committed transactions because they might
      24                 :             :  * commit in the future from the POV of the WAL entry we're currently
      25                 :             :  * decoding. This definition has the advantage that we only need to prevent
      26                 :             :  * removal of catalog rows, while normal table's rows can still be
      27                 :             :  * removed. This is achieved by using the replication slot mechanism.
      28                 :             :  *
      29                 :             :  * As the percentage of transactions modifying the catalog normally is fairly
      30                 :             :  * small in comparisons to ones only manipulating user data, we keep track of
      31                 :             :  * the committed catalog modifying ones inside [xmin, xmax) instead of keeping
      32                 :             :  * track of all running transactions like it's done in a normal snapshot. Note
      33                 :             :  * that we're generally only looking at transactions that have acquired an
      34                 :             :  * xid. That is we keep a list of transactions between snapshot->(xmin, xmax)
      35                 :             :  * that we consider committed, everything else is considered aborted/in
      36                 :             :  * progress. That also allows us not to care about subtransactions before they
      37                 :             :  * have committed which means this module, in contrast to HS, doesn't have to
      38                 :             :  * care about suboverflowed subtransactions and similar.
      39                 :             :  *
      40                 :             :  * One complexity of doing this is that to e.g. handle mixed DDL/DML
      41                 :             :  * transactions we need Snapshots that see intermediate versions of the
      42                 :             :  * catalog in a transaction. During normal operation this is achieved by using
      43                 :             :  * CommandIds/cmin/cmax. The problem with that however is that for space
      44                 :             :  * efficiency reasons, the cmin and cmax are not included in WAL records. We
      45                 :             :  * cannot read the cmin/cmax from the tuple itself, either, because it is
      46                 :             :  * reset on crash recovery. Even if we could, we could not decode combocids
      47                 :             :  * which are only tracked in the original backend's memory. To work around
      48                 :             :  * that, heapam writes an extra WAL record (XLOG_HEAP2_NEW_CID) every time a
      49                 :             :  * catalog row is modified, which includes the cmin and cmax of the
      50                 :             :  * tuple. During decoding, we insert the ctid->(cmin,cmax) mappings into the
      51                 :             :  * reorder buffer, and use them at visibility checks instead of the cmin/cmax
      52                 :             :  * on the tuple itself. Check the reorderbuffer.c's comment above
      53                 :             :  * ResolveCminCmaxDuringDecoding() for details.
      54                 :             :  *
      55                 :             :  * To facilitate all this we need our own visibility routine, as the normal
      56                 :             :  * ones are optimized for different usecases.
      57                 :             :  *
      58                 :             :  * To replace the normal catalog snapshots with decoding ones use the
      59                 :             :  * SetupHistoricSnapshot() and TeardownHistoricSnapshot() functions.
      60                 :             :  *
      61                 :             :  *
      62                 :             :  *
      63                 :             :  * The snapbuild machinery is starting up in several stages, as illustrated
      64                 :             :  * by the following graph describing the SnapBuild->state transitions:
      65                 :             :  *
      66                 :             :  *         +-------------------------+
      67                 :             :  *    +----|         START           |-------------+
      68                 :             :  *    |    +-------------------------+             |
      69                 :             :  *    |                 |                          |
      70                 :             :  *    |                 |                          |
      71                 :             :  *    |        running_xacts #1                    |
      72                 :             :  *    |                 |                          |
      73                 :             :  *    |                 |                          |
      74                 :             :  *    |                 v                          |
      75                 :             :  *    |    +-------------------------+             v
      76                 :             :  *    |    |   BUILDING_SNAPSHOT     |------------>|
      77                 :             :  *    |    +-------------------------+             |
      78                 :             :  *    |                 |                          |
      79                 :             :  *    |                 |                          |
      80                 :             :  *    | running_xacts #2, xacts from #1 finished   |
      81                 :             :  *    |                 |                          |
      82                 :             :  *    |                 |                          |
      83                 :             :  *    |                 v                          |
      84                 :             :  *    |    +-------------------------+             v
      85                 :             :  *    |    |       FULL_SNAPSHOT     |------------>|
      86                 :             :  *    |    +-------------------------+             |
      87                 :             :  *    |                 |                          |
      88                 :             :  * running_xacts        |                      saved snapshot
      89                 :             :  * with zero xacts      |                 at running_xacts's lsn
      90                 :             :  *    |                 |                          |
      91                 :             :  *    | running_xacts with xacts from #2 finished  |
      92                 :             :  *    |                 |                          |
      93                 :             :  *    |                 v                          |
      94                 :             :  *    |    +-------------------------+             |
      95                 :             :  *    +--->|SNAPBUILD_CONSISTENT  |<------------+
      96                 :             :  *         +-------------------------+
      97                 :             :  *
      98                 :             :  * Initially the machinery is in the START stage. When an xl_running_xacts
      99                 :             :  * record is read that is sufficiently new (above the safe xmin horizon),
     100                 :             :  * there's a state transition. If there were no running xacts when the
     101                 :             :  * xl_running_xacts record was generated, we'll directly go into CONSISTENT
     102                 :             :  * state, otherwise we'll switch to the BUILDING_SNAPSHOT state. Having a full
     103                 :             :  * snapshot means that all transactions that start henceforth can be decoded
     104                 :             :  * in their entirety, but transactions that started previously can't. In
     105                 :             :  * FULL_SNAPSHOT we'll switch into CONSISTENT once all those previously
     106                 :             :  * running transactions have committed or aborted.
     107                 :             :  *
     108                 :             :  * Only transactions that commit after CONSISTENT state has been reached will
     109                 :             :  * be replayed, even though they might have started while still in
     110                 :             :  * FULL_SNAPSHOT. That ensures that we'll reach a point where no previous
     111                 :             :  * changes has been exported, but all the following ones will be. That point
     112                 :             :  * is a convenient point to initialize replication from, which is why we
     113                 :             :  * export a snapshot at that point, which *can* be used to read normal data.
     114                 :             :  *
     115                 :             :  * Copyright (c) 2012-2026, PostgreSQL Global Development Group
     116                 :             :  *
     117                 :             :  * IDENTIFICATION
     118                 :             :  *    src/backend/replication/logical/snapbuild.c
     119                 :             :  *
     120                 :             :  *-------------------------------------------------------------------------
     121                 :             :  */
     122                 :             : 
     123                 :             : #include "postgres.h"
     124                 :             : 
     125                 :             : #include <sys/stat.h>
     126                 :             : #include <unistd.h>
     127                 :             : 
     128                 :             : #include "access/heapam_xlog.h"
     129                 :             : #include "access/transam.h"
     130                 :             : #include "access/xact.h"
     131                 :             : #include "common/file_utils.h"
     132                 :             : #include "miscadmin.h"
     133                 :             : #include "pgstat.h"
     134                 :             : #include "replication/logical.h"
     135                 :             : #include "replication/reorderbuffer.h"
     136                 :             : #include "replication/snapbuild.h"
     137                 :             : #include "replication/snapbuild_internal.h"
     138                 :             : #include "storage/fd.h"
     139                 :             : #include "storage/lmgr.h"
     140                 :             : #include "storage/proc.h"
     141                 :             : #include "storage/procarray.h"
     142                 :             : #include "storage/standby.h"
     143                 :             : #include "utils/builtins.h"
     144                 :             : #include "utils/memutils.h"
     145                 :             : #include "utils/snapmgr.h"
     146                 :             : #include "utils/snapshot.h"
     147                 :             : #include "utils/wait_event.h"
     148                 :             : 
     149                 :             : 
     150                 :             : /*
     151                 :             :  * Starting a transaction -- which we need to do while exporting a snapshot --
     152                 :             :  * removes knowledge about the previously used resowner, so we save it here.
     153                 :             :  */
     154                 :             : static ResourceOwner SavedResourceOwnerDuringExport = NULL;
     155                 :             : static bool ExportInProgress = false;
     156                 :             : 
     157                 :             : /* ->committed and ->catchange manipulation */
     158                 :             : static void SnapBuildPurgeOlderTxn(SnapBuild *builder);
     159                 :             : 
     160                 :             : /* snapshot building/manipulation/distribution functions */
     161                 :             : static Snapshot SnapBuildBuildSnapshot(SnapBuild *builder);
     162                 :             : 
     163                 :             : static void SnapBuildFreeSnapshot(Snapshot snap);
     164                 :             : 
     165                 :             : static void SnapBuildSnapIncRefcount(Snapshot snap);
     166                 :             : 
     167                 :             : static void SnapBuildDistributeSnapshotAndInval(SnapBuild *builder, XLogRecPtr lsn, TransactionId xid);
     168                 :             : 
     169                 :             : static inline bool SnapBuildXidHasCatalogChanges(SnapBuild *builder, TransactionId xid,
     170                 :             :                                                  uint32 xinfo);
     171                 :             : 
     172                 :             : /* xlog reading helper functions for SnapBuildProcessRunningXacts */
     173                 :             : static bool SnapBuildFindSnapshot(SnapBuild *builder, XLogRecPtr lsn,
     174                 :             :                                   xl_running_xacts *running);
     175                 :             : static void SnapBuildWaitSnapshot(xl_running_xacts *running, TransactionId cutoff);
     176                 :             : 
     177                 :             : /* serialization functions */
     178                 :             : static void SnapBuildSerialize(SnapBuild *builder, XLogRecPtr lsn);
     179                 :             : static bool SnapBuildRestore(SnapBuild *builder, XLogRecPtr lsn);
     180                 :             : static void SnapBuildRestoreContents(int fd, void *dest, Size size, const char *path);
     181                 :             : 
     182                 :             : /*
     183                 :             :  * Allocate a new snapshot builder.
     184                 :             :  *
     185                 :             :  * xmin_horizon is the xid >= which we can be sure no catalog rows have been
     186                 :             :  * removed, start_lsn is the LSN >= we want to replay commits.
     187                 :             :  */
     188                 :             : SnapBuild *
     189                 :        1230 : AllocateSnapshotBuilder(ReorderBuffer *reorder,
     190                 :             :                         TransactionId xmin_horizon,
     191                 :             :                         XLogRecPtr start_lsn,
     192                 :             :                         bool need_full_snapshot,
     193                 :             :                         bool in_slot_creation,
     194                 :             :                         XLogRecPtr two_phase_at)
     195                 :             : {
     196                 :             :     MemoryContext context;
     197                 :             :     MemoryContext oldcontext;
     198                 :             :     SnapBuild  *builder;
     199                 :             : 
     200                 :             :     /* allocate memory in own context, to have better accountability */
     201                 :        1230 :     context = AllocSetContextCreate(CurrentMemoryContext,
     202                 :             :                                     "snapshot builder context",
     203                 :             :                                     ALLOCSET_DEFAULT_SIZES);
     204                 :        1230 :     oldcontext = MemoryContextSwitchTo(context);
     205                 :             : 
     206                 :        1230 :     builder = palloc0_object(SnapBuild);
     207                 :             : 
     208                 :        1230 :     builder->state = SNAPBUILD_START;
     209                 :        1230 :     builder->context = context;
     210                 :        1230 :     builder->reorder = reorder;
     211                 :             :     /* Other struct members initialized by zeroing via palloc0 above */
     212                 :             : 
     213                 :        1230 :     builder->committed.xcnt = 0;
     214                 :        1230 :     builder->committed.xcnt_space = 128; /* arbitrary number */
     215                 :        1230 :     builder->committed.xip =
     216                 :        1230 :         palloc0_array(TransactionId, builder->committed.xcnt_space);
     217                 :        1230 :     builder->committed.includes_all_transactions = true;
     218                 :             : 
     219                 :        1230 :     builder->catchange.xcnt = 0;
     220                 :        1230 :     builder->catchange.xip = NULL;
     221                 :             : 
     222                 :        1230 :     builder->initial_xmin_horizon = xmin_horizon;
     223                 :        1230 :     builder->start_decoding_at = start_lsn;
     224                 :        1230 :     builder->in_slot_creation = in_slot_creation;
     225                 :        1230 :     builder->building_full_snapshot = need_full_snapshot;
     226                 :        1230 :     builder->two_phase_at = two_phase_at;
     227                 :             : 
     228                 :        1230 :     MemoryContextSwitchTo(oldcontext);
     229                 :             : 
     230                 :        1230 :     return builder;
     231                 :             : }
     232                 :             : 
     233                 :             : /*
     234                 :             :  * Free a snapshot builder.
     235                 :             :  */
     236                 :             : void
     237                 :         956 : FreeSnapshotBuilder(SnapBuild *builder)
     238                 :             : {
     239                 :         956 :     MemoryContext context = builder->context;
     240                 :             : 
     241                 :             :     /* free snapshot explicitly, that contains some error checking */
     242         [ +  + ]:         956 :     if (builder->snapshot != NULL)
     243                 :             :     {
     244                 :         236 :         SnapBuildSnapDecRefcount(builder->snapshot);
     245                 :         236 :         builder->snapshot = NULL;
     246                 :             :     }
     247                 :             : 
     248                 :             :     /* other resources are deallocated via memory context reset */
     249                 :         956 :     MemoryContextDelete(context);
     250                 :         956 : }
     251                 :             : 
     252                 :             : /*
     253                 :             :  * Free an unreferenced snapshot that has previously been built by us.
     254                 :             :  */
     255                 :             : static void
     256                 :        1861 : SnapBuildFreeSnapshot(Snapshot snap)
     257                 :             : {
     258                 :             :     /* make sure we don't get passed an external snapshot */
     259                 :             :     Assert(snap->snapshot_type == SNAPSHOT_HISTORIC_MVCC);
     260                 :             : 
     261                 :             :     /* make sure nobody modified our snapshot */
     262                 :             :     Assert(snap->curcid == FirstCommandId);
     263                 :             :     Assert(!snap->suboverflowed);
     264                 :             :     Assert(!snap->takenDuringRecovery);
     265                 :             :     Assert(snap->regd_count == 0);
     266                 :             : 
     267                 :             :     /* slightly more likely, so it's checked even without c-asserts */
     268         [ -  + ]:        1861 :     if (snap->copied)
     269         [ #  # ]:           0 :         elog(ERROR, "cannot free a copied snapshot");
     270                 :             : 
     271         [ -  + ]:        1861 :     if (snap->active_count)
     272         [ #  # ]:           0 :         elog(ERROR, "cannot free an active snapshot");
     273                 :             : 
     274                 :        1861 :     pfree(snap);
     275                 :        1861 : }
     276                 :             : 
     277                 :             : /*
     278                 :             :  * In which state of snapshot building are we?
     279                 :             :  */
     280                 :             : SnapBuildState
     281                 :     2380415 : SnapBuildCurrentState(SnapBuild *builder)
     282                 :             : {
     283                 :     2380415 :     return builder->state;
     284                 :             : }
     285                 :             : 
     286                 :             : /*
     287                 :             :  * Return the LSN at which the two-phase decoding was first enabled.
     288                 :             :  */
     289                 :             : XLogRecPtr
     290                 :          33 : SnapBuildGetTwoPhaseAt(SnapBuild *builder)
     291                 :             : {
     292                 :          33 :     return builder->two_phase_at;
     293                 :             : }
     294                 :             : 
     295                 :             : /*
     296                 :             :  * Set the LSN at which two-phase decoding is enabled.
     297                 :             :  */
     298                 :             : void
     299                 :           8 : SnapBuildSetTwoPhaseAt(SnapBuild *builder, XLogRecPtr ptr)
     300                 :             : {
     301                 :           8 :     builder->two_phase_at = ptr;
     302                 :           8 : }
     303                 :             : 
     304                 :             : /*
     305                 :             :  * Should the contents of transaction ending at 'ptr' be decoded?
     306                 :             :  */
     307                 :             : bool
     308                 :      557773 : SnapBuildXactNeedsSkip(SnapBuild *builder, XLogRecPtr ptr)
     309                 :             : {
     310                 :      557773 :     return ptr < builder->start_decoding_at;
     311                 :             : }
     312                 :             : 
     313                 :             : /*
     314                 :             :  * Increase refcount of a snapshot.
     315                 :             :  *
     316                 :             :  * This is used when handing out a snapshot to some external resource or when
     317                 :             :  * adding a Snapshot as builder->snapshot.
     318                 :             :  */
     319                 :             : static void
     320                 :        8015 : SnapBuildSnapIncRefcount(Snapshot snap)
     321                 :             : {
     322                 :        8015 :     snap->active_count++;
     323                 :        8015 : }
     324                 :             : 
     325                 :             : /*
     326                 :             :  * Decrease refcount of a snapshot and free if the refcount reaches zero.
     327                 :             :  *
     328                 :             :  * Externally visible, so that external resources that have been handed an
     329                 :             :  * IncRef'ed Snapshot can adjust its refcount easily.
     330                 :             :  */
     331                 :             : void
     332                 :        7701 : SnapBuildSnapDecRefcount(Snapshot snap)
     333                 :             : {
     334                 :             :     /* make sure we don't get passed an external snapshot */
     335                 :             :     Assert(snap->snapshot_type == SNAPSHOT_HISTORIC_MVCC);
     336                 :             : 
     337                 :             :     /* make sure nobody modified our snapshot */
     338                 :             :     Assert(snap->curcid == FirstCommandId);
     339                 :             :     Assert(!snap->suboverflowed);
     340                 :             :     Assert(!snap->takenDuringRecovery);
     341                 :             : 
     342                 :             :     Assert(snap->regd_count == 0);
     343                 :             : 
     344                 :             :     Assert(snap->active_count > 0);
     345                 :             : 
     346                 :             :     /* slightly more likely, so it's checked even without casserts */
     347         [ -  + ]:        7701 :     if (snap->copied)
     348         [ #  # ]:           0 :         elog(ERROR, "cannot free a copied snapshot");
     349                 :             : 
     350                 :        7701 :     snap->active_count--;
     351         [ +  + ]:        7701 :     if (snap->active_count == 0)
     352                 :        1861 :         SnapBuildFreeSnapshot(snap);
     353                 :        7701 : }
     354                 :             : 
     355                 :             : /*
     356                 :             :  * Build a new snapshot, based on currently committed catalog-modifying
     357                 :             :  * transactions.
     358                 :             :  *
     359                 :             :  * In-progress transactions with catalog access are *not* allowed to modify
     360                 :             :  * these snapshots; they have to copy them and fill in appropriate ->curcid
     361                 :             :  * and ->subxip/subxcnt values.
     362                 :             :  */
     363                 :             : static Snapshot
     364                 :        2369 : SnapBuildBuildSnapshot(SnapBuild *builder)
     365                 :             : {
     366                 :             :     Snapshot    snapshot;
     367                 :             :     Size        ssize;
     368                 :             : 
     369                 :             :     Assert(builder->state >= SNAPBUILD_FULL_SNAPSHOT);
     370                 :             : 
     371                 :        2369 :     ssize = sizeof(SnapshotData)
     372                 :        2369 :         + sizeof(TransactionId) * builder->committed.xcnt
     373                 :        2369 :         + sizeof(TransactionId) * 1 /* toplevel xid */ ;
     374                 :             : 
     375                 :        2369 :     snapshot = MemoryContextAllocZero(builder->context, ssize);
     376                 :             : 
     377                 :        2369 :     snapshot->snapshot_type = SNAPSHOT_HISTORIC_MVCC;
     378                 :             : 
     379                 :             :     /*
     380                 :             :      * We misuse the original meaning of SnapshotData's xip and subxip fields
     381                 :             :      * to make the more fitting for our needs.
     382                 :             :      *
     383                 :             :      * In the 'xip' array we store transactions that have to be treated as
     384                 :             :      * committed. Since we will only ever look at tuples from transactions
     385                 :             :      * that have modified the catalog it's more efficient to store those few
     386                 :             :      * that exist between xmin and xmax (frequently there are none).
     387                 :             :      *
     388                 :             :      * Snapshots that are used in transactions that have modified the catalog
     389                 :             :      * also use the 'subxip' array to store their toplevel xid and all the
     390                 :             :      * subtransaction xids so we can recognize when we need to treat rows as
     391                 :             :      * visible that are not in xip but still need to be visible. Subxip only
     392                 :             :      * gets filled when the transaction is copied into the context of a
     393                 :             :      * catalog modifying transaction since we otherwise share a snapshot
     394                 :             :      * between transactions. As long as a txn hasn't modified the catalog it
     395                 :             :      * doesn't need to treat any uncommitted rows as visible, so there is no
     396                 :             :      * need for those xids.
     397                 :             :      *
     398                 :             :      * Both arrays are qsort'ed so that we can use bsearch() on them.
     399                 :             :      */
     400                 :             :     Assert(TransactionIdIsNormal(builder->xmin));
     401                 :             :     Assert(TransactionIdIsNormal(builder->xmax));
     402                 :             : 
     403                 :        2369 :     snapshot->xmin = builder->xmin;
     404                 :        2369 :     snapshot->xmax = builder->xmax;
     405                 :             : 
     406                 :             :     /* store all transactions to be treated as committed by this snapshot */
     407                 :        2369 :     snapshot->xip =
     408                 :        2369 :         (TransactionId *) ((char *) snapshot + sizeof(SnapshotData));
     409                 :        2369 :     snapshot->xcnt = builder->committed.xcnt;
     410                 :        2369 :     memcpy(snapshot->xip,
     411                 :        2369 :            builder->committed.xip,
     412                 :        2369 :            builder->committed.xcnt * sizeof(TransactionId));
     413                 :             : 
     414                 :             :     /* sort so we can bsearch() */
     415                 :        2369 :     qsort(snapshot->xip, snapshot->xcnt, sizeof(TransactionId), xidComparator);
     416                 :             : 
     417                 :             :     /*
     418                 :             :      * Initially, subxip is empty, i.e. it's a snapshot to be used by
     419                 :             :      * transactions that don't modify the catalog. Will be filled by
     420                 :             :      * ReorderBufferCopySnap() if necessary.
     421                 :             :      */
     422                 :        2369 :     snapshot->subxcnt = 0;
     423                 :        2369 :     snapshot->subxip = NULL;
     424                 :             : 
     425                 :        2369 :     snapshot->suboverflowed = false;
     426                 :        2369 :     snapshot->takenDuringRecovery = false;
     427                 :        2369 :     snapshot->copied = false;
     428                 :        2369 :     snapshot->curcid = FirstCommandId;
     429                 :        2369 :     snapshot->active_count = 0;
     430                 :        2369 :     snapshot->regd_count = 0;
     431                 :        2369 :     snapshot->snapXactCompletionCount = 0;
     432                 :             : 
     433                 :        2369 :     return snapshot;
     434                 :             : }
     435                 :             : 
     436                 :             : /*
     437                 :             :  * Build the initial slot snapshot and convert it to a normal snapshot that
     438                 :             :  * is understood by HeapTupleSatisfiesMVCC.
     439                 :             :  *
     440                 :             :  * The snapshot will be usable directly in current transaction or exported
     441                 :             :  * for loading in different transaction.
     442                 :             :  */
     443                 :             : Snapshot
     444                 :         222 : SnapBuildInitialSnapshot(SnapBuild *builder)
     445                 :             : {
     446                 :             :     Snapshot    snap;
     447                 :             :     TransactionId xid;
     448                 :             :     TransactionId safeXid;
     449                 :             :     TransactionId *newxip;
     450                 :         222 :     int         newxcnt = 0;
     451                 :             : 
     452                 :             :     Assert(XactIsoLevel == XACT_REPEATABLE_READ);
     453                 :             :     Assert(builder->building_full_snapshot);
     454                 :             : 
     455                 :             :     /* don't allow older snapshots */
     456                 :         222 :     InvalidateCatalogSnapshot();    /* about to overwrite MyProc->xmin */
     457         [ -  + ]:         222 :     if (HaveRegisteredOrActiveSnapshot())
     458         [ #  # ]:           0 :         elog(ERROR, "cannot build an initial slot snapshot when snapshots exist");
     459                 :             :     Assert(!HistoricSnapshotActive());
     460                 :             : 
     461         [ -  + ]:         222 :     if (builder->state != SNAPBUILD_CONSISTENT)
     462         [ #  # ]:           0 :         elog(ERROR, "cannot build an initial slot snapshot before reaching a consistent state");
     463                 :             : 
     464         [ -  + ]:         222 :     if (!builder->committed.includes_all_transactions)
     465         [ #  # ]:           0 :         elog(ERROR, "cannot build an initial slot snapshot, not all transactions are monitored anymore");
     466                 :             : 
     467                 :             :     /* so we don't overwrite the existing value */
     468         [ -  + ]:         222 :     if (TransactionIdIsValid(MyProc->xmin))
     469         [ #  # ]:           0 :         elog(ERROR, "cannot build an initial slot snapshot when MyProc->xmin already is valid");
     470                 :             : 
     471                 :         222 :     snap = SnapBuildBuildSnapshot(builder);
     472                 :             : 
     473                 :             :     /*
     474                 :             :      * We know that snap->xmin is alive, enforced by the logical xmin
     475                 :             :      * mechanism. Due to that we can do this without locks, we're only
     476                 :             :      * changing our own value.
     477                 :             :      *
     478                 :             :      * Building an initial snapshot is expensive and an unenforced xmin
     479                 :             :      * horizon would have bad consequences, therefore always double-check that
     480                 :             :      * the horizon is enforced.
     481                 :             :      */
     482                 :         222 :     LWLockAcquire(ProcArrayLock, LW_SHARED);
     483                 :         222 :     safeXid = GetOldestSafeDecodingTransactionId(false);
     484                 :         222 :     LWLockRelease(ProcArrayLock);
     485                 :             : 
     486         [ -  + ]:         222 :     if (TransactionIdFollows(safeXid, snap->xmin))
     487         [ #  # ]:           0 :         elog(ERROR, "cannot build an initial slot snapshot as oldest safe xid %u follows snapshot's xmin %u",
     488                 :             :              safeXid, snap->xmin);
     489                 :             : 
     490                 :         222 :     MyProc->xmin = snap->xmin;
     491                 :             : 
     492                 :             :     /* allocate in transaction context */
     493                 :         222 :     newxip = palloc_array(TransactionId, GetMaxSnapshotXidCount());
     494                 :             : 
     495                 :             :     /*
     496                 :             :      * snapbuild.c builds transactions in an "inverted" manner, which means it
     497                 :             :      * stores committed transactions in ->xip, not ones in progress. Build a
     498                 :             :      * classical snapshot by marking all non-committed transactions as
     499                 :             :      * in-progress. This can be expensive.
     500                 :             :      */
     501         [ -  + ]:         222 :     for (xid = snap->xmin; NormalTransactionIdPrecedes(xid, snap->xmax);)
     502                 :             :     {
     503                 :             :         void       *test;
     504                 :             : 
     505                 :             :         /*
     506                 :             :          * Check whether transaction committed using the decoding snapshot
     507                 :             :          * meaning of ->xip.
     508                 :             :          */
     509                 :           0 :         test = bsearch(&xid, snap->xip, snap->xcnt,
     510                 :             :                        sizeof(TransactionId), xidComparator);
     511                 :             : 
     512         [ #  # ]:           0 :         if (test == NULL)
     513                 :             :         {
     514         [ #  # ]:           0 :             if (newxcnt >= GetMaxSnapshotXidCount())
     515         [ #  # ]:           0 :                 ereport(ERROR,
     516                 :             :                         (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
     517                 :             :                          errmsg("initial slot snapshot too large")));
     518                 :             : 
     519                 :           0 :             newxip[newxcnt++] = xid;
     520                 :             :         }
     521                 :             : 
     522         [ #  # ]:           0 :         TransactionIdAdvance(xid);
     523                 :             :     }
     524                 :             : 
     525                 :             :     /* adjust remaining snapshot fields as needed */
     526                 :         222 :     snap->snapshot_type = SNAPSHOT_MVCC;
     527                 :         222 :     snap->xcnt = newxcnt;
     528                 :         222 :     snap->xip = newxip;
     529                 :             : 
     530                 :         222 :     return snap;
     531                 :             : }
     532                 :             : 
     533                 :             : /*
     534                 :             :  * Export a snapshot so it can be set in another session with SET TRANSACTION
     535                 :             :  * SNAPSHOT.
     536                 :             :  *
     537                 :             :  * For that we need to start a transaction in the current backend as the
     538                 :             :  * importing side checks whether the source transaction is still open to make
     539                 :             :  * sure the xmin horizon hasn't advanced since then.
     540                 :             :  */
     541                 :             : const char *
     542                 :           1 : SnapBuildExportSnapshot(SnapBuild *builder)
     543                 :             : {
     544                 :             :     Snapshot    snap;
     545                 :             :     char       *snapname;
     546                 :             : 
     547         [ -  + ]:           1 :     if (IsTransactionOrTransactionBlock())
     548         [ #  # ]:           0 :         elog(ERROR, "cannot export a snapshot from within a transaction");
     549                 :             : 
     550         [ -  + ]:           1 :     if (SavedResourceOwnerDuringExport)
     551         [ #  # ]:           0 :         elog(ERROR, "can only export one snapshot at a time");
     552                 :             : 
     553                 :           1 :     SavedResourceOwnerDuringExport = CurrentResourceOwner;
     554                 :           1 :     ExportInProgress = true;
     555                 :             : 
     556                 :           1 :     StartTransactionCommand();
     557                 :             : 
     558                 :             :     /* There doesn't seem to a nice API to set these */
     559                 :           1 :     XactIsoLevel = XACT_REPEATABLE_READ;
     560                 :           1 :     XactReadOnly = true;
     561                 :             : 
     562                 :           1 :     snap = SnapBuildInitialSnapshot(builder);
     563                 :             : 
     564                 :             :     /*
     565                 :             :      * now that we've built a plain snapshot, make it active and use the
     566                 :             :      * normal mechanisms for exporting it
     567                 :             :      */
     568                 :           1 :     snapname = ExportSnapshot(snap);
     569                 :             : 
     570         [ +  - ]:           1 :     ereport(LOG,
     571                 :             :             (errmsg_plural("exported logical decoding snapshot: \"%s\" with %u transaction ID",
     572                 :             :                            "exported logical decoding snapshot: \"%s\" with %u transaction IDs",
     573                 :             :                            snap->xcnt,
     574                 :             :                            snapname, snap->xcnt)));
     575                 :           1 :     return snapname;
     576                 :             : }
     577                 :             : 
     578                 :             : /*
     579                 :             :  * Ensure there is a snapshot and if not build one for current transaction.
     580                 :             :  */
     581                 :             : Snapshot
     582                 :           9 : SnapBuildGetOrBuildSnapshot(SnapBuild *builder)
     583                 :             : {
     584                 :             :     Assert(builder->state == SNAPBUILD_CONSISTENT);
     585                 :             : 
     586                 :             :     /* only build a new snapshot if we don't have a prebuilt one */
     587         [ +  + ]:           9 :     if (builder->snapshot == NULL)
     588                 :             :     {
     589                 :           2 :         builder->snapshot = SnapBuildBuildSnapshot(builder);
     590                 :             :         /* increase refcount for the snapshot builder */
     591                 :           2 :         SnapBuildSnapIncRefcount(builder->snapshot);
     592                 :             :     }
     593                 :             : 
     594                 :           9 :     return builder->snapshot;
     595                 :             : }
     596                 :             : 
     597                 :             : /*
     598                 :             :  * Reset a previously SnapBuildExportSnapshot()'ed snapshot if there is
     599                 :             :  * any. Aborts the previously started transaction and resets the resource
     600                 :             :  * owner back to its original value.
     601                 :             :  */
     602                 :             : void
     603                 :        5978 : SnapBuildClearExportedSnapshot(void)
     604                 :             : {
     605                 :             :     ResourceOwner tmpResOwner;
     606                 :             : 
     607                 :             :     /* nothing exported, that is the usual case */
     608         [ +  + ]:        5978 :     if (!ExportInProgress)
     609                 :        5977 :         return;
     610                 :             : 
     611         [ -  + ]:           1 :     if (!IsTransactionState())
     612         [ #  # ]:           0 :         elog(ERROR, "clearing exported snapshot in wrong transaction state");
     613                 :             : 
     614                 :             :     /*
     615                 :             :      * AbortCurrentTransaction() takes care of resetting the snapshot state,
     616                 :             :      * so remember SavedResourceOwnerDuringExport.
     617                 :             :      */
     618                 :           1 :     tmpResOwner = SavedResourceOwnerDuringExport;
     619                 :             : 
     620                 :             :     /* make sure nothing could have ever happened */
     621                 :           1 :     AbortCurrentTransaction();
     622                 :             : 
     623                 :           1 :     CurrentResourceOwner = tmpResOwner;
     624                 :             : }
     625                 :             : 
     626                 :             : /*
     627                 :             :  * Clear snapshot export state during transaction abort.
     628                 :             :  */
     629                 :             : void
     630                 :       35946 : SnapBuildResetExportedSnapshotState(void)
     631                 :             : {
     632                 :       35946 :     SavedResourceOwnerDuringExport = NULL;
     633                 :       35946 :     ExportInProgress = false;
     634                 :       35946 : }
     635                 :             : 
     636                 :             : /*
     637                 :             :  * Handle the effects of a single heap change, appropriate to the current state
     638                 :             :  * of the snapshot builder and returns whether changes made at (xid, lsn) can
     639                 :             :  * be decoded.
     640                 :             :  */
     641                 :             : bool
     642                 :     1589187 : SnapBuildProcessChange(SnapBuild *builder, TransactionId xid, XLogRecPtr lsn)
     643                 :             : {
     644                 :             :     /*
     645                 :             :      * We can't handle data in transactions if we haven't built a snapshot
     646                 :             :      * yet, so don't store them.
     647                 :             :      */
     648         [ -  + ]:     1589187 :     if (builder->state < SNAPBUILD_FULL_SNAPSHOT)
     649                 :           0 :         return false;
     650                 :             : 
     651                 :             :     /*
     652                 :             :      * No point in keeping track of changes in transactions that we don't have
     653                 :             :      * enough information about to decode. This means that they started before
     654                 :             :      * we got into the SNAPBUILD_FULL_SNAPSHOT state.
     655                 :             :      */
     656   [ +  +  -  + ]:     1589190 :     if (builder->state < SNAPBUILD_CONSISTENT &&
     657                 :           3 :         TransactionIdPrecedes(xid, builder->next_phase_at))
     658                 :           0 :         return false;
     659                 :             : 
     660                 :             :     /*
     661                 :             :      * If the reorderbuffer doesn't yet have a snapshot, add one now, it will
     662                 :             :      * be needed to decode the change we're currently processing.
     663                 :             :      */
     664         [ +  + ]:     1589187 :     if (!ReorderBufferXidHasBaseSnapshot(builder->reorder, xid))
     665                 :             :     {
     666                 :             :         /* only build a new snapshot if we don't have a prebuilt one */
     667         [ +  + ]:        4185 :         if (builder->snapshot == NULL)
     668                 :             :         {
     669                 :         466 :             builder->snapshot = SnapBuildBuildSnapshot(builder);
     670                 :             :             /* increase refcount for the snapshot builder */
     671                 :         466 :             SnapBuildSnapIncRefcount(builder->snapshot);
     672                 :             :         }
     673                 :             : 
     674                 :             :         /*
     675                 :             :          * Increase refcount for the transaction we're handing the snapshot
     676                 :             :          * out to.
     677                 :             :          */
     678                 :        4185 :         SnapBuildSnapIncRefcount(builder->snapshot);
     679                 :        4185 :         ReorderBufferSetBaseSnapshot(builder->reorder, xid, lsn,
     680                 :             :                                      builder->snapshot);
     681                 :             :     }
     682                 :             : 
     683                 :     1589187 :     return true;
     684                 :             : }
     685                 :             : 
     686                 :             : /*
     687                 :             :  * Do CommandId/combo CID handling after reading an xl_heap_new_cid record.
     688                 :             :  * This implies that a transaction has done some form of write to system
     689                 :             :  * catalogs.
     690                 :             :  */
     691                 :             : void
     692                 :       28117 : SnapBuildProcessNewCid(SnapBuild *builder, TransactionId xid,
     693                 :             :                        XLogRecPtr lsn, xl_heap_new_cid *xlrec)
     694                 :             : {
     695                 :             :     CommandId   cid;
     696                 :             : 
     697                 :             :     /*
     698                 :             :      * we only log new_cid's if a catalog tuple was modified, so mark the
     699                 :             :      * transaction as containing catalog modifications
     700                 :             :      */
     701                 :       28117 :     ReorderBufferXidSetCatalogChanges(builder->reorder, xid, lsn);
     702                 :             : 
     703                 :       28117 :     ReorderBufferAddNewTupleCids(builder->reorder, xlrec->top_xid, lsn,
     704                 :             :                                  xlrec->target_locator, xlrec->target_tid,
     705                 :             :                                  xlrec->cmin, xlrec->cmax,
     706                 :             :                                  xlrec->combocid);
     707                 :             : 
     708                 :             :     /* figure out new command id */
     709         [ +  + ]:       28117 :     if (xlrec->cmin != InvalidCommandId &&
     710         [ +  + ]:       23623 :         xlrec->cmax != InvalidCommandId)
     711                 :        3320 :         cid = Max(xlrec->cmin, xlrec->cmax);
     712         [ +  + ]:       24797 :     else if (xlrec->cmax != InvalidCommandId)
     713                 :        4494 :         cid = xlrec->cmax;
     714         [ +  - ]:       20303 :     else if (xlrec->cmin != InvalidCommandId)
     715                 :       20303 :         cid = xlrec->cmin;
     716                 :             :     else
     717                 :             :     {
     718                 :           0 :         cid = InvalidCommandId; /* silence compiler */
     719         [ #  # ]:           0 :         elog(ERROR, "xl_heap_new_cid record without a valid CommandId");
     720                 :             :     }
     721                 :             : 
     722                 :       28117 :     ReorderBufferAddNewCommandId(builder->reorder, xid, lsn, cid + 1);
     723                 :       28117 : }
     724                 :             : 
     725                 :             : /*
     726                 :             :  * Add a new Snapshot and invalidation messages to all transactions we're
     727                 :             :  * decoding that currently are in-progress so they can see new catalog contents
     728                 :             :  * made by the transaction that just committed. This is necessary because those
     729                 :             :  * in-progress transactions will use the new catalog's contents from here on
     730                 :             :  * (at the very least everything they do needs to be compatible with newer
     731                 :             :  * catalog contents).
     732                 :             :  */
     733                 :             : static void
     734                 :        1671 : SnapBuildDistributeSnapshotAndInval(SnapBuild *builder, XLogRecPtr lsn, TransactionId xid)
     735                 :             : {
     736                 :             :     dlist_iter  txn_i;
     737                 :             :     ReorderBufferTXN *txn;
     738                 :             : 
     739                 :             :     /*
     740                 :             :      * Iterate through all toplevel transactions. This can include
     741                 :             :      * subtransactions which we just don't yet know to be that, but that's
     742                 :             :      * fine, they will just get an unnecessary snapshot and invalidations
     743                 :             :      * queued.
     744                 :             :      */
     745   [ +  -  +  + ]:        3376 :     dlist_foreach(txn_i, &builder->reorder->toplevel_by_lsn)
     746                 :             :     {
     747                 :        1705 :         txn = dlist_container(ReorderBufferTXN, node, txn_i.cur);
     748                 :             : 
     749                 :             :         Assert(TransactionIdIsValid(txn->xid));
     750                 :             : 
     751                 :             :         /*
     752                 :             :          * If we don't have a base snapshot yet, there are no changes in this
     753                 :             :          * transaction which in turn implies we don't yet need a snapshot at
     754                 :             :          * all. We'll add a snapshot when the first change gets queued.
     755                 :             :          *
     756                 :             :          * Similarly, we don't need to add invalidations to a transaction
     757                 :             :          * whose base snapshot is not yet set. Once a base snapshot is built,
     758                 :             :          * it will include the xids of committed transactions that have
     759                 :             :          * modified the catalog, thus reflecting the new catalog contents. The
     760                 :             :          * existing catalog cache will have already been invalidated after
     761                 :             :          * processing the invalidations in the transaction that modified
     762                 :             :          * catalogs, ensuring that a fresh cache is constructed during
     763                 :             :          * decoding.
     764                 :             :          *
     765                 :             :          * NB: This works correctly even for subtransactions because
     766                 :             :          * ReorderBufferAssignChild() takes care to transfer the base snapshot
     767                 :             :          * to the top-level transaction, and while iterating the changequeue
     768                 :             :          * we'll get the change from the subtxn.
     769                 :             :          */
     770         [ +  + ]:        1705 :         if (!ReorderBufferXidHasBaseSnapshot(builder->reorder, txn->xid))
     771                 :           2 :             continue;
     772                 :             : 
     773                 :             :         /*
     774                 :             :          * We don't need to add snapshot or invalidations to prepared
     775                 :             :          * transactions as they should not see the new catalog contents.
     776                 :             :          */
     777         [ +  + ]:        1703 :         if (rbtxn_is_prepared(txn))
     778                 :          28 :             continue;
     779                 :             : 
     780         [ +  + ]:        1675 :         elog(DEBUG2, "adding a new snapshot and invalidations to %u at %X/%08X",
     781                 :             :              txn->xid, LSN_FORMAT_ARGS(lsn));
     782                 :             : 
     783                 :             :         /*
     784                 :             :          * increase the snapshot's refcount for the transaction we are handing
     785                 :             :          * it out to
     786                 :             :          */
     787                 :        1675 :         SnapBuildSnapIncRefcount(builder->snapshot);
     788                 :        1675 :         ReorderBufferAddSnapshot(builder->reorder, txn->xid, lsn,
     789                 :             :                                  builder->snapshot);
     790                 :             : 
     791                 :             :         /*
     792                 :             :          * Add invalidation messages to the reorder buffer of in-progress
     793                 :             :          * transactions except the current committed transaction, for which we
     794                 :             :          * will execute invalidations at the end.
     795                 :             :          *
     796                 :             :          * It is required, otherwise, we will end up using the stale catcache
     797                 :             :          * contents built by the current transaction even after its decoding,
     798                 :             :          * which should have been invalidated due to concurrent catalog
     799                 :             :          * changing transaction.
     800                 :             :          *
     801                 :             :          * Distribute only the invalidation messages generated by the current
     802                 :             :          * committed transaction. Invalidation messages received from other
     803                 :             :          * transactions would have already been propagated to the relevant
     804                 :             :          * in-progress transactions. This transaction would have processed
     805                 :             :          * those invalidations, ensuring that subsequent transactions observe
     806                 :             :          * a consistent cache state.
     807                 :             :          */
     808         [ +  + ]:        1675 :         if (txn->xid != xid)
     809                 :             :         {
     810                 :             :             uint32      ninvalidations;
     811                 :          32 :             SharedInvalidationMessage *msgs = NULL;
     812                 :             : 
     813                 :          32 :             ninvalidations = ReorderBufferGetInvalidations(builder->reorder,
     814                 :             :                                                            xid, &msgs);
     815                 :             : 
     816         [ +  + ]:          32 :             if (ninvalidations > 0)
     817                 :             :             {
     818                 :             :                 Assert(msgs != NULL);
     819                 :             : 
     820                 :          28 :                 ReorderBufferAddDistributedInvalidations(builder->reorder,
     821                 :             :                                                          txn->xid, lsn,
     822                 :             :                                                          ninvalidations, msgs);
     823                 :             :             }
     824                 :             :         }
     825                 :             :     }
     826                 :        1671 : }
     827                 :             : 
     828                 :             : /*
     829                 :             :  * Keep track of a new catalog changing transaction that has committed.
     830                 :             :  */
     831                 :             : static void
     832                 :        1680 : SnapBuildAddCommittedTxn(SnapBuild *builder, TransactionId xid)
     833                 :             : {
     834                 :             :     Assert(TransactionIdIsValid(xid));
     835                 :             : 
     836         [ -  + ]:        1680 :     if (builder->committed.xcnt == builder->committed.xcnt_space)
     837                 :             :     {
     838                 :           0 :         builder->committed.xcnt_space = builder->committed.xcnt_space * 2 + 1;
     839                 :             : 
     840         [ #  # ]:           0 :         elog(DEBUG1, "increasing space for committed transactions to %u",
     841                 :             :              (uint32) builder->committed.xcnt_space);
     842                 :             : 
     843                 :           0 :         builder->committed.xip = repalloc_array(builder->committed.xip,
     844                 :             :                                                 TransactionId,
     845                 :             :                                                 builder->committed.xcnt_space);
     846                 :             :     }
     847                 :             : 
     848                 :             :     /*
     849                 :             :      * TODO: It might make sense to keep the array sorted here instead of
     850                 :             :      * doing it every time we build a new snapshot. On the other hand this
     851                 :             :      * gets called repeatedly when a transaction with subtransactions commits.
     852                 :             :      */
     853                 :        1680 :     builder->committed.xip[builder->committed.xcnt++] = xid;
     854                 :        1680 : }
     855                 :             : 
     856                 :             : /*
     857                 :             :  * Remove knowledge about transactions we treat as committed or containing catalog
     858                 :             :  * changes that are smaller than ->xmin. Those won't ever get checked via
     859                 :             :  * the ->committed or ->catchange array, respectively. The committed xids will
     860                 :             :  * get checked via the clog machinery.
     861                 :             :  *
     862                 :             :  * We can ideally remove the transaction from catchange array once it is
     863                 :             :  * finished (committed/aborted) but that could be costly as we need to maintain
     864                 :             :  * the xids order in the array.
     865                 :             :  */
     866                 :             : static void
     867                 :         537 : SnapBuildPurgeOlderTxn(SnapBuild *builder)
     868                 :             : {
     869                 :             :     TransactionId *workspace;
     870                 :         537 :     int         surviving_xids = 0;
     871                 :             : 
     872                 :             :     /* not ready yet */
     873         [ -  + ]:         537 :     if (!TransactionIdIsNormal(builder->xmin))
     874                 :           0 :         return;
     875                 :             : 
     876                 :             :     /* TODO: Neater algorithm than just copying and iterating? */
     877                 :             :     workspace =
     878                 :         537 :         MemoryContextAlloc(builder->context,
     879                 :         537 :                            builder->committed.xcnt * sizeof(TransactionId));
     880                 :             : 
     881                 :             :     /* copy xids that still are interesting to workspace */
     882         [ +  + ]:         923 :     for (size_t off = 0; off < builder->committed.xcnt; off++)
     883                 :             :     {
     884         [ +  + ]:         386 :         if (NormalTransactionIdPrecedes(builder->committed.xip[off],
     885                 :             :                                         builder->xmin))
     886                 :             :             ;                   /* remove */
     887                 :             :         else
     888                 :           1 :             workspace[surviving_xids++] = builder->committed.xip[off];
     889                 :             :     }
     890                 :             : 
     891                 :             :     /* copy workspace back to persistent state */
     892                 :         537 :     memcpy(builder->committed.xip, workspace,
     893                 :             :            surviving_xids * sizeof(TransactionId));
     894                 :             : 
     895         [ -  + ]:         537 :     elog(DEBUG3, "purged committed transactions from %u to %u, xmin: %u, xmax: %u",
     896                 :             :          (uint32) builder->committed.xcnt, (uint32) surviving_xids,
     897                 :             :          builder->xmin, builder->xmax);
     898                 :         537 :     builder->committed.xcnt = surviving_xids;
     899                 :             : 
     900                 :         537 :     pfree(workspace);
     901                 :             : 
     902                 :             :     /*
     903                 :             :      * Purge xids in ->catchange as well. The purged array must also be sorted
     904                 :             :      * in xidComparator order.
     905                 :             :      */
     906         [ +  + ]:         537 :     if (builder->catchange.xcnt > 0)
     907                 :             :     {
     908                 :             :         size_t      off;
     909                 :             : 
     910                 :             :         /*
     911                 :             :          * Since catchange.xip is sorted, we find the lower bound of xids that
     912                 :             :          * are still interesting.
     913                 :             :          */
     914         [ +  + ]:           7 :         for (off = 0; off < builder->catchange.xcnt; off++)
     915                 :             :         {
     916         [ +  + ]:           5 :             if (TransactionIdFollowsOrEquals(builder->catchange.xip[off],
     917                 :             :                                              builder->xmin))
     918                 :           1 :                 break;
     919                 :             :         }
     920                 :             : 
     921                 :           3 :         surviving_xids = builder->catchange.xcnt - off;
     922                 :             : 
     923         [ +  + ]:           3 :         if (surviving_xids > 0)
     924                 :             :         {
     925                 :           1 :             memmove(builder->catchange.xip, &(builder->catchange.xip[off]),
     926                 :             :                     surviving_xids * sizeof(TransactionId));
     927                 :             :         }
     928                 :             :         else
     929                 :             :         {
     930                 :           2 :             pfree(builder->catchange.xip);
     931                 :           2 :             builder->catchange.xip = NULL;
     932                 :             :         }
     933                 :             : 
     934         [ -  + ]:           3 :         elog(DEBUG3, "purged catalog modifying transactions from %u to %u, xmin: %u, xmax: %u",
     935                 :             :              (uint32) builder->catchange.xcnt, (uint32) surviving_xids,
     936                 :             :              builder->xmin, builder->xmax);
     937                 :           3 :         builder->catchange.xcnt = surviving_xids;
     938                 :             :     }
     939                 :             : }
     940                 :             : 
     941                 :             : /*
     942                 :             :  * Handle everything that needs to be done when a transaction commits
     943                 :             :  */
     944                 :             : void
     945                 :        4027 : SnapBuildCommitTxn(SnapBuild *builder, XLogRecPtr lsn, TransactionId xid,
     946                 :             :                    int nsubxacts, TransactionId *subxacts, uint32 xinfo)
     947                 :             : {
     948                 :             :     int         nxact;
     949                 :             : 
     950                 :        4027 :     bool        needs_snapshot = false;
     951                 :        4027 :     bool        needs_timetravel = false;
     952                 :        4027 :     bool        sub_needs_timetravel = false;
     953                 :             : 
     954                 :        4027 :     TransactionId xmax = xid;
     955                 :             : 
     956                 :             :     /*
     957                 :             :      * Transactions preceding BUILDING_SNAPSHOT will neither be decoded, nor
     958                 :             :      * will they be part of a snapshot.  So we don't need to record anything.
     959                 :             :      */
     960         [ +  - ]:        4027 :     if (builder->state == SNAPBUILD_START ||
     961   [ -  +  -  - ]:        4027 :         (builder->state == SNAPBUILD_BUILDING_SNAPSHOT &&
     962                 :           0 :          TransactionIdPrecedes(xid, builder->next_phase_at)))
     963                 :             :     {
     964                 :             :         /* ensure that only commits after this are getting replayed */
     965         [ #  # ]:           0 :         if (builder->start_decoding_at <= lsn)
     966                 :           0 :             builder->start_decoding_at = lsn + 1;
     967                 :           0 :         return;
     968                 :             :     }
     969                 :             : 
     970         [ +  + ]:        4027 :     if (builder->state < SNAPBUILD_CONSISTENT)
     971                 :             :     {
     972                 :             :         /* ensure that only commits after this are getting replayed */
     973         [ +  + ]:           5 :         if (builder->start_decoding_at <= lsn)
     974                 :           2 :             builder->start_decoding_at = lsn + 1;
     975                 :             : 
     976                 :             :         /*
     977                 :             :          * If building an exportable snapshot, force xid to be tracked, even
     978                 :             :          * if the transaction didn't modify the catalog.
     979                 :             :          */
     980         [ -  + ]:           5 :         if (builder->building_full_snapshot)
     981                 :             :         {
     982                 :           0 :             needs_timetravel = true;
     983                 :             :         }
     984                 :             :     }
     985                 :             : 
     986         [ +  + ]:        5251 :     for (nxact = 0; nxact < nsubxacts; nxact++)
     987                 :             :     {
     988                 :        1224 :         TransactionId subxid = subxacts[nxact];
     989                 :             : 
     990                 :             :         /*
     991                 :             :          * Add subtransaction to base snapshot if catalog modifying, we don't
     992                 :             :          * distinguish to toplevel transactions there.
     993                 :             :          */
     994         [ +  + ]:        1224 :         if (SnapBuildXidHasCatalogChanges(builder, subxid, xinfo))
     995                 :             :         {
     996                 :           9 :             sub_needs_timetravel = true;
     997                 :           9 :             needs_snapshot = true;
     998                 :             : 
     999         [ -  + ]:           9 :             elog(DEBUG1, "found subtransaction %u:%u with catalog changes",
    1000                 :             :                  xid, subxid);
    1001                 :             : 
    1002                 :           9 :             SnapBuildAddCommittedTxn(builder, subxid);
    1003                 :             : 
    1004         [ +  - ]:           9 :             if (NormalTransactionIdFollows(subxid, xmax))
    1005                 :           9 :                 xmax = subxid;
    1006                 :             :         }
    1007                 :             : 
    1008                 :             :         /*
    1009                 :             :          * If we're forcing timetravel we also need visibility information
    1010                 :             :          * about subtransaction, so keep track of subtransaction's state, even
    1011                 :             :          * if not catalog modifying.  Don't need to distribute a snapshot in
    1012                 :             :          * that case.
    1013                 :             :          */
    1014         [ -  + ]:        1215 :         else if (needs_timetravel)
    1015                 :             :         {
    1016                 :           0 :             SnapBuildAddCommittedTxn(builder, subxid);
    1017         [ #  # ]:           0 :             if (NormalTransactionIdFollows(subxid, xmax))
    1018                 :           0 :                 xmax = subxid;
    1019                 :             :         }
    1020                 :             :     }
    1021                 :             : 
    1022                 :             :     /* if top-level modified catalog, it'll need a snapshot */
    1023         [ +  + ]:        4027 :     if (SnapBuildXidHasCatalogChanges(builder, xid, xinfo))
    1024                 :             :     {
    1025         [ +  + ]:        1670 :         elog(DEBUG2, "found top level transaction %u, with catalog changes",
    1026                 :             :              xid);
    1027                 :        1670 :         needs_snapshot = true;
    1028                 :        1670 :         needs_timetravel = true;
    1029                 :        1670 :         SnapBuildAddCommittedTxn(builder, xid);
    1030                 :             :     }
    1031         [ +  + ]:        2357 :     else if (sub_needs_timetravel)
    1032                 :             :     {
    1033                 :             :         /* track toplevel txn as well, subxact alone isn't meaningful */
    1034         [ -  + ]:           1 :         elog(DEBUG2, "forced transaction %u to do timetravel due to one of its subtransactions",
    1035                 :             :              xid);
    1036                 :           1 :         needs_timetravel = true;
    1037                 :           1 :         SnapBuildAddCommittedTxn(builder, xid);
    1038                 :             :     }
    1039         [ -  + ]:        2356 :     else if (needs_timetravel)
    1040                 :             :     {
    1041         [ #  # ]:           0 :         elog(DEBUG2, "forced transaction %u to do timetravel", xid);
    1042                 :             : 
    1043                 :           0 :         SnapBuildAddCommittedTxn(builder, xid);
    1044                 :             :     }
    1045                 :             : 
    1046         [ +  + ]:        4027 :     if (!needs_timetravel)
    1047                 :             :     {
    1048                 :             :         /* record that we cannot export a general snapshot anymore */
    1049                 :        2356 :         builder->committed.includes_all_transactions = false;
    1050                 :             :     }
    1051                 :             : 
    1052                 :             :     Assert(!needs_snapshot || needs_timetravel);
    1053                 :             : 
    1054                 :             :     /*
    1055                 :             :      * Adjust xmax of the snapshot builder, we only do that for committed,
    1056                 :             :      * catalog modifying, transactions, everything else isn't interesting for
    1057                 :             :      * us since we'll never look at the respective rows.
    1058                 :             :      */
    1059         [ +  + ]:        4027 :     if (needs_timetravel &&
    1060   [ +  -  +  - ]:        3342 :         (!TransactionIdIsValid(builder->xmax) ||
    1061                 :        1671 :          TransactionIdFollowsOrEquals(xmax, builder->xmax)))
    1062                 :             :     {
    1063                 :        1671 :         builder->xmax = xmax;
    1064         [ -  + ]:        1671 :         TransactionIdAdvance(builder->xmax);
    1065                 :             :     }
    1066                 :             : 
    1067                 :             :     /* if there's any reason to build a historic snapshot, do so now */
    1068         [ +  + ]:        4027 :     if (needs_snapshot)
    1069                 :             :     {
    1070                 :             :         /*
    1071                 :             :          * If we haven't built a complete snapshot yet there's no need to hand
    1072                 :             :          * it out, it wouldn't (and couldn't) be used anyway.
    1073                 :             :          */
    1074         [ -  + ]:        1671 :         if (builder->state < SNAPBUILD_FULL_SNAPSHOT)
    1075                 :           0 :             return;
    1076                 :             : 
    1077                 :             :         /*
    1078                 :             :          * Decrease the snapshot builder's refcount of the old snapshot, note
    1079                 :             :          * that it still will be used if it has been handed out to the
    1080                 :             :          * reorderbuffer earlier.
    1081                 :             :          */
    1082         [ +  - ]:        1671 :         if (builder->snapshot)
    1083                 :        1671 :             SnapBuildSnapDecRefcount(builder->snapshot);
    1084                 :             : 
    1085                 :        1671 :         builder->snapshot = SnapBuildBuildSnapshot(builder);
    1086                 :             : 
    1087                 :             :         /* we might need to execute invalidations, add snapshot */
    1088         [ +  + ]:        1671 :         if (!ReorderBufferXidHasBaseSnapshot(builder->reorder, xid))
    1089                 :             :         {
    1090                 :           8 :             SnapBuildSnapIncRefcount(builder->snapshot);
    1091                 :           8 :             ReorderBufferSetBaseSnapshot(builder->reorder, xid, lsn,
    1092                 :             :                                          builder->snapshot);
    1093                 :             :         }
    1094                 :             : 
    1095                 :             :         /* refcount of the snapshot builder for the new snapshot */
    1096                 :        1671 :         SnapBuildSnapIncRefcount(builder->snapshot);
    1097                 :             : 
    1098                 :             :         /*
    1099                 :             :          * Add a new catalog snapshot and invalidations messages to all
    1100                 :             :          * currently running transactions.
    1101                 :             :          */
    1102                 :        1671 :         SnapBuildDistributeSnapshotAndInval(builder, lsn, xid);
    1103                 :             :     }
    1104                 :             : }
    1105                 :             : 
    1106                 :             : /*
    1107                 :             :  * Check the reorder buffer and the snapshot to see if the given transaction has
    1108                 :             :  * modified catalogs.
    1109                 :             :  */
    1110                 :             : static inline bool
    1111                 :        5251 : SnapBuildXidHasCatalogChanges(SnapBuild *builder, TransactionId xid,
    1112                 :             :                               uint32 xinfo)
    1113                 :             : {
    1114         [ +  + ]:        5251 :     if (ReorderBufferXidHasCatalogChanges(builder->reorder, xid))
    1115                 :        1675 :         return true;
    1116                 :             : 
    1117                 :             :     /*
    1118                 :             :      * The transactions that have changed catalogs must have invalidation
    1119                 :             :      * info.
    1120                 :             :      */
    1121         [ +  + ]:        3576 :     if (!(xinfo & XACT_XINFO_HAS_INVALS))
    1122                 :        3568 :         return false;
    1123                 :             : 
    1124                 :             :     /* Check the catchange XID array */
    1125   [ +  +  +  - ]:          12 :     return ((builder->catchange.xcnt > 0) &&
    1126                 :           4 :             (bsearch(&xid, builder->catchange.xip, builder->catchange.xcnt,
    1127                 :             :                      sizeof(TransactionId), xidComparator) != NULL));
    1128                 :             : }
    1129                 :             : 
    1130                 :             : /* -----------------------------------
    1131                 :             :  * Snapshot building functions dealing with xlog records
    1132                 :             :  * -----------------------------------
    1133                 :             :  */
    1134                 :             : 
    1135                 :             : /*
    1136                 :             :  * Process a running xacts record, and use its information to first build a
    1137                 :             :  * historic snapshot and later to release resources that aren't needed
    1138                 :             :  * anymore.
    1139                 :             :  */
    1140                 :             : void
    1141                 :        1715 : SnapBuildProcessRunningXacts(SnapBuild *builder, XLogRecPtr lsn, xl_running_xacts *running)
    1142                 :             : {
    1143                 :             :     ReorderBufferTXN *txn;
    1144                 :             :     TransactionId xmin;
    1145                 :             : 
    1146                 :             :     /*
    1147                 :             :      * If we're not consistent yet, inspect the record to see whether it
    1148                 :             :      * allows to get closer to being consistent. If we are consistent, dump
    1149                 :             :      * our snapshot so others or we, after a restart, can use it.
    1150                 :             :      */
    1151         [ +  + ]:        1715 :     if (builder->state < SNAPBUILD_CONSISTENT)
    1152                 :             :     {
    1153                 :             :         /* returns false if there's no point in performing cleanup just yet */
    1154         [ +  + ]:        1201 :         if (!SnapBuildFindSnapshot(builder, lsn, running))
    1155                 :        1176 :             return;
    1156                 :             :     }
    1157                 :             :     else
    1158                 :         514 :         SnapBuildSerialize(builder, lsn);
    1159                 :             : 
    1160                 :             :     /*
    1161                 :             :      * Update range of interesting xids based on the running xacts
    1162                 :             :      * information. We don't increase ->xmax using it, because once we are in
    1163                 :             :      * a consistent state we can do that ourselves and much more efficiently
    1164                 :             :      * so, because we only need to do it for catalog transactions since we
    1165                 :             :      * only ever look at those.
    1166                 :             :      *
    1167                 :             :      * NB: We only increase xmax when a catalog modifying transaction commits
    1168                 :             :      * (see SnapBuildCommitTxn).  Because of this, xmax can be lower than
    1169                 :             :      * xmin, which looks odd but is correct and actually more efficient, since
    1170                 :             :      * we hit fast paths in heapam_visibility.c.
    1171                 :             :      */
    1172                 :         537 :     builder->xmin = running->oldestRunningXid;
    1173                 :             : 
    1174                 :             :     /* Remove transactions we don't need to keep track off anymore */
    1175                 :         537 :     SnapBuildPurgeOlderTxn(builder);
    1176                 :             : 
    1177                 :             :     /*
    1178                 :             :      * Advance the xmin limit for the current replication slot, to allow
    1179                 :             :      * vacuum to clean up the tuples this slot has been protecting.
    1180                 :             :      *
    1181                 :             :      * The reorderbuffer might have an xmin among the currently running
    1182                 :             :      * snapshots; use it if so.  If not, we need only consider the snapshots
    1183                 :             :      * we'll produce later, which can't be less than the oldest running xid in
    1184                 :             :      * the record we're reading now.
    1185                 :             :      */
    1186                 :         537 :     xmin = ReorderBufferGetOldestXmin(builder->reorder);
    1187         [ +  + ]:         537 :     if (xmin == InvalidTransactionId)
    1188                 :         488 :         xmin = running->oldestRunningXid;
    1189         [ -  + ]:         537 :     elog(DEBUG3, "xmin: %u, xmax: %u, oldest running: %u, oldest xmin: %u",
    1190                 :             :          builder->xmin, builder->xmax, running->oldestRunningXid, xmin);
    1191                 :         537 :     LogicalIncreaseXminForSlot(lsn, xmin);
    1192                 :             : 
    1193                 :             :     /*
    1194                 :             :      * Also tell the slot where we can restart decoding from. We don't want to
    1195                 :             :      * do that after every commit because changing that implies an fsync of
    1196                 :             :      * the logical slot's state file, so we only do it every time we see a
    1197                 :             :      * running xacts record.
    1198                 :             :      *
    1199                 :             :      * Do so by looking for the oldest in progress transaction (determined by
    1200                 :             :      * the first LSN of any of its relevant records). Every transaction
    1201                 :             :      * remembers the last location we stored the snapshot to disk before its
    1202                 :             :      * beginning. That point is where we can restart from.
    1203                 :             :      */
    1204                 :             : 
    1205                 :             :     /*
    1206                 :             :      * Can't know about a serialized snapshot's location if we're not
    1207                 :             :      * consistent.
    1208                 :             :      */
    1209         [ +  + ]:         537 :     if (builder->state < SNAPBUILD_CONSISTENT)
    1210                 :          18 :         return;
    1211                 :             : 
    1212                 :         519 :     txn = ReorderBufferGetOldestTXN(builder->reorder);
    1213                 :             : 
    1214                 :             :     /*
    1215                 :             :      * oldest ongoing txn might have started when we didn't yet serialize
    1216                 :             :      * anything because we hadn't reached a consistent state yet.
    1217                 :             :      */
    1218   [ +  +  +  + ]:         519 :     if (txn != NULL && XLogRecPtrIsValid(txn->restart_decoding_lsn))
    1219                 :          24 :         LogicalIncreaseRestartDecodingForSlot(lsn, txn->restart_decoding_lsn);
    1220                 :             : 
    1221                 :             :     /*
    1222                 :             :      * No in-progress transaction, can reuse the last serialized snapshot if
    1223                 :             :      * we have one.
    1224                 :             :      */
    1225         [ +  + ]:         495 :     else if (txn == NULL &&
    1226         [ +  + ]:         461 :              XLogRecPtrIsValid(builder->reorder->current_restart_decoding_lsn) &&
    1227         [ +  - ]:         459 :              XLogRecPtrIsValid(builder->last_serialized_snapshot))
    1228                 :         459 :         LogicalIncreaseRestartDecodingForSlot(lsn,
    1229                 :             :                                               builder->last_serialized_snapshot);
    1230                 :             : }
    1231                 :             : 
    1232                 :             : 
    1233                 :             : /*
    1234                 :             :  * Build the start of a snapshot that's capable of decoding the catalog.
    1235                 :             :  *
    1236                 :             :  * Helper function for SnapBuildProcessRunningXacts() while we're not yet
    1237                 :             :  * consistent.
    1238                 :             :  *
    1239                 :             :  * Returns true if there is a point in performing internal maintenance/cleanup
    1240                 :             :  * using the xl_running_xacts record.
    1241                 :             :  */
    1242                 :             : static bool
    1243                 :        1201 : SnapBuildFindSnapshot(SnapBuild *builder, XLogRecPtr lsn, xl_running_xacts *running)
    1244                 :             : {
    1245                 :             :     /* ---
    1246                 :             :      * Build catalog decoding snapshot incrementally using information about
    1247                 :             :      * the currently running transactions. There are several ways to do that:
    1248                 :             :      *
    1249                 :             :      * a) There were no running transactions when the xl_running_xacts record
    1250                 :             :      *    was inserted, jump to CONSISTENT immediately. We might find such a
    1251                 :             :      *    state while waiting on c)'s sub-states.
    1252                 :             :      *
    1253                 :             :      * b) This (in a previous run) or another decoding slot serialized a
    1254                 :             :      *    snapshot to disk that we can use. Can't use this method while finding
    1255                 :             :      *    the start point for decoding changes as the restart LSN would be an
    1256                 :             :      *    arbitrary LSN but we need to find the start point to extract changes
    1257                 :             :      *    where we won't see the data for partial transactions. Also, we cannot
    1258                 :             :      *    use this method when a slot needs a full snapshot for export or direct
    1259                 :             :      *    use, as that snapshot will only contain catalog modifying transactions.
    1260                 :             :      *
    1261                 :             :      * c) First incrementally build a snapshot for catalog tuples
    1262                 :             :      *    (BUILDING_SNAPSHOT), that requires all, already in-progress,
    1263                 :             :      *    transactions to finish.  Every transaction starting after that
    1264                 :             :      *    (FULL_SNAPSHOT state), has enough information to be decoded.  But
    1265                 :             :      *    for older running transactions no viable snapshot exists yet, so
    1266                 :             :      *    CONSISTENT will only be reached once all of those have finished.
    1267                 :             :      * ---
    1268                 :             :      */
    1269                 :             : 
    1270                 :             :     /*
    1271                 :             :      * xl_running_xacts record is older than what we can use, we might not
    1272                 :             :      * have all necessary catalog rows anymore.
    1273                 :             :      */
    1274         [ +  + ]:        1201 :     if (TransactionIdIsNormal(builder->initial_xmin_horizon) &&
    1275         [ +  + ]:         512 :         NormalTransactionIdPrecedes(running->oldestRunningXid,
    1276                 :             :                                     builder->initial_xmin_horizon))
    1277                 :             :     {
    1278         [ -  + ]:           1 :         ereport(DEBUG1,
    1279                 :             :                 errmsg_internal("skipping snapshot at %X/%08X while building logical decoding snapshot, xmin horizon too low",
    1280                 :             :                                 LSN_FORMAT_ARGS(lsn)),
    1281                 :             :                 errdetail_internal("initial xmin horizon of %u vs the snapshot's %u",
    1282                 :             :                                    builder->initial_xmin_horizon, running->oldestRunningXid));
    1283                 :             : 
    1284                 :             : 
    1285                 :           1 :         SnapBuildWaitSnapshot(running, builder->initial_xmin_horizon);
    1286                 :             : 
    1287                 :           1 :         return true;
    1288                 :             :     }
    1289                 :             : 
    1290                 :             :     /*
    1291                 :             :      * a) No transaction were running, we can jump to consistent.
    1292                 :             :      *
    1293                 :             :      * This is not affected by races around xl_running_xacts, because we can
    1294                 :             :      * miss transaction commits, but currently not transactions starting.
    1295                 :             :      *
    1296                 :             :      * NB: We might have already started to incrementally assemble a snapshot,
    1297                 :             :      * so we need to be careful to deal with that.
    1298                 :             :      */
    1299         [ +  + ]:        1200 :     if (running->oldestRunningXid == running->nextXid)
    1300                 :             :     {
    1301         [ +  + ]:        1168 :         if (!XLogRecPtrIsValid(builder->start_decoding_at) ||
    1302         [ +  + ]:         663 :             builder->start_decoding_at <= lsn)
    1303                 :             :             /* can decode everything after this */
    1304                 :         506 :             builder->start_decoding_at = lsn + 1;
    1305                 :             : 
    1306                 :             :         /* As no transactions were running xmin/xmax can be trivially set. */
    1307                 :        1168 :         builder->xmin = running->nextXid; /* < are finished */
    1308                 :        1168 :         builder->xmax = running->nextXid; /* >= are running */
    1309                 :             : 
    1310                 :             :         /* so we can safely use the faster comparisons */
    1311                 :             :         Assert(TransactionIdIsNormal(builder->xmin));
    1312                 :             :         Assert(TransactionIdIsNormal(builder->xmax));
    1313                 :             : 
    1314                 :        1168 :         builder->state = SNAPBUILD_CONSISTENT;
    1315                 :        1168 :         builder->next_phase_at = InvalidTransactionId;
    1316                 :             : 
    1317   [ +  +  +  + ]:        1168 :         ereport(LogicalDecodingLogLevel(),
    1318                 :             :                 errmsg("logical decoding found consistent point at %X/%08X",
    1319                 :             :                        LSN_FORMAT_ARGS(lsn)),
    1320                 :             :                 errdetail("There are no running transactions."));
    1321                 :             : 
    1322                 :        1168 :         return false;
    1323                 :             :     }
    1324                 :             : 
    1325                 :             :     /*
    1326                 :             :      * b) valid on disk state and while neither building full snapshot nor
    1327                 :             :      * creating a slot.
    1328                 :             :      */
    1329         [ +  + ]:          32 :     else if (!builder->building_full_snapshot &&
    1330   [ +  +  +  + ]:          50 :              !builder->in_slot_creation &&
    1331                 :          19 :              SnapBuildRestore(builder, lsn))
    1332                 :             :     {
    1333                 :             :         /* there won't be any state to cleanup */
    1334                 :           8 :         return false;
    1335                 :             :     }
    1336                 :             : 
    1337                 :             :     /*
    1338                 :             :      * c) transition from START to BUILDING_SNAPSHOT.
    1339                 :             :      *
    1340                 :             :      * In START state, and a xl_running_xacts record with running xacts is
    1341                 :             :      * encountered.  In that case, switch to BUILDING_SNAPSHOT state, and
    1342                 :             :      * record xl_running_xacts->nextXid.  Once all running xacts have finished
    1343                 :             :      * (i.e. they're all >= nextXid), we have a complete catalog snapshot.  It
    1344                 :             :      * might look that we could use xl_running_xacts's ->xids information to
    1345                 :             :      * get there quicker, but that is problematic because transactions marked
    1346                 :             :      * as running, might already have inserted their commit record - it's
    1347                 :             :      * infeasible to change that with locking.
    1348                 :             :      */
    1349         [ +  + ]:          24 :     else if (builder->state == SNAPBUILD_START)
    1350                 :             :     {
    1351                 :          13 :         builder->state = SNAPBUILD_BUILDING_SNAPSHOT;
    1352                 :          13 :         builder->next_phase_at = running->nextXid;
    1353                 :             : 
    1354                 :             :         /*
    1355                 :             :          * Start with an xmin/xmax that's correct for future, when all the
    1356                 :             :          * currently running transactions have finished. We'll update both
    1357                 :             :          * while waiting for the pending transactions to finish.
    1358                 :             :          */
    1359                 :          13 :         builder->xmin = running->nextXid; /* < are finished */
    1360                 :          13 :         builder->xmax = running->nextXid; /* >= are running */
    1361                 :             : 
    1362                 :             :         /* so we can safely use the faster comparisons */
    1363                 :             :         Assert(TransactionIdIsNormal(builder->xmin));
    1364                 :             :         Assert(TransactionIdIsNormal(builder->xmax));
    1365                 :             : 
    1366         [ +  - ]:          13 :         ereport(LOG,
    1367                 :             :                 errmsg("logical decoding found initial starting point at %X/%08X",
    1368                 :             :                        LSN_FORMAT_ARGS(lsn)),
    1369                 :             :                 errdetail("Waiting for transactions (approximately %d) older than %u to end.",
    1370                 :             :                           running->xcnt, running->nextXid));
    1371                 :             : 
    1372                 :          13 :         SnapBuildWaitSnapshot(running, running->nextXid);
    1373                 :             :     }
    1374                 :             : 
    1375                 :             :     /*
    1376                 :             :      * c) transition from BUILDING_SNAPSHOT to FULL_SNAPSHOT.
    1377                 :             :      *
    1378                 :             :      * In BUILDING_SNAPSHOT state, and this xl_running_xacts' oldestRunningXid
    1379                 :             :      * is >= than nextXid from when we switched to BUILDING_SNAPSHOT.  This
    1380                 :             :      * means all transactions starting afterwards have enough information to
    1381                 :             :      * be decoded.  Switch to FULL_SNAPSHOT.
    1382                 :             :      */
    1383   [ +  +  +  + ]:          17 :     else if (builder->state == SNAPBUILD_BUILDING_SNAPSHOT &&
    1384                 :           6 :              TransactionIdPrecedesOrEquals(builder->next_phase_at,
    1385                 :             :                                            running->oldestRunningXid))
    1386                 :             :     {
    1387                 :           5 :         builder->state = SNAPBUILD_FULL_SNAPSHOT;
    1388                 :           5 :         builder->next_phase_at = running->nextXid;
    1389                 :             : 
    1390         [ +  - ]:           5 :         ereport(LOG,
    1391                 :             :                 errmsg("logical decoding found initial consistent point at %X/%08X",
    1392                 :             :                        LSN_FORMAT_ARGS(lsn)),
    1393                 :             :                 errdetail("Waiting for transactions (approximately %d) older than %u to end.",
    1394                 :             :                           running->xcnt, running->nextXid));
    1395                 :             : 
    1396                 :           5 :         SnapBuildWaitSnapshot(running, running->nextXid);
    1397                 :             :     }
    1398                 :             : 
    1399                 :             :     /*
    1400                 :             :      * c) transition from FULL_SNAPSHOT to CONSISTENT.
    1401                 :             :      *
    1402                 :             :      * In FULL_SNAPSHOT state, and this xl_running_xacts' oldestRunningXid is
    1403                 :             :      * >= than nextXid from when we switched to FULL_SNAPSHOT.  This means all
    1404                 :             :      * transactions that are currently in progress have a catalog snapshot,
    1405                 :             :      * and all their changes have been collected.  Switch to CONSISTENT.
    1406                 :             :      */
    1407   [ +  +  +  - ]:          11 :     else if (builder->state == SNAPBUILD_FULL_SNAPSHOT &&
    1408                 :           5 :              TransactionIdPrecedesOrEquals(builder->next_phase_at,
    1409                 :             :                                            running->oldestRunningXid))
    1410                 :             :     {
    1411                 :           5 :         builder->state = SNAPBUILD_CONSISTENT;
    1412                 :           5 :         builder->next_phase_at = InvalidTransactionId;
    1413                 :             : 
    1414   [ +  -  -  + ]:           5 :         ereport(LogicalDecodingLogLevel(),
    1415                 :             :                 errmsg("logical decoding found consistent point at %X/%08X",
    1416                 :             :                        LSN_FORMAT_ARGS(lsn)),
    1417                 :             :                 errdetail("There are no old transactions anymore."));
    1418                 :             :     }
    1419                 :             : 
    1420                 :             :     /*
    1421                 :             :      * We already started to track running xacts and need to wait for all
    1422                 :             :      * in-progress ones to finish. We fall through to the normal processing of
    1423                 :             :      * records so incremental cleanup can be performed.
    1424                 :             :      */
    1425                 :          22 :     return true;
    1426                 :             : }
    1427                 :             : 
    1428                 :             : /* ---
    1429                 :             :  * Iterate through xids in record, wait for all older than the cutoff to
    1430                 :             :  * finish.  Then, if possible, log a new xl_running_xacts record.
    1431                 :             :  *
    1432                 :             :  * This isn't required for the correctness of decoding, but to:
    1433                 :             :  * a) allow isolationtester to notice that we're currently waiting for
    1434                 :             :  *    something.
    1435                 :             :  * b) log a new xl_running_xacts record where it'd be helpful, without having
    1436                 :             :  *    to wait for bgwriter or checkpointer.
    1437                 :             :  * ---
    1438                 :             :  */
    1439                 :             : static void
    1440                 :          19 : SnapBuildWaitSnapshot(xl_running_xacts *running, TransactionId cutoff)
    1441                 :             : {
    1442                 :             :     int         off;
    1443                 :             : 
    1444         [ +  + ]:          35 :     for (off = 0; off < running->xcnt; off++)
    1445                 :             :     {
    1446                 :          18 :         TransactionId xid = running->xids[off];
    1447                 :             : 
    1448                 :             :         /*
    1449                 :             :          * Upper layers should prevent that we ever need to wait on ourselves.
    1450                 :             :          * Check anyway, since failing to do so would either result in an
    1451                 :             :          * endless wait or an Assert() failure.
    1452                 :             :          */
    1453         [ -  + ]:          18 :         if (TransactionIdIsCurrentTransactionId(xid))
    1454         [ #  # ]:           0 :             elog(ERROR, "waiting for ourselves");
    1455                 :             : 
    1456         [ -  + ]:          18 :         if (TransactionIdFollows(xid, cutoff))
    1457                 :           0 :             continue;
    1458                 :             : 
    1459                 :          18 :         XactLockTableWait(xid, NULL, NULL, XLTW_None);
    1460                 :             :     }
    1461                 :             : 
    1462                 :             :     /*
    1463                 :             :      * All transactions we needed to finish finished - try to ensure there is
    1464                 :             :      * another xl_running_xacts record in a timely manner, without having to
    1465                 :             :      * wait for bgwriter or checkpointer to log one.  During recovery we can't
    1466                 :             :      * enforce that, so we'll have to wait.
    1467                 :             :      */
    1468         [ +  - ]:          17 :     if (!RecoveryInProgress())
    1469                 :             :     {
    1470                 :          17 :         LogStandbySnapshot();
    1471                 :             :     }
    1472                 :          17 : }
    1473                 :             : 
    1474                 :             : #define SnapBuildOnDiskConstantSize \
    1475                 :             :     offsetof(SnapBuildOnDisk, builder)
    1476                 :             : #define SnapBuildOnDiskNotChecksummedSize \
    1477                 :             :     offsetof(SnapBuildOnDisk, version)
    1478                 :             : 
    1479                 :             : #define SNAPBUILD_MAGIC 0x51A1E001
    1480                 :             : #define SNAPBUILD_VERSION 6
    1481                 :             : 
    1482                 :             : /*
    1483                 :             :  * Store/Load a snapshot from disk, depending on the snapshot builder's state.
    1484                 :             :  *
    1485                 :             :  * Supposed to be used by external (i.e. not snapbuild.c) code that just read
    1486                 :             :  * a record that's a potential location for a serialized snapshot.
    1487                 :             :  */
    1488                 :             : void
    1489                 :          91 : SnapBuildSerializationPoint(SnapBuild *builder, XLogRecPtr lsn)
    1490                 :             : {
    1491         [ -  + ]:          91 :     if (builder->state < SNAPBUILD_CONSISTENT)
    1492                 :           0 :         SnapBuildRestore(builder, lsn);
    1493                 :             :     else
    1494                 :          91 :         SnapBuildSerialize(builder, lsn);
    1495                 :          91 : }
    1496                 :             : 
    1497                 :             : /*
    1498                 :             :  * Serialize the snapshot 'builder' at the location 'lsn' if it hasn't already
    1499                 :             :  * been done by another decoding process.
    1500                 :             :  */
    1501                 :             : static void
    1502                 :         605 : SnapBuildSerialize(SnapBuild *builder, XLogRecPtr lsn)
    1503                 :             : {
    1504                 :             :     Size        needed_length;
    1505                 :         605 :     SnapBuildOnDisk *ondisk = NULL;
    1506                 :         605 :     TransactionId *catchange_xip = NULL;
    1507                 :             :     MemoryContext old_ctx;
    1508                 :             :     size_t      catchange_xcnt;
    1509                 :             :     char       *ondisk_c;
    1510                 :             :     int         fd;
    1511                 :             :     char        tmppath[MAXPGPATH];
    1512                 :             :     char        path[MAXPGPATH];
    1513                 :             :     int         ret;
    1514                 :             :     struct stat stat_buf;
    1515                 :             :     Size        sz;
    1516                 :             : 
    1517                 :             :     Assert(XLogRecPtrIsValid(lsn));
    1518                 :             :     Assert(!XLogRecPtrIsValid(builder->last_serialized_snapshot) ||
    1519                 :             :            builder->last_serialized_snapshot <= lsn);
    1520                 :             : 
    1521                 :             :     /*
    1522                 :             :      * no point in serializing if we cannot continue to work immediately after
    1523                 :             :      * restoring the snapshot
    1524                 :             :      */
    1525         [ -  + ]:         605 :     if (builder->state < SNAPBUILD_CONSISTENT)
    1526                 :           0 :         return;
    1527                 :             : 
    1528                 :             :     /* consistent snapshots have no next phase */
    1529                 :             :     Assert(builder->next_phase_at == InvalidTransactionId);
    1530                 :             : 
    1531                 :             :     /*
    1532                 :             :      * We identify snapshots by the LSN they are valid for. We don't need to
    1533                 :             :      * include timelines in the name as each LSN maps to exactly one timeline
    1534                 :             :      * unless the user used pg_resetwal or similar. If a user did so, there's
    1535                 :             :      * no hope continuing to decode anyway.
    1536                 :             :      */
    1537                 :         605 :     sprintf(path, "%s/%X-%X.snap",
    1538                 :             :             PG_LOGICAL_SNAPSHOTS_DIR,
    1539                 :         605 :             LSN_FORMAT_ARGS(lsn));
    1540                 :             : 
    1541                 :             :     /*
    1542                 :             :      * first check whether some other backend already has written the snapshot
    1543                 :             :      * for this LSN. It's perfectly fine if there's none, so we accept ENOENT
    1544                 :             :      * as a valid state. Everything else is an unexpected error.
    1545                 :             :      */
    1546                 :         605 :     ret = stat(path, &stat_buf);
    1547                 :             : 
    1548   [ +  +  -  + ]:         605 :     if (ret != 0 && errno != ENOENT)
    1549         [ #  # ]:           0 :         ereport(ERROR,
    1550                 :             :                 (errcode_for_file_access(),
    1551                 :             :                  errmsg("could not stat file \"%s\": %m", path)));
    1552                 :             : 
    1553         [ +  + ]:         605 :     else if (ret == 0)
    1554                 :             :     {
    1555                 :             :         /*
    1556                 :             :          * somebody else has already serialized to this point, don't overwrite
    1557                 :             :          * but remember location, so we don't need to read old data again.
    1558                 :             :          *
    1559                 :             :          * To be sure it has been synced to disk after the rename() from the
    1560                 :             :          * tempfile filename to the real filename, we just repeat the fsync.
    1561                 :             :          * That ought to be cheap because in most scenarios it should already
    1562                 :             :          * be safely on disk.
    1563                 :             :          */
    1564                 :         269 :         fsync_fname(path, false);
    1565                 :         269 :         fsync_fname(PG_LOGICAL_SNAPSHOTS_DIR, true);
    1566                 :             : 
    1567                 :         269 :         builder->last_serialized_snapshot = lsn;
    1568                 :         269 :         goto out;
    1569                 :             :     }
    1570                 :             : 
    1571                 :             :     /*
    1572                 :             :      * there is an obvious race condition here between the time we stat(2) the
    1573                 :             :      * file and us writing the file. But we rename the file into place
    1574                 :             :      * atomically and all files created need to contain the same data anyway,
    1575                 :             :      * so this is perfectly fine, although a bit of a resource waste. Locking
    1576                 :             :      * seems like pointless complication.
    1577                 :             :      */
    1578         [ +  + ]:         336 :     elog(DEBUG1, "serializing snapshot to %s", path);
    1579                 :             : 
    1580                 :             :     /* to make sure only we will write to this tempfile, include pid */
    1581                 :         336 :     sprintf(tmppath, "%s/%X-%X.snap.%d.tmp",
    1582                 :             :             PG_LOGICAL_SNAPSHOTS_DIR,
    1583                 :         336 :             LSN_FORMAT_ARGS(lsn), MyProcPid);
    1584                 :             : 
    1585                 :             :     /*
    1586                 :             :      * Unlink temporary file if it already exists, needs to have been before a
    1587                 :             :      * crash/error since we won't enter this function twice from within a
    1588                 :             :      * single decoding slot/backend and the temporary file contains the pid of
    1589                 :             :      * the current process.
    1590                 :             :      */
    1591   [ +  -  -  + ]:         336 :     if (unlink(tmppath) != 0 && errno != ENOENT)
    1592         [ #  # ]:           0 :         ereport(ERROR,
    1593                 :             :                 (errcode_for_file_access(),
    1594                 :             :                  errmsg("could not remove file \"%s\": %m", tmppath)));
    1595                 :             : 
    1596                 :         336 :     old_ctx = MemoryContextSwitchTo(builder->context);
    1597                 :             : 
    1598                 :             :     /* Get the catalog modifying transactions that are yet not committed */
    1599                 :         336 :     catchange_xip = ReorderBufferGetCatalogChangesXacts(builder->reorder);
    1600                 :         336 :     catchange_xcnt = dclist_count(&builder->reorder->catchange_txns);
    1601                 :             : 
    1602                 :         336 :     needed_length = sizeof(SnapBuildOnDisk) +
    1603                 :         336 :         sizeof(TransactionId) * (builder->committed.xcnt + catchange_xcnt);
    1604                 :             : 
    1605                 :         336 :     ondisk_c = palloc0(needed_length);
    1606                 :         336 :     ondisk = (SnapBuildOnDisk *) ondisk_c;
    1607                 :         336 :     ondisk->magic = SNAPBUILD_MAGIC;
    1608                 :         336 :     ondisk->version = SNAPBUILD_VERSION;
    1609                 :         336 :     ondisk->length = needed_length;
    1610                 :         336 :     INIT_CRC32C(ondisk->checksum);
    1611                 :         336 :     COMP_CRC32C(ondisk->checksum,
    1612                 :             :                 ((char *) ondisk) + SnapBuildOnDiskNotChecksummedSize,
    1613                 :             :                 SnapBuildOnDiskConstantSize - SnapBuildOnDiskNotChecksummedSize);
    1614                 :         336 :     ondisk_c += sizeof(SnapBuildOnDisk);
    1615                 :             : 
    1616                 :         336 :     memcpy(&ondisk->builder, builder, sizeof(SnapBuild));
    1617                 :             :     /* NULL-ify memory-only data */
    1618                 :         336 :     ondisk->builder.context = NULL;
    1619                 :         336 :     ondisk->builder.snapshot = NULL;
    1620                 :         336 :     ondisk->builder.reorder = NULL;
    1621                 :         336 :     ondisk->builder.committed.xip = NULL;
    1622                 :         336 :     ondisk->builder.catchange.xip = NULL;
    1623                 :             :     /* update catchange only on disk data */
    1624                 :         336 :     ondisk->builder.catchange.xcnt = catchange_xcnt;
    1625                 :             : 
    1626                 :         336 :     COMP_CRC32C(ondisk->checksum,
    1627                 :             :                 &ondisk->builder,
    1628                 :             :                 sizeof(SnapBuild));
    1629                 :             : 
    1630                 :             :     /* copy committed xacts */
    1631         [ +  + ]:         336 :     if (builder->committed.xcnt > 0)
    1632                 :             :     {
    1633                 :          61 :         sz = sizeof(TransactionId) * builder->committed.xcnt;
    1634                 :          61 :         memcpy(ondisk_c, builder->committed.xip, sz);
    1635                 :          61 :         COMP_CRC32C(ondisk->checksum, ondisk_c, sz);
    1636                 :          61 :         ondisk_c += sz;
    1637                 :             :     }
    1638                 :             : 
    1639                 :             :     /* copy catalog modifying xacts */
    1640         [ +  + ]:         336 :     if (catchange_xcnt > 0)
    1641                 :             :     {
    1642                 :           8 :         sz = sizeof(TransactionId) * catchange_xcnt;
    1643                 :           8 :         memcpy(ondisk_c, catchange_xip, sz);
    1644                 :           8 :         COMP_CRC32C(ondisk->checksum, ondisk_c, sz);
    1645                 :           8 :         ondisk_c += sz;
    1646                 :             :     }
    1647                 :             : 
    1648                 :         336 :     FIN_CRC32C(ondisk->checksum);
    1649                 :             : 
    1650                 :             :     /* we have valid data now, open tempfile and write it there */
    1651                 :         336 :     fd = OpenTransientFile(tmppath,
    1652                 :             :                            O_CREAT | O_EXCL | O_WRONLY | PG_BINARY);
    1653         [ -  + ]:         336 :     if (fd < 0)
    1654         [ #  # ]:           0 :         ereport(ERROR,
    1655                 :             :                 (errcode_for_file_access(),
    1656                 :             :                  errmsg("could not open file \"%s\": %m", tmppath)));
    1657                 :             : 
    1658                 :         336 :     errno = 0;
    1659                 :         336 :     pgstat_report_wait_start(WAIT_EVENT_SNAPBUILD_WRITE);
    1660         [ -  + ]:         336 :     if ((write(fd, ondisk, needed_length)) != needed_length)
    1661                 :             :     {
    1662                 :           0 :         int         save_errno = errno;
    1663                 :             : 
    1664                 :           0 :         CloseTransientFile(fd);
    1665                 :             : 
    1666                 :             :         /* if write didn't set errno, assume problem is no disk space */
    1667         [ #  # ]:           0 :         errno = save_errno ? save_errno : ENOSPC;
    1668         [ #  # ]:           0 :         ereport(ERROR,
    1669                 :             :                 (errcode_for_file_access(),
    1670                 :             :                  errmsg("could not write to file \"%s\": %m", tmppath)));
    1671                 :             :     }
    1672                 :         336 :     pgstat_report_wait_end();
    1673                 :             : 
    1674                 :             :     /*
    1675                 :             :      * fsync the file before renaming so that even if we crash after this we
    1676                 :             :      * have either a fully valid file or nothing.
    1677                 :             :      *
    1678                 :             :      * It's safe to just ERROR on fsync() here because we'll retry the whole
    1679                 :             :      * operation including the writes.
    1680                 :             :      *
    1681                 :             :      * TODO: Do the fsync() via checkpoints/restartpoints, doing it here has
    1682                 :             :      * some noticeable overhead since it's performed synchronously during
    1683                 :             :      * decoding?
    1684                 :             :      */
    1685                 :         336 :     pgstat_report_wait_start(WAIT_EVENT_SNAPBUILD_SYNC);
    1686         [ -  + ]:         336 :     if (pg_fsync(fd) != 0)
    1687                 :             :     {
    1688                 :           0 :         int         save_errno = errno;
    1689                 :             : 
    1690                 :           0 :         CloseTransientFile(fd);
    1691                 :           0 :         errno = save_errno;
    1692         [ #  # ]:           0 :         ereport(ERROR,
    1693                 :             :                 (errcode_for_file_access(),
    1694                 :             :                  errmsg("could not fsync file \"%s\": %m", tmppath)));
    1695                 :             :     }
    1696                 :         336 :     pgstat_report_wait_end();
    1697                 :             : 
    1698         [ -  + ]:         336 :     if (CloseTransientFile(fd) != 0)
    1699         [ #  # ]:           0 :         ereport(ERROR,
    1700                 :             :                 (errcode_for_file_access(),
    1701                 :             :                  errmsg("could not close file \"%s\": %m", tmppath)));
    1702                 :             : 
    1703                 :         336 :     fsync_fname(PG_LOGICAL_SNAPSHOTS_DIR, true);
    1704                 :             : 
    1705                 :             :     /*
    1706                 :             :      * We may overwrite the work from some other backend, but that's ok, our
    1707                 :             :      * snapshot is valid as well, we'll just have done some superfluous work.
    1708                 :             :      */
    1709         [ -  + ]:         336 :     if (rename(tmppath, path) != 0)
    1710                 :             :     {
    1711         [ #  # ]:           0 :         ereport(ERROR,
    1712                 :             :                 (errcode_for_file_access(),
    1713                 :             :                  errmsg("could not rename file \"%s\" to \"%s\": %m",
    1714                 :             :                         tmppath, path)));
    1715                 :             :     }
    1716                 :             : 
    1717                 :             :     /* make sure we persist */
    1718                 :         336 :     fsync_fname(path, false);
    1719                 :         336 :     fsync_fname(PG_LOGICAL_SNAPSHOTS_DIR, true);
    1720                 :             : 
    1721                 :             :     /*
    1722                 :             :      * Now there's no way we can lose the dumped state anymore, remember this
    1723                 :             :      * as a serialization point.
    1724                 :             :      */
    1725                 :         336 :     builder->last_serialized_snapshot = lsn;
    1726                 :             : 
    1727                 :         336 :     MemoryContextSwitchTo(old_ctx);
    1728                 :             : 
    1729                 :         605 : out:
    1730                 :         605 :     ReorderBufferSetRestartPoint(builder->reorder,
    1731                 :             :                                  builder->last_serialized_snapshot);
    1732                 :             :     /* be tidy */
    1733         [ +  + ]:         605 :     if (ondisk)
    1734                 :         336 :         pfree(ondisk);
    1735         [ +  + ]:         605 :     if (catchange_xip)
    1736                 :           8 :         pfree(catchange_xip);
    1737                 :             : }
    1738                 :             : 
    1739                 :             : /*
    1740                 :             :  * Restore the logical snapshot file contents to 'ondisk'.
    1741                 :             :  *
    1742                 :             :  * 'context' is the memory context where the catalog modifying/committed xid
    1743                 :             :  * will live.
    1744                 :             :  * If 'missing_ok' is true, will not throw an error if the file is not found.
    1745                 :             :  */
    1746                 :             : bool
    1747                 :          21 : SnapBuildRestoreSnapshot(SnapBuildOnDisk *ondisk, XLogRecPtr lsn,
    1748                 :             :                          MemoryContext context, bool missing_ok)
    1749                 :             : {
    1750                 :             :     int         fd;
    1751                 :             :     pg_crc32c   checksum;
    1752                 :             :     Size        sz;
    1753                 :             :     char        path[MAXPGPATH];
    1754                 :             : 
    1755                 :          21 :     sprintf(path, "%s/%X-%X.snap",
    1756                 :             :             PG_LOGICAL_SNAPSHOTS_DIR,
    1757                 :          21 :             LSN_FORMAT_ARGS(lsn));
    1758                 :             : 
    1759                 :          21 :     fd = OpenTransientFile(path, O_RDONLY | PG_BINARY);
    1760                 :             : 
    1761         [ +  + ]:          21 :     if (fd < 0)
    1762                 :             :     {
    1763   [ +  -  +  - ]:          11 :         if (missing_ok && errno == ENOENT)
    1764                 :          11 :             return false;
    1765                 :             : 
    1766         [ #  # ]:           0 :         ereport(ERROR,
    1767                 :             :                 (errcode_for_file_access(),
    1768                 :             :                  errmsg("could not open file \"%s\": %m", path)));
    1769                 :             :     }
    1770                 :             : 
    1771                 :             :     /* ----
    1772                 :             :      * Make sure the snapshot had been stored safely to disk, that's normally
    1773                 :             :      * cheap.
    1774                 :             :      * Note that we do not need PANIC here, nobody will be able to use the
    1775                 :             :      * slot without fsyncing, and saving it won't succeed without an fsync()
    1776                 :             :      * either...
    1777                 :             :      * ----
    1778                 :             :      */
    1779                 :          10 :     fsync_fname(path, false);
    1780                 :          10 :     fsync_fname(PG_LOGICAL_SNAPSHOTS_DIR, true);
    1781                 :             : 
    1782                 :             :     /* read statically sized portion of snapshot */
    1783                 :          10 :     SnapBuildRestoreContents(fd, ondisk, SnapBuildOnDiskConstantSize, path);
    1784                 :             : 
    1785         [ -  + ]:          10 :     if (ondisk->magic != SNAPBUILD_MAGIC)
    1786         [ #  # ]:           0 :         ereport(ERROR,
    1787                 :             :                 (errcode(ERRCODE_DATA_CORRUPTED),
    1788                 :             :                  errmsg("snapbuild state file \"%s\" has wrong magic number: %u instead of %u",
    1789                 :             :                         path, ondisk->magic, SNAPBUILD_MAGIC)));
    1790                 :             : 
    1791         [ -  + ]:          10 :     if (ondisk->version != SNAPBUILD_VERSION)
    1792         [ #  # ]:           0 :         ereport(ERROR,
    1793                 :             :                 (errcode(ERRCODE_DATA_CORRUPTED),
    1794                 :             :                  errmsg("snapbuild state file \"%s\" has unsupported version: %u instead of %u",
    1795                 :             :                         path, ondisk->version, SNAPBUILD_VERSION)));
    1796                 :             : 
    1797                 :          10 :     INIT_CRC32C(checksum);
    1798                 :          10 :     COMP_CRC32C(checksum,
    1799                 :             :                 ((char *) ondisk) + SnapBuildOnDiskNotChecksummedSize,
    1800                 :             :                 SnapBuildOnDiskConstantSize - SnapBuildOnDiskNotChecksummedSize);
    1801                 :             : 
    1802                 :             :     /* read SnapBuild */
    1803                 :          10 :     SnapBuildRestoreContents(fd, &ondisk->builder, sizeof(SnapBuild), path);
    1804                 :          10 :     COMP_CRC32C(checksum, &ondisk->builder, sizeof(SnapBuild));
    1805                 :             : 
    1806                 :             :     /* restore committed xacts information */
    1807         [ +  + ]:          10 :     if (ondisk->builder.committed.xcnt > 0)
    1808                 :             :     {
    1809                 :           5 :         sz = sizeof(TransactionId) * ondisk->builder.committed.xcnt;
    1810                 :           5 :         ondisk->builder.committed.xip = MemoryContextAllocZero(context, sz);
    1811                 :           5 :         SnapBuildRestoreContents(fd, ondisk->builder.committed.xip, sz, path);
    1812                 :           5 :         COMP_CRC32C(checksum, ondisk->builder.committed.xip, sz);
    1813                 :             :     }
    1814                 :             : 
    1815                 :             :     /* restore catalog modifying xacts information */
    1816         [ +  + ]:          10 :     if (ondisk->builder.catchange.xcnt > 0)
    1817                 :             :     {
    1818                 :           4 :         sz = sizeof(TransactionId) * ondisk->builder.catchange.xcnt;
    1819                 :           4 :         ondisk->builder.catchange.xip = MemoryContextAllocZero(context, sz);
    1820                 :           4 :         SnapBuildRestoreContents(fd, ondisk->builder.catchange.xip, sz, path);
    1821                 :           4 :         COMP_CRC32C(checksum, ondisk->builder.catchange.xip, sz);
    1822                 :             :     }
    1823                 :             : 
    1824         [ -  + ]:          10 :     if (CloseTransientFile(fd) != 0)
    1825         [ #  # ]:           0 :         ereport(ERROR,
    1826                 :             :                 (errcode_for_file_access(),
    1827                 :             :                  errmsg("could not close file \"%s\": %m", path)));
    1828                 :             : 
    1829                 :          10 :     FIN_CRC32C(checksum);
    1830                 :             : 
    1831                 :             :     /* verify checksum of what we've read */
    1832         [ -  + ]:          10 :     if (!EQ_CRC32C(checksum, ondisk->checksum))
    1833         [ #  # ]:           0 :         ereport(ERROR,
    1834                 :             :                 (errcode(ERRCODE_DATA_CORRUPTED),
    1835                 :             :                  errmsg("checksum mismatch for snapbuild state file \"%s\": is %u, should be %u",
    1836                 :             :                         path, checksum, ondisk->checksum)));
    1837                 :             : 
    1838                 :          10 :     return true;
    1839                 :             : }
    1840                 :             : 
    1841                 :             : /*
    1842                 :             :  * Restore a snapshot into 'builder' if previously one has been stored at the
    1843                 :             :  * location indicated by 'lsn'. Returns true if successful, false otherwise.
    1844                 :             :  */
    1845                 :             : static bool
    1846                 :          19 : SnapBuildRestore(SnapBuild *builder, XLogRecPtr lsn)
    1847                 :             : {
    1848                 :             :     SnapBuildOnDisk ondisk;
    1849                 :             : 
    1850                 :             :     /* no point in loading a snapshot if we're already there */
    1851         [ -  + ]:          19 :     if (builder->state == SNAPBUILD_CONSISTENT)
    1852                 :           0 :         return false;
    1853                 :             : 
    1854                 :             :     /* validate and restore the snapshot to 'ondisk' */
    1855         [ +  + ]:          19 :     if (!SnapBuildRestoreSnapshot(&ondisk, lsn, builder->context, true))
    1856                 :          11 :         return false;
    1857                 :             : 
    1858                 :             :     /*
    1859                 :             :      * ok, we now have a sensible snapshot here, figure out if it has more
    1860                 :             :      * information than we have.
    1861                 :             :      */
    1862                 :             : 
    1863                 :             :     /*
    1864                 :             :      * We are only interested in consistent snapshots for now, comparing
    1865                 :             :      * whether one incomplete snapshot is more "advanced" seems to be
    1866                 :             :      * unnecessarily complex.
    1867                 :             :      */
    1868         [ -  + ]:           8 :     if (ondisk.builder.state < SNAPBUILD_CONSISTENT)
    1869                 :           0 :         goto snapshot_not_interesting;
    1870                 :             : 
    1871                 :             :     /*
    1872                 :             :      * Don't use a snapshot that requires an xmin that we cannot guarantee to
    1873                 :             :      * be available.
    1874                 :             :      */
    1875         [ -  + ]:           8 :     if (TransactionIdPrecedes(ondisk.builder.xmin, builder->initial_xmin_horizon))
    1876                 :           0 :         goto snapshot_not_interesting;
    1877                 :             : 
    1878                 :             :     /*
    1879                 :             :      * Consistent snapshots have no next phase. Reset next_phase_at as it is
    1880                 :             :      * possible that an old value may remain.
    1881                 :             :      */
    1882                 :             :     Assert(ondisk.builder.next_phase_at == InvalidTransactionId);
    1883                 :           8 :     builder->next_phase_at = InvalidTransactionId;
    1884                 :             : 
    1885                 :             :     /* ok, we think the snapshot is sensible, copy over everything important */
    1886                 :           8 :     builder->xmin = ondisk.builder.xmin;
    1887                 :           8 :     builder->xmax = ondisk.builder.xmax;
    1888                 :           8 :     builder->state = ondisk.builder.state;
    1889                 :             : 
    1890                 :           8 :     builder->committed.xcnt = ondisk.builder.committed.xcnt;
    1891                 :             :     /* We only allocated/stored xcnt, not xcnt_space xids ! */
    1892                 :             :     /* don't overwrite preallocated xip, if we don't have anything here */
    1893         [ +  + ]:           8 :     if (builder->committed.xcnt > 0)
    1894                 :             :     {
    1895                 :           3 :         pfree(builder->committed.xip);
    1896                 :           3 :         builder->committed.xcnt_space = ondisk.builder.committed.xcnt;
    1897                 :           3 :         builder->committed.xip = ondisk.builder.committed.xip;
    1898                 :             :     }
    1899                 :           8 :     ondisk.builder.committed.xip = NULL;
    1900                 :             : 
    1901                 :             :     /* set catalog modifying transactions */
    1902         [ -  + ]:           8 :     if (builder->catchange.xip)
    1903                 :           0 :         pfree(builder->catchange.xip);
    1904                 :           8 :     builder->catchange.xcnt = ondisk.builder.catchange.xcnt;
    1905                 :           8 :     builder->catchange.xip = ondisk.builder.catchange.xip;
    1906                 :           8 :     ondisk.builder.catchange.xip = NULL;
    1907                 :             : 
    1908                 :             :     /* our snapshot is not interesting anymore, build a new one */
    1909         [ -  + ]:           8 :     if (builder->snapshot != NULL)
    1910                 :             :     {
    1911                 :           0 :         SnapBuildSnapDecRefcount(builder->snapshot);
    1912                 :             :     }
    1913                 :           8 :     builder->snapshot = SnapBuildBuildSnapshot(builder);
    1914                 :           8 :     SnapBuildSnapIncRefcount(builder->snapshot);
    1915                 :             : 
    1916                 :           8 :     ReorderBufferSetRestartPoint(builder->reorder, lsn);
    1917                 :             : 
    1918                 :             :     Assert(builder->state == SNAPBUILD_CONSISTENT);
    1919                 :             : 
    1920   [ +  +  +  + ]:           8 :     ereport(LogicalDecodingLogLevel(),
    1921                 :             :             errmsg("logical decoding found consistent point at %X/%08X",
    1922                 :             :                    LSN_FORMAT_ARGS(lsn)),
    1923                 :             :             errdetail("Logical decoding will begin using saved snapshot."));
    1924                 :           8 :     return true;
    1925                 :             : 
    1926                 :           0 : snapshot_not_interesting:
    1927         [ #  # ]:           0 :     if (ondisk.builder.committed.xip != NULL)
    1928                 :           0 :         pfree(ondisk.builder.committed.xip);
    1929         [ #  # ]:           0 :     if (ondisk.builder.catchange.xip != NULL)
    1930                 :           0 :         pfree(ondisk.builder.catchange.xip);
    1931                 :           0 :     return false;
    1932                 :             : }
    1933                 :             : 
    1934                 :             : /*
    1935                 :             :  * Read the contents of the serialized snapshot to 'dest'.
    1936                 :             :  */
    1937                 :             : static void
    1938                 :          29 : SnapBuildRestoreContents(int fd, void *dest, Size size, const char *path)
    1939                 :             : {
    1940                 :             :     ssize_t     readBytes;
    1941                 :             : 
    1942                 :          29 :     pgstat_report_wait_start(WAIT_EVENT_SNAPBUILD_READ);
    1943                 :          29 :     readBytes = read(fd, dest, size);
    1944                 :          29 :     pgstat_report_wait_end();
    1945         [ -  + ]:          29 :     if (readBytes != size)
    1946                 :             :     {
    1947                 :           0 :         int         save_errno = errno;
    1948                 :             : 
    1949                 :           0 :         CloseTransientFile(fd);
    1950                 :             : 
    1951         [ #  # ]:           0 :         if (readBytes < 0)
    1952                 :             :         {
    1953                 :           0 :             errno = save_errno;
    1954         [ #  # ]:           0 :             ereport(ERROR,
    1955                 :             :                     (errcode_for_file_access(),
    1956                 :             :                      errmsg("could not read file \"%s\": %m", path)));
    1957                 :             :         }
    1958                 :             :         else
    1959         [ #  # ]:           0 :             ereport(ERROR,
    1960                 :             :                     (errcode(ERRCODE_DATA_CORRUPTED),
    1961                 :             :                      errmsg("could not read file \"%s\": read %zd of %zu",
    1962                 :             :                             path, readBytes, size)));
    1963                 :             :     }
    1964                 :          29 : }
    1965                 :             : 
    1966                 :             : /*
    1967                 :             :  * Remove all serialized snapshots that are not required anymore because no
    1968                 :             :  * slot can need them. This doesn't actually have to run during a checkpoint,
    1969                 :             :  * but it's a convenient point to schedule this.
    1970                 :             :  *
    1971                 :             :  * NB: We run this during checkpoints even if logical decoding is disabled so
    1972                 :             :  * we cleanup old slots at some point after it got disabled.
    1973                 :             :  */
    1974                 :             : void
    1975                 :        1950 : CheckPointSnapBuild(void)
    1976                 :             : {
    1977                 :             :     XLogRecPtr  cutoff;
    1978                 :             :     XLogRecPtr  redo;
    1979                 :             :     DIR        *snap_dir;
    1980                 :             :     struct dirent *snap_de;
    1981                 :             :     char        path[MAXPGPATH + sizeof(PG_LOGICAL_SNAPSHOTS_DIR)];
    1982                 :             : 
    1983                 :             :     /*
    1984                 :             :      * We start off with a minimum of the last redo pointer. No new
    1985                 :             :      * replication slot will start before that, so that's a safe upper bound
    1986                 :             :      * for removal.
    1987                 :             :      */
    1988                 :        1950 :     redo = GetRedoRecPtr();
    1989                 :             : 
    1990                 :             :     /* now check for the restart ptrs from existing slots */
    1991                 :        1950 :     cutoff = ReplicationSlotsComputeLogicalRestartLSN();
    1992                 :             : 
    1993                 :             :     /* don't start earlier than the restart lsn */
    1994         [ +  + ]:        1950 :     if (redo < cutoff)
    1995                 :           1 :         cutoff = redo;
    1996                 :             : 
    1997                 :        1950 :     snap_dir = AllocateDir(PG_LOGICAL_SNAPSHOTS_DIR);
    1998         [ +  + ]:        6153 :     while ((snap_de = ReadDir(snap_dir, PG_LOGICAL_SNAPSHOTS_DIR)) != NULL)
    1999                 :             :     {
    2000                 :             :         uint32      hi;
    2001                 :             :         uint32      lo;
    2002                 :             :         XLogRecPtr  lsn;
    2003                 :             :         PGFileType  de_type;
    2004                 :             : 
    2005         [ +  + ]:        4203 :         if (strcmp(snap_de->d_name, ".") == 0 ||
    2006         [ +  + ]:        2253 :             strcmp(snap_de->d_name, "..") == 0)
    2007                 :        3900 :             continue;
    2008                 :             : 
    2009                 :         303 :         snprintf(path, sizeof(path), "%s/%s", PG_LOGICAL_SNAPSHOTS_DIR, snap_de->d_name);
    2010                 :         303 :         de_type = get_dirent_type(path, snap_de, false, DEBUG1);
    2011                 :             : 
    2012   [ +  -  -  + ]:         303 :         if (de_type != PGFILETYPE_ERROR && de_type != PGFILETYPE_REG)
    2013                 :             :         {
    2014         [ #  # ]:           0 :             elog(DEBUG1, "only regular files expected: %s", path);
    2015                 :           0 :             continue;
    2016                 :             :         }
    2017                 :             : 
    2018                 :             :         /*
    2019                 :             :          * temporary filenames from SnapBuildSerialize() include the LSN and
    2020                 :             :          * everything but are postfixed by .$pid.tmp. We can just remove them
    2021                 :             :          * the same as other files because there can be none that are
    2022                 :             :          * currently being written that are older than cutoff.
    2023                 :             :          *
    2024                 :             :          * We just log a message if a file doesn't fit the pattern, it's
    2025                 :             :          * probably some editors lock/state file or similar...
    2026                 :             :          */
    2027         [ -  + ]:         303 :         if (sscanf(snap_de->d_name, "%X-%X.snap", &hi, &lo) != 2)
    2028                 :             :         {
    2029         [ #  # ]:           0 :             ereport(LOG,
    2030                 :             :                     (errmsg("could not parse file name \"%s\"", path)));
    2031                 :           0 :             continue;
    2032                 :             :         }
    2033                 :             : 
    2034                 :         303 :         lsn = ((uint64) hi) << 32 | lo;
    2035                 :             : 
    2036                 :             :         /* check whether we still need it */
    2037   [ +  +  +  + ]:         303 :         if (lsn < cutoff || !XLogRecPtrIsValid(cutoff))
    2038                 :             :         {
    2039         [ +  + ]:         197 :             elog(DEBUG1, "removing snapbuild snapshot %s", path);
    2040                 :             : 
    2041                 :             :             /*
    2042                 :             :              * It's not particularly harmful, though strange, if we can't
    2043                 :             :              * remove the file here. Don't prevent the checkpoint from
    2044                 :             :              * completing, that'd be a cure worse than the disease.
    2045                 :             :              */
    2046         [ -  + ]:         197 :             if (unlink(path) < 0)
    2047                 :             :             {
    2048         [ #  # ]:           0 :                 ereport(LOG,
    2049                 :             :                         (errcode_for_file_access(),
    2050                 :             :                          errmsg("could not remove file \"%s\": %m",
    2051                 :             :                                 path)));
    2052                 :           0 :                 continue;
    2053                 :             :             }
    2054                 :             :         }
    2055                 :             :     }
    2056                 :        1950 :     FreeDir(snap_dir);
    2057                 :        1950 : }
    2058                 :             : 
    2059                 :             : /*
    2060                 :             :  * Check if a logical snapshot at the specified point has been serialized.
    2061                 :             :  */
    2062                 :             : bool
    2063                 :          14 : SnapBuildSnapshotExists(XLogRecPtr lsn)
    2064                 :             : {
    2065                 :             :     char        path[MAXPGPATH];
    2066                 :             :     int         ret;
    2067                 :             :     struct stat stat_buf;
    2068                 :             : 
    2069                 :          14 :     sprintf(path, "%s/%X-%X.snap",
    2070                 :             :             PG_LOGICAL_SNAPSHOTS_DIR,
    2071                 :          14 :             LSN_FORMAT_ARGS(lsn));
    2072                 :             : 
    2073                 :          14 :     ret = stat(path, &stat_buf);
    2074                 :             : 
    2075   [ +  +  -  + ]:          14 :     if (ret != 0 && errno != ENOENT)
    2076         [ #  # ]:           0 :         ereport(ERROR,
    2077                 :             :                 (errcode_for_file_access(),
    2078                 :             :                  errmsg("could not stat file \"%s\": %m", path)));
    2079                 :             : 
    2080                 :          14 :     return ret == 0;
    2081                 :             : }
        

Generated by: LCOV version 2.0-1