LCOV - differential code coverage report
Current view: top level - src/backend/replication/logical - reorderbuffer.c (source / functions) Coverage Total Hit UNC LBC UBC GBC GNC CBC EUB ECB DCB
Current: c70b6db34ffeab48beef1fb4ce61bcad3772b8dd vs 06473f5a344df8c9594ead90a609b86f6724cff8 Lines: 93.6 % 1762 1649 113 2 1647 2
Current Date: 2025-09-06 07:49:51 +0900 Functions: 100.0 % 94 94 2 92
Baseline: lcov-20250906-005545-baseline Branches: 69.1 % 1173 810 1 1 361 1 3 806 3 3
Baseline Date: 2025-09-05 08:21:35 +0100 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(30,360] days: 88.8 % 161 143 18 2 141
(360..) days: 94.1 % 1601 1506 95 1506
Function coverage date bins:
(30,360] days: 100.0 % 14 14 14
(360..) days: 100.0 % 80 80 2 78
Branch coverage date bins:
(30,360] days: 67.3 % 110 74 1 35 3 71
(360..) days: 68.8 % 1069 736 1 326 1 735 3 3

 Age         Owner                    Branch data    TLA  Line data    Source code
                                  1                 :                : /*-------------------------------------------------------------------------
                                  2                 :                :  *
                                  3                 :                :  * reorderbuffer.c
                                  4                 :                :  *    PostgreSQL logical replay/reorder buffer management
                                  5                 :                :  *
                                  6                 :                :  *
                                  7                 :                :  * Copyright (c) 2012-2025, PostgreSQL Global Development Group
                                  8                 :                :  *
                                  9                 :                :  *
                                 10                 :                :  * IDENTIFICATION
                                 11                 :                :  *    src/backend/replication/logical/reorderbuffer.c
                                 12                 :                :  *
                                 13                 :                :  * NOTES
                                 14                 :                :  *    This module gets handed individual pieces of transactions in the order
                                 15                 :                :  *    they are written to the WAL and is responsible to reassemble them into
                                 16                 :                :  *    toplevel transaction sized pieces. When a transaction is completely
                                 17                 :                :  *    reassembled - signaled by reading the transaction commit record - it
                                 18                 :                :  *    will then call the output plugin (cf. ReorderBufferCommit()) with the
                                 19                 :                :  *    individual changes. The output plugins rely on snapshots built by
                                 20                 :                :  *    snapbuild.c which hands them to us.
                                 21                 :                :  *
                                 22                 :                :  *    Transactions and subtransactions/savepoints in postgres are not
                                 23                 :                :  *    immediately linked to each other from outside the performing
                                 24                 :                :  *    backend. Only at commit/abort (or special xact_assignment records) they
                                 25                 :                :  *    are linked together. Which means that we will have to splice together a
                                 26                 :                :  *    toplevel transaction from its subtransactions. To do that efficiently we
                                 27                 :                :  *    build a binary heap indexed by the smallest current lsn of the individual
                                 28                 :                :  *    subtransactions' changestreams. As the individual streams are inherently
                                 29                 :                :  *    ordered by LSN - since that is where we build them from - the transaction
                                 30                 :                :  *    can easily be reassembled by always using the subtransaction with the
                                 31                 :                :  *    smallest current LSN from the heap.
                                 32                 :                :  *
                                 33                 :                :  *    In order to cope with large transactions - which can be several times as
                                 34                 :                :  *    big as the available memory - this module supports spooling the contents
                                 35                 :                :  *    of large transactions to disk. When the transaction is replayed the
                                 36                 :                :  *    contents of individual (sub-)transactions will be read from disk in
                                 37                 :                :  *    chunks.
                                 38                 :                :  *
                                 39                 :                :  *    This module also has to deal with reassembling toast records from the
                                 40                 :                :  *    individual chunks stored in WAL. When a new (or initial) version of a
                                 41                 :                :  *    tuple is stored in WAL it will always be preceded by the toast chunks
                                 42                 :                :  *    emitted for the columns stored out of line. Within a single toplevel
                                 43                 :                :  *    transaction there will be no other data carrying records between a row's
                                 44                 :                :  *    toast chunks and the row data itself. See ReorderBufferToast* for
                                 45                 :                :  *    details.
                                 46                 :                :  *
                                 47                 :                :  *    ReorderBuffer uses two special memory context types - SlabContext for
                                 48                 :                :  *    allocations of fixed-length structures (changes and transactions), and
                                 49                 :                :  *    GenerationContext for the variable-length transaction data (allocated
                                 50                 :                :  *    and freed in groups with similar lifespans).
                                 51                 :                :  *
                                 52                 :                :  *    To limit the amount of memory used by decoded changes, we track memory
                                 53                 :                :  *    used at the reorder buffer level (i.e. total amount of memory), and for
                                 54                 :                :  *    each transaction. When the total amount of used memory exceeds the
                                 55                 :                :  *    limit, the transaction consuming the most memory is then serialized to
                                 56                 :                :  *    disk.
                                 57                 :                :  *
                                 58                 :                :  *    Only decoded changes are evicted from memory (spilled to disk), not the
                                 59                 :                :  *    transaction records. The number of toplevel transactions is limited,
                                 60                 :                :  *    but a transaction with many subtransactions may still consume significant
                                 61                 :                :  *    amounts of memory. However, the transaction records are fairly small and
                                 62                 :                :  *    are not included in the memory limit.
                                 63                 :                :  *
                                 64                 :                :  *    The current eviction algorithm is very simple - the transaction is
                                 65                 :                :  *    picked merely by size, while it might be useful to also consider age
                                 66                 :                :  *    (LSN) of the changes for example. With the new Generational memory
                                 67                 :                :  *    allocator, evicting the oldest changes would make it more likely the
                                 68                 :                :  *    memory gets actually freed.
                                 69                 :                :  *
                                 70                 :                :  *    We use a max-heap with transaction size as the key to efficiently find
                                 71                 :                :  *    the largest transaction. We update the max-heap whenever the memory
                                 72                 :                :  *    counter is updated; however transactions with size 0 are not stored in
                                 73                 :                :  *    the heap, because they have no changes to evict.
                                 74                 :                :  *
                                 75                 :                :  *    We still rely on max_changes_in_memory when loading serialized changes
                                 76                 :                :  *    back into memory. At that point we can't use the memory limit directly
                                 77                 :                :  *    as we load the subxacts independently. One option to deal with this
                                 78                 :                :  *    would be to count the subxacts, and allow each to allocate 1/N of the
                                 79                 :                :  *    memory limit. That however does not seem very appealing, because with
                                 80                 :                :  *    many subtransactions it may easily cause thrashing (short cycles of
                                 81                 :                :  *    deserializing and applying very few changes). We probably should give
                                 82                 :                :  *    a bit more memory to the oldest subtransactions, because it's likely
                                 83                 :                :  *    they are the source for the next sequence of changes.
                                 84                 :                :  *
                                 85                 :                :  * -------------------------------------------------------------------------
                                 86                 :                :  */
                                 87                 :                : #include "postgres.h"
                                 88                 :                : 
                                 89                 :                : #include <unistd.h>
                                 90                 :                : #include <sys/stat.h>
                                 91                 :                : 
                                 92                 :                : #include "access/detoast.h"
                                 93                 :                : #include "access/heapam.h"
                                 94                 :                : #include "access/rewriteheap.h"
                                 95                 :                : #include "access/transam.h"
                                 96                 :                : #include "access/xact.h"
                                 97                 :                : #include "access/xlog_internal.h"
                                 98                 :                : #include "catalog/catalog.h"
                                 99                 :                : #include "common/int.h"
                                100                 :                : #include "lib/binaryheap.h"
                                101                 :                : #include "miscadmin.h"
                                102                 :                : #include "pgstat.h"
                                103                 :                : #include "replication/logical.h"
                                104                 :                : #include "replication/reorderbuffer.h"
                                105                 :                : #include "replication/slot.h"
                                106                 :                : #include "replication/snapbuild.h"    /* just for SnapBuildSnapDecRefcount */
                                107                 :                : #include "storage/bufmgr.h"
                                108                 :                : #include "storage/fd.h"
                                109                 :                : #include "storage/procarray.h"
                                110                 :                : #include "storage/sinval.h"
                                111                 :                : #include "utils/builtins.h"
                                112                 :                : #include "utils/inval.h"
                                113                 :                : #include "utils/memutils.h"
                                114                 :                : #include "utils/rel.h"
                                115                 :                : #include "utils/relfilenumbermap.h"
                                116                 :                : 
                                117                 :                : /*
                                118                 :                :  * Each transaction has an 8MB limit for invalidation messages distributed from
                                119                 :                :  * other transactions. This limit is set considering scenarios with many
                                120                 :                :  * concurrent logical decoding operations. When the distributed invalidation
                                121                 :                :  * messages reach this threshold, the transaction is marked as
                                122                 :                :  * RBTXN_DISTR_INVAL_OVERFLOWED to invalidate the complete cache as we have lost
                                123                 :                :  * some inval messages and hence don't know what needs to be invalidated.
                                124                 :                :  */
                                125                 :                : #define MAX_DISTR_INVAL_MSG_PER_TXN \
                                126                 :                :     ((8 * 1024 * 1024) / sizeof(SharedInvalidationMessage))
                                127                 :                : 
                                128                 :                : /* entry for a hash table we use to map from xid to our transaction state */
                                129                 :                : typedef struct ReorderBufferTXNByIdEnt
                                130                 :                : {
                                131                 :                :     TransactionId xid;
                                132                 :                :     ReorderBufferTXN *txn;
                                133                 :                : } ReorderBufferTXNByIdEnt;
                                134                 :                : 
                                135                 :                : /* data structures for (relfilelocator, ctid) => (cmin, cmax) mapping */
                                136                 :                : typedef struct ReorderBufferTupleCidKey
                                137                 :                : {
                                138                 :                :     RelFileLocator rlocator;
                                139                 :                :     ItemPointerData tid;
                                140                 :                : } ReorderBufferTupleCidKey;
                                141                 :                : 
                                142                 :                : typedef struct ReorderBufferTupleCidEnt
                                143                 :                : {
                                144                 :                :     ReorderBufferTupleCidKey key;
                                145                 :                :     CommandId   cmin;
                                146                 :                :     CommandId   cmax;
                                147                 :                :     CommandId   combocid;       /* just for debugging */
                                148                 :                : } ReorderBufferTupleCidEnt;
                                149                 :                : 
                                150                 :                : /* Virtual file descriptor with file offset tracking */
                                151                 :                : typedef struct TXNEntryFile
                                152                 :                : {
                                153                 :                :     File        vfd;            /* -1 when the file is closed */
                                154                 :                :     off_t       curOffset;      /* offset for next write or read. Reset to 0
                                155                 :                :                                  * when vfd is opened. */
                                156                 :                : } TXNEntryFile;
                                157                 :                : 
                                158                 :                : /* k-way in-order change iteration support structures */
                                159                 :                : typedef struct ReorderBufferIterTXNEntry
                                160                 :                : {
                                161                 :                :     XLogRecPtr  lsn;
                                162                 :                :     ReorderBufferChange *change;
                                163                 :                :     ReorderBufferTXN *txn;
                                164                 :                :     TXNEntryFile file;
                                165                 :                :     XLogSegNo   segno;
                                166                 :                : } ReorderBufferIterTXNEntry;
                                167                 :                : 
                                168                 :                : typedef struct ReorderBufferIterTXNState
                                169                 :                : {
                                170                 :                :     binaryheap *heap;
                                171                 :                :     Size        nr_txns;
                                172                 :                :     dlist_head  old_change;
                                173                 :                :     ReorderBufferIterTXNEntry entries[FLEXIBLE_ARRAY_MEMBER];
                                174                 :                : } ReorderBufferIterTXNState;
                                175                 :                : 
                                176                 :                : /* toast datastructures */
                                177                 :                : typedef struct ReorderBufferToastEnt
                                178                 :                : {
                                179                 :                :     Oid         chunk_id;       /* toast_table.chunk_id */
                                180                 :                :     int32       last_chunk_seq; /* toast_table.chunk_seq of the last chunk we
                                181                 :                :                                  * have seen */
                                182                 :                :     Size        num_chunks;     /* number of chunks we've already seen */
                                183                 :                :     Size        size;           /* combined size of chunks seen */
                                184                 :                :     dlist_head  chunks;         /* linked list of chunks */
                                185                 :                :     struct varlena *reconstructed;  /* reconstructed varlena now pointed to in
                                186                 :                :                                      * main tup */
                                187                 :                : } ReorderBufferToastEnt;
                                188                 :                : 
                                189                 :                : /* Disk serialization support datastructures */
                                190                 :                : typedef struct ReorderBufferDiskChange
                                191                 :                : {
                                192                 :                :     Size        size;
                                193                 :                :     ReorderBufferChange change;
                                194                 :                :     /* data follows */
                                195                 :                : } ReorderBufferDiskChange;
                                196                 :                : 
                                197                 :                : #define IsSpecInsert(action) \
                                198                 :                : ( \
                                199                 :                :     ((action) == REORDER_BUFFER_CHANGE_INTERNAL_SPEC_INSERT) \
                                200                 :                : )
                                201                 :                : #define IsSpecConfirmOrAbort(action) \
                                202                 :                : ( \
                                203                 :                :     (((action) == REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM) || \
                                204                 :                :     ((action) == REORDER_BUFFER_CHANGE_INTERNAL_SPEC_ABORT)) \
                                205                 :                : )
                                206                 :                : #define IsInsertOrUpdate(action) \
                                207                 :                : ( \
                                208                 :                :     (((action) == REORDER_BUFFER_CHANGE_INSERT) || \
                                209                 :                :     ((action) == REORDER_BUFFER_CHANGE_UPDATE) || \
                                210                 :                :     ((action) == REORDER_BUFFER_CHANGE_INTERNAL_SPEC_INSERT)) \
                                211                 :                : )
                                212                 :                : 
                                213                 :                : /*
                                214                 :                :  * Maximum number of changes kept in memory, per transaction. After that,
                                215                 :                :  * changes are spooled to disk.
                                216                 :                :  *
                                217                 :                :  * The current value should be sufficient to decode the entire transaction
                                218                 :                :  * without hitting disk in OLTP workloads, while starting to spool to disk in
                                219                 :                :  * other workloads reasonably fast.
                                220                 :                :  *
                                221                 :                :  * At some point in the future it probably makes sense to have a more elaborate
                                222                 :                :  * resource management here, but it's not entirely clear what that would look
                                223                 :                :  * like.
                                224                 :                :  */
                                225                 :                : int         logical_decoding_work_mem;
                                226                 :                : static const Size max_changes_in_memory = 4096; /* XXX for restore only */
                                227                 :                : 
                                228                 :                : /* GUC variable */
                                229                 :                : int         debug_logical_replication_streaming = DEBUG_LOGICAL_REP_STREAMING_BUFFERED;
                                230                 :                : 
                                231                 :                : /* ---------------------------------------
                                232                 :                :  * primary reorderbuffer support routines
                                233                 :                :  * ---------------------------------------
                                234                 :                :  */
                                235                 :                : static ReorderBufferTXN *ReorderBufferAllocTXN(ReorderBuffer *rb);
                                236                 :                : static void ReorderBufferFreeTXN(ReorderBuffer *rb, ReorderBufferTXN *txn);
                                237                 :                : static ReorderBufferTXN *ReorderBufferTXNByXid(ReorderBuffer *rb,
                                238                 :                :                                                TransactionId xid, bool create, bool *is_new,
                                239                 :                :                                                XLogRecPtr lsn, bool create_as_top);
                                240                 :                : static void ReorderBufferTransferSnapToParent(ReorderBufferTXN *txn,
                                241                 :                :                                               ReorderBufferTXN *subtxn);
                                242                 :                : 
                                243                 :                : static void AssertTXNLsnOrder(ReorderBuffer *rb);
                                244                 :                : 
                                245                 :                : /* ---------------------------------------
                                246                 :                :  * support functions for lsn-order iterating over the ->changes of a
                                247                 :                :  * transaction and its subtransactions
                                248                 :                :  *
                                249                 :                :  * used for iteration over the k-way heap merge of a transaction and its
                                250                 :                :  * subtransactions
                                251                 :                :  * ---------------------------------------
                                252                 :                :  */
                                253                 :                : static void ReorderBufferIterTXNInit(ReorderBuffer *rb, ReorderBufferTXN *txn,
                                254                 :                :                                      ReorderBufferIterTXNState *volatile *iter_state);
                                255                 :                : static ReorderBufferChange *ReorderBufferIterTXNNext(ReorderBuffer *rb, ReorderBufferIterTXNState *state);
                                256                 :                : static void ReorderBufferIterTXNFinish(ReorderBuffer *rb,
                                257                 :                :                                        ReorderBufferIterTXNState *state);
                                258                 :                : static void ReorderBufferExecuteInvalidations(uint32 nmsgs, SharedInvalidationMessage *msgs);
                                259                 :                : 
                                260                 :                : /*
                                261                 :                :  * ---------------------------------------
                                262                 :                :  * Disk serialization support functions
                                263                 :                :  * ---------------------------------------
                                264                 :                :  */
                                265                 :                : static void ReorderBufferCheckMemoryLimit(ReorderBuffer *rb);
                                266                 :                : static void ReorderBufferSerializeTXN(ReorderBuffer *rb, ReorderBufferTXN *txn);
                                267                 :                : static void ReorderBufferSerializeChange(ReorderBuffer *rb, ReorderBufferTXN *txn,
                                268                 :                :                                          int fd, ReorderBufferChange *change);
                                269                 :                : static Size ReorderBufferRestoreChanges(ReorderBuffer *rb, ReorderBufferTXN *txn,
                                270                 :                :                                         TXNEntryFile *file, XLogSegNo *segno);
                                271                 :                : static void ReorderBufferRestoreChange(ReorderBuffer *rb, ReorderBufferTXN *txn,
                                272                 :                :                                        char *data);
                                273                 :                : static void ReorderBufferRestoreCleanup(ReorderBuffer *rb, ReorderBufferTXN *txn);
                                274                 :                : static void ReorderBufferTruncateTXN(ReorderBuffer *rb, ReorderBufferTXN *txn,
                                275                 :                :                                      bool txn_prepared);
                                276                 :                : static void ReorderBufferMaybeMarkTXNStreamed(ReorderBuffer *rb, ReorderBufferTXN *txn);
                                277                 :                : static bool ReorderBufferCheckAndTruncateAbortedTXN(ReorderBuffer *rb, ReorderBufferTXN *txn);
                                278                 :                : static void ReorderBufferCleanupSerializedTXNs(const char *slotname);
                                279                 :                : static void ReorderBufferSerializedPath(char *path, ReplicationSlot *slot,
                                280                 :                :                                         TransactionId xid, XLogSegNo segno);
                                281                 :                : static int  ReorderBufferTXNSizeCompare(const pairingheap_node *a, const pairingheap_node *b, void *arg);
                                282                 :                : 
                                283                 :                : static void ReorderBufferFreeSnap(ReorderBuffer *rb, Snapshot snap);
                                284                 :                : static Snapshot ReorderBufferCopySnap(ReorderBuffer *rb, Snapshot orig_snap,
                                285                 :                :                                       ReorderBufferTXN *txn, CommandId cid);
                                286                 :                : 
                                287                 :                : /*
                                288                 :                :  * ---------------------------------------
                                289                 :                :  * Streaming support functions
                                290                 :                :  * ---------------------------------------
                                291                 :                :  */
                                292                 :                : static inline bool ReorderBufferCanStream(ReorderBuffer *rb);
                                293                 :                : static inline bool ReorderBufferCanStartStreaming(ReorderBuffer *rb);
                                294                 :                : static void ReorderBufferStreamTXN(ReorderBuffer *rb, ReorderBufferTXN *txn);
                                295                 :                : static void ReorderBufferStreamCommit(ReorderBuffer *rb, ReorderBufferTXN *txn);
                                296                 :                : 
                                297                 :                : /* ---------------------------------------
                                298                 :                :  * toast reassembly support
                                299                 :                :  * ---------------------------------------
                                300                 :                :  */
                                301                 :                : static void ReorderBufferToastInitHash(ReorderBuffer *rb, ReorderBufferTXN *txn);
                                302                 :                : static void ReorderBufferToastReset(ReorderBuffer *rb, ReorderBufferTXN *txn);
                                303                 :                : static void ReorderBufferToastReplace(ReorderBuffer *rb, ReorderBufferTXN *txn,
                                304                 :                :                                       Relation relation, ReorderBufferChange *change);
                                305                 :                : static void ReorderBufferToastAppendChunk(ReorderBuffer *rb, ReorderBufferTXN *txn,
                                306                 :                :                                           Relation relation, ReorderBufferChange *change);
                                307                 :                : 
                                308                 :                : /*
                                309                 :                :  * ---------------------------------------
                                310                 :                :  * memory accounting
                                311                 :                :  * ---------------------------------------
                                312                 :                :  */
                                313                 :                : static Size ReorderBufferChangeSize(ReorderBufferChange *change);
                                314                 :                : static void ReorderBufferChangeMemoryUpdate(ReorderBuffer *rb,
                                315                 :                :                                             ReorderBufferChange *change,
                                316                 :                :                                             ReorderBufferTXN *txn,
                                317                 :                :                                             bool addition, Size sz);
                                318                 :                : 
                                319                 :                : /*
                                320                 :                :  * Allocate a new ReorderBuffer and clean out any old serialized state from
                                321                 :                :  * prior ReorderBuffer instances for the same slot.
                                322                 :                :  */
                                323                 :                : ReorderBuffer *
 4205 rhaas@postgresql.org      324                 :CBC        1051 : ReorderBufferAllocate(void)
                                325                 :                : {
                                326                 :                :     ReorderBuffer *buffer;
                                327                 :                :     HASHCTL     hash_ctl;
                                328                 :                :     MemoryContext new_ctx;
                                329                 :                : 
 2741 alvherre@alvh.no-ip.      330         [ -  + ]:           1051 :     Assert(MyReplicationSlot != NULL);
                                331                 :                : 
                                332                 :                :     /* allocate memory in own context, to have better accountability */
 4205 rhaas@postgresql.org      333                 :           1051 :     new_ctx = AllocSetContextCreate(CurrentMemoryContext,
                                334                 :                :                                     "ReorderBuffer",
                                335                 :                :                                     ALLOCSET_DEFAULT_SIZES);
                                336                 :                : 
                                337                 :                :     buffer =
                                338                 :           1051 :         (ReorderBuffer *) MemoryContextAlloc(new_ctx, sizeof(ReorderBuffer));
                                339                 :                : 
                                340                 :           1051 :     memset(&hash_ctl, 0, sizeof(hash_ctl));
                                341                 :                : 
                                342                 :           1051 :     buffer->context = new_ctx;
                                343                 :                : 
 3113 andres@anarazel.de        344                 :           1051 :     buffer->change_context = SlabContextCreate(new_ctx,
                                345                 :                :                                                "Change",
                                346                 :                :                                                SLAB_DEFAULT_BLOCK_SIZE,
                                347                 :                :                                                sizeof(ReorderBufferChange));
                                348                 :                : 
                                349                 :           1051 :     buffer->txn_context = SlabContextCreate(new_ctx,
                                350                 :                :                                             "TXN",
                                351                 :                :                                             SLAB_DEFAULT_BLOCK_SIZE,
                                352                 :                :                                             sizeof(ReorderBufferTXN));
                                353                 :                : 
                                354                 :                :     /*
                                355                 :                :      * To minimize memory fragmentation caused by long-running transactions
                                356                 :                :      * with changes spanning multiple memory blocks, we use a single
                                357                 :                :      * fixed-size memory block for decoded tuple storage. The performance
                                358                 :                :      * testing showed that the default memory block size maintains logical
                                359                 :                :      * decoding performance without causing fragmentation due to concurrent
                                360                 :                :      * transactions. One might think that we can use the max size as
                                361                 :                :      * SLAB_LARGE_BLOCK_SIZE but the test also showed it doesn't help resolve
                                362                 :                :      * the memory fragmentation.
                                363                 :                :      */
 2844 simon@2ndQuadrant.co      364                 :           1051 :     buffer->tup_context = GenerationContextCreate(new_ctx,
                                365                 :                :                                                   "Tuples",
                                366                 :                :                                                   SLAB_DEFAULT_BLOCK_SIZE,
                                367                 :                :                                                   SLAB_DEFAULT_BLOCK_SIZE,
                                368                 :                :                                                   SLAB_DEFAULT_BLOCK_SIZE);
                                369                 :                : 
 4205 rhaas@postgresql.org      370                 :           1051 :     hash_ctl.keysize = sizeof(TransactionId);
                                371                 :           1051 :     hash_ctl.entrysize = sizeof(ReorderBufferTXNByIdEnt);
                                372                 :           1051 :     hash_ctl.hcxt = buffer->context;
                                373                 :                : 
                                374                 :           1051 :     buffer->by_txn = hash_create("ReorderBufferByXid", 1000, &hash_ctl,
                                375                 :                :                                  HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
                                376                 :                : 
                                377                 :           1051 :     buffer->by_txn_last_xid = InvalidTransactionId;
                                378                 :           1051 :     buffer->by_txn_last_txn = NULL;
                                379                 :                : 
                                380                 :           1051 :     buffer->outbuf = NULL;
                                381                 :           1051 :     buffer->outbufsize = 0;
 2121 akapila@postgresql.o      382                 :           1051 :     buffer->size = 0;
                                383                 :                : 
                                384                 :                :     /* txn_heap is ordered by transaction size */
  513 msawada@postgresql.o      385                 :           1051 :     buffer->txn_heap = pairingheap_allocate(ReorderBufferTXNSizeCompare, NULL);
                                386                 :                : 
 1794 akapila@postgresql.o      387                 :           1051 :     buffer->spillTxns = 0;
                                388                 :           1051 :     buffer->spillCount = 0;
                                389                 :           1051 :     buffer->spillBytes = 0;
 1773                           390                 :           1051 :     buffer->streamTxns = 0;
                                391                 :           1051 :     buffer->streamCount = 0;
                                392                 :           1051 :     buffer->streamBytes = 0;
 1604                           393                 :           1051 :     buffer->totalTxns = 0;
                                394                 :           1051 :     buffer->totalBytes = 0;
                                395                 :                : 
 4205 rhaas@postgresql.org      396                 :           1051 :     buffer->current_restart_decoding_lsn = InvalidXLogRecPtr;
                                397                 :                : 
                                398                 :           1051 :     dlist_init(&buffer->toplevel_by_lsn);
 2629 alvherre@alvh.no-ip.      399                 :           1051 :     dlist_init(&buffer->txns_by_base_snapshot_lsn);
 1039 drowley@postgresql.o      400                 :           1051 :     dclist_init(&buffer->catchange_txns);
                                401                 :                : 
                                402                 :                :     /*
                                403                 :                :      * Ensure there's no stale data from prior uses of this slot, in case some
                                404                 :                :      * prior exit avoided calling ReorderBufferFree. Failure to do this can
                                405                 :                :      * produce duplicated txns, and it's very cheap if there's nothing there.
                                406                 :                :      */
 2741 alvherre@alvh.no-ip.      407                 :           1051 :     ReorderBufferCleanupSerializedTXNs(NameStr(MyReplicationSlot->data.name));
                                408                 :                : 
 4205 rhaas@postgresql.org      409                 :           1051 :     return buffer;
                                410                 :                : }
                                411                 :                : 
                                412                 :                : /*
                                413                 :                :  * Free a ReorderBuffer
                                414                 :                :  */
                                415                 :                : void
                                416                 :            839 : ReorderBufferFree(ReorderBuffer *rb)
                                417                 :                : {
                                418                 :            839 :     MemoryContext context = rb->context;
                                419                 :                : 
                                420                 :                :     /*
                                421                 :                :      * We free separately allocated data by entirely scrapping reorderbuffer's
                                422                 :                :      * memory context.
                                423                 :                :      */
                                424                 :            839 :     MemoryContextDelete(context);
                                425                 :                : 
                                426                 :                :     /* Free disk space used by unconsumed reorder buffers */
 2741 alvherre@alvh.no-ip.      427                 :            839 :     ReorderBufferCleanupSerializedTXNs(NameStr(MyReplicationSlot->data.name));
 4205 rhaas@postgresql.org      428                 :            839 : }
                                429                 :                : 
                                430                 :                : /*
                                431                 :                :  * Allocate a new ReorderBufferTXN.
                                432                 :                :  */
                                433                 :                : static ReorderBufferTXN *
  178 heikki.linnakangas@i      434                 :           3817 : ReorderBufferAllocTXN(ReorderBuffer *rb)
                                435                 :                : {
                                436                 :                :     ReorderBufferTXN *txn;
                                437                 :                : 
                                438                 :                :     txn = (ReorderBufferTXN *)
 3113 andres@anarazel.de        439                 :           3817 :         MemoryContextAlloc(rb->txn_context, sizeof(ReorderBufferTXN));
                                440                 :                : 
 4205 rhaas@postgresql.org      441                 :           3817 :     memset(txn, 0, sizeof(ReorderBufferTXN));
                                442                 :                : 
                                443                 :           3817 :     dlist_init(&txn->changes);
                                444                 :           3817 :     dlist_init(&txn->tuplecids);
                                445                 :           3817 :     dlist_init(&txn->subtxns);
                                446                 :                : 
                                447                 :                :     /* InvalidCommandId is not zero, so set it explicitly */
 1855 akapila@postgresql.o      448                 :           3817 :     txn->command_id = InvalidCommandId;
 1754                           449                 :           3817 :     txn->output_plugin_private = NULL;
                                450                 :                : 
 4205 rhaas@postgresql.org      451                 :           3817 :     return txn;
                                452                 :                : }
                                453                 :                : 
                                454                 :                : /*
                                455                 :                :  * Free a ReorderBufferTXN.
                                456                 :                :  */
                                457                 :                : static void
  178 heikki.linnakangas@i      458                 :           3756 : ReorderBufferFreeTXN(ReorderBuffer *rb, ReorderBufferTXN *txn)
                                459                 :                : {
                                460                 :                :     /* clean the lookup cache if we were cached (quite likely) */
 4205 rhaas@postgresql.org      461         [ +  + ]:           3756 :     if (rb->by_txn_last_xid == txn->xid)
                                462                 :                :     {
                                463                 :           3571 :         rb->by_txn_last_xid = InvalidTransactionId;
                                464                 :           3571 :         rb->by_txn_last_txn = NULL;
                                465                 :                :     }
                                466                 :                : 
                                467                 :                :     /* free data that's contained */
                                468                 :                : 
 1706 akapila@postgresql.o      469         [ +  + ]:           3756 :     if (txn->gid != NULL)
                                470                 :                :     {
                                471                 :             41 :         pfree(txn->gid);
                                472                 :             41 :         txn->gid = NULL;
                                473                 :                :     }
                                474                 :                : 
 4205 rhaas@postgresql.org      475         [ +  + ]:           3756 :     if (txn->tuplecid_hash != NULL)
                                476                 :                :     {
                                477                 :            590 :         hash_destroy(txn->tuplecid_hash);
                                478                 :            590 :         txn->tuplecid_hash = NULL;
                                479                 :                :     }
                                480                 :                : 
                                481         [ +  + ]:           3756 :     if (txn->invalidations)
                                482                 :                :     {
                                483                 :           1165 :         pfree(txn->invalidations);
                                484                 :           1165 :         txn->invalidations = NULL;
                                485                 :                :     }
                                486                 :                : 
   82 msawada@postgresql.o      487         [ +  + ]:           3756 :     if (txn->invalidations_distributed)
                                488                 :                :     {
                                489                 :             22 :         pfree(txn->invalidations_distributed);
                                490                 :             22 :         txn->invalidations_distributed = NULL;
                                491                 :                :     }
                                492                 :                : 
                                493                 :                :     /* Reset the toast hash */
 1544 akapila@postgresql.o      494                 :           3756 :     ReorderBufferToastReset(rb, txn);
                                495                 :                : 
                                496                 :                :     /* All changes must be deallocated */
  376 msawada@postgresql.o      497         [ -  + ]:           3756 :     Assert(txn->size == 0);
                                498                 :                : 
 3113 andres@anarazel.de        499                 :           3756 :     pfree(txn);
 4205 rhaas@postgresql.org      500                 :           3756 : }
                                501                 :                : 
                                502                 :                : /*
                                503                 :                :  * Allocate a ReorderBufferChange.
                                504                 :                :  */
                                505                 :                : ReorderBufferChange *
  178 heikki.linnakangas@i      506                 :        1475389 : ReorderBufferAllocChange(ReorderBuffer *rb)
                                507                 :                : {
                                508                 :                :     ReorderBufferChange *change;
                                509                 :                : 
                                510                 :                :     change = (ReorderBufferChange *)
 3113 andres@anarazel.de        511                 :        1475389 :         MemoryContextAlloc(rb->change_context, sizeof(ReorderBufferChange));
                                512                 :                : 
 4205 rhaas@postgresql.org      513                 :        1475389 :     memset(change, 0, sizeof(ReorderBufferChange));
                                514                 :        1475389 :     return change;
                                515                 :                : }
                                516                 :                : 
                                517                 :                : /*
                                518                 :                :  * Free a ReorderBufferChange and update memory accounting, if requested.
                                519                 :                :  */
                                520                 :                : void
  178 heikki.linnakangas@i      521                 :        1475119 : ReorderBufferFreeChange(ReorderBuffer *rb, ReorderBufferChange *change,
                                522                 :                :                         bool upd_mem)
                                523                 :                : {
                                524                 :                :     /* update memory accounting info */
 1855 akapila@postgresql.o      525         [ +  + ]:        1475119 :     if (upd_mem)
  521 msawada@postgresql.o      526                 :         197279 :         ReorderBufferChangeMemoryUpdate(rb, change, NULL, false,
                                527                 :                :                                         ReorderBufferChangeSize(change));
                                528                 :                : 
                                529                 :                :     /* free contained data */
 4201 tgl@sss.pgh.pa.us         530   [ +  +  +  +  :        1475119 :     switch (change->action)
                                           +  +  - ]
                                531                 :                :     {
                                532                 :        1400617 :         case REORDER_BUFFER_CHANGE_INSERT:
                                533                 :                :         case REORDER_BUFFER_CHANGE_UPDATE:
                                534                 :                :         case REORDER_BUFFER_CHANGE_DELETE:
                                535                 :                :         case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_INSERT:
                                536         [ +  + ]:        1400617 :             if (change->data.tp.newtuple)
                                537                 :                :             {
  178 heikki.linnakangas@i      538                 :        1186062 :                 ReorderBufferFreeTupleBuf(change->data.tp.newtuple);
 4201 tgl@sss.pgh.pa.us         539                 :        1186062 :                 change->data.tp.newtuple = NULL;
                                540                 :                :             }
                                541                 :                : 
                                542         [ +  + ]:        1400617 :             if (change->data.tp.oldtuple)
                                543                 :                :             {
  178 heikki.linnakangas@i      544                 :         146171 :                 ReorderBufferFreeTupleBuf(change->data.tp.oldtuple);
 4201 tgl@sss.pgh.pa.us         545                 :         146171 :                 change->data.tp.oldtuple = NULL;
                                546                 :                :             }
 4205 rhaas@postgresql.org      547                 :        1400617 :             break;
 3440 simon@2ndQuadrant.co      548                 :             40 :         case REORDER_BUFFER_CHANGE_MESSAGE:
                                549         [ +  - ]:             40 :             if (change->data.msg.prefix != NULL)
                                550                 :             40 :                 pfree(change->data.msg.prefix);
                                551                 :             40 :             change->data.msg.prefix = NULL;
                                552         [ +  - ]:             40 :             if (change->data.msg.message != NULL)
                                553                 :             40 :                 pfree(change->data.msg.message);
                                554                 :             40 :             change->data.msg.message = NULL;
                                555                 :             40 :             break;
 1787 akapila@postgresql.o      556                 :           5145 :         case REORDER_BUFFER_CHANGE_INVALIDATION:
                                557         [ +  - ]:           5145 :             if (change->data.inval.invalidations)
                                558                 :           5145 :                 pfree(change->data.inval.invalidations);
                                559                 :           5145 :             change->data.inval.invalidations = NULL;
                                560                 :           5145 :             break;
 4205 rhaas@postgresql.org      561                 :           1191 :         case REORDER_BUFFER_CHANGE_INTERNAL_SNAPSHOT:
 4201 tgl@sss.pgh.pa.us         562         [ +  - ]:           1191 :             if (change->data.snapshot)
                                563                 :                :             {
                                564                 :           1191 :                 ReorderBufferFreeSnap(rb, change->data.snapshot);
                                565                 :           1191 :                 change->data.snapshot = NULL;
                                566                 :                :             }
 4205 rhaas@postgresql.org      567                 :           1191 :             break;
                                568                 :                :             /* no data in addition to the struct itself */
 2560 tomas.vondra@postgre      569                 :             48 :         case REORDER_BUFFER_CHANGE_TRUNCATE:
                                570         [ +  - ]:             48 :             if (change->data.truncate.relids != NULL)
                                571                 :                :             {
  178 heikki.linnakangas@i      572                 :             48 :                 ReorderBufferFreeRelids(rb, change->data.truncate.relids);
 2560 tomas.vondra@postgre      573                 :             48 :                 change->data.truncate.relids = NULL;
                                574                 :                :             }
                                575                 :             48 :             break;
 3774 andres@anarazel.de        576                 :          68078 :         case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM:
                                577                 :                :         case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_ABORT:
                                578                 :                :         case REORDER_BUFFER_CHANGE_INTERNAL_COMMAND_ID:
                                579                 :                :         case REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID:
 4205 rhaas@postgresql.org      580                 :          68078 :             break;
                                581                 :                :     }
                                582                 :                : 
 3113 andres@anarazel.de        583                 :        1475119 :     pfree(change);
 4205 rhaas@postgresql.org      584                 :        1475119 : }
                                585                 :                : 
                                586                 :                : /*
                                587                 :                :  * Allocate a HeapTuple fitting a tuple of size tuple_len (excluding header
                                588                 :                :  * overhead).
                                589                 :                :  */
                                590                 :                : HeapTuple
  178 heikki.linnakangas@i      591                 :        1332277 : ReorderBufferAllocTupleBuf(ReorderBuffer *rb, Size tuple_len)
                                592                 :                : {
                                593                 :                :     HeapTuple   tuple;
                                594                 :                :     Size        alloc_len;
                                595                 :                : 
 3472 andres@anarazel.de        596                 :        1332277 :     alloc_len = tuple_len + SizeofHeapTupleHeader;
                                597                 :                : 
  586 msawada@postgresql.o      598                 :        1332277 :     tuple = (HeapTuple) MemoryContextAlloc(rb->tup_context,
                                599                 :                :                                            HEAPTUPLESIZE + alloc_len);
                                600                 :        1332277 :     tuple->t_data = (HeapTupleHeader) ((char *) tuple + HEAPTUPLESIZE);
                                601                 :                : 
 4205 rhaas@postgresql.org      602                 :        1332277 :     return tuple;
                                603                 :                : }
                                604                 :                : 
                                605                 :                : /*
                                606                 :                :  * Free a HeapTuple returned by ReorderBufferAllocTupleBuf().
                                607                 :                :  */
                                608                 :                : void
  178 heikki.linnakangas@i      609                 :        1332233 : ReorderBufferFreeTupleBuf(HeapTuple tuple)
                                610                 :                : {
 2844 simon@2ndQuadrant.co      611                 :        1332233 :     pfree(tuple);
 4205 rhaas@postgresql.org      612                 :        1332233 : }
                                613                 :                : 
                                614                 :                : /*
                                615                 :                :  * Allocate an array for relids of truncated relations.
                                616                 :                :  *
                                617                 :                :  * We use the global memory context (for the whole reorder buffer), because
                                618                 :                :  * none of the existing ones seems like a good match (some are SLAB, so we
                                619                 :                :  * can't use those, and tup_context is meant for tuple data, not relids). We
                                620                 :                :  * could add yet another context, but it seems like an overkill - TRUNCATE is
                                621                 :                :  * not particularly common operation, so it does not seem worth it.
                                622                 :                :  */
                                623                 :                : Oid *
  178 heikki.linnakangas@i      624                 :             53 : ReorderBufferAllocRelids(ReorderBuffer *rb, int nrelids)
                                625                 :                : {
                                626                 :                :     Oid        *relids;
                                627                 :                :     Size        alloc_len;
                                628                 :                : 
 2560 tomas.vondra@postgre      629                 :             53 :     alloc_len = sizeof(Oid) * nrelids;
                                630                 :                : 
                                631                 :             53 :     relids = (Oid *) MemoryContextAlloc(rb->context, alloc_len);
                                632                 :                : 
                                633                 :             53 :     return relids;
                                634                 :                : }
                                635                 :                : 
                                636                 :                : /*
                                637                 :                :  * Free an array of relids.
                                638                 :                :  */
                                639                 :                : void
  178 heikki.linnakangas@i      640                 :             48 : ReorderBufferFreeRelids(ReorderBuffer *rb, Oid *relids)
                                641                 :                : {
 2560 tomas.vondra@postgre      642                 :             48 :     pfree(relids);
                                643                 :             48 : }
                                644                 :                : 
                                645                 :                : /*
                                646                 :                :  * Return the ReorderBufferTXN from the given buffer, specified by Xid.
                                647                 :                :  * If create is true, and a transaction doesn't already exist, create it
                                648                 :                :  * (with the given LSN, and as top transaction if that's specified);
                                649                 :                :  * when this happens, is_new is set to true.
                                650                 :                :  */
                                651                 :                : static ReorderBufferTXN *
 4205 rhaas@postgresql.org      652                 :        4841199 : ReorderBufferTXNByXid(ReorderBuffer *rb, TransactionId xid, bool create,
                                653                 :                :                       bool *is_new, XLogRecPtr lsn, bool create_as_top)
                                654                 :                : {
                                655                 :                :     ReorderBufferTXN *txn;
                                656                 :                :     ReorderBufferTXNByIdEnt *ent;
                                657                 :                :     bool        found;
                                658                 :                : 
                                659         [ -  + ]:        4841199 :     Assert(TransactionIdIsValid(xid));
                                660                 :                : 
                                661                 :                :     /*
                                662                 :                :      * Check the one-entry lookup cache first
                                663                 :                :      */
                                664         [ +  + ]:        4841199 :     if (TransactionIdIsValid(rb->by_txn_last_xid) &&
                                665         [ +  + ]:        4837588 :         rb->by_txn_last_xid == xid)
                                666                 :                :     {
                                667                 :        4167917 :         txn = rb->by_txn_last_txn;
                                668                 :                : 
                                669         [ +  + ]:        4167917 :         if (txn != NULL)
                                670                 :                :         {
                                671                 :                :             /* found it, and it's valid */
                                672         [ +  + ]:        4167900 :             if (is_new)
                                673                 :           3108 :                 *is_new = false;
                                674                 :        4167900 :             return txn;
                                675                 :                :         }
                                676                 :                : 
                                677                 :                :         /*
                                678                 :                :          * cached as non-existent, and asked not to create? Then nothing else
                                679                 :                :          * to do.
                                680                 :                :          */
                                681         [ +  + ]:             17 :         if (!create)
                                682                 :             14 :             return NULL;
                                683                 :                :         /* otherwise fall through to create it */
                                684                 :                :     }
                                685                 :                : 
                                686                 :                :     /*
                                687                 :                :      * If the cache wasn't hit or it yielded a "does-not-exist" and we want to
                                688                 :                :      * create an entry.
                                689                 :                :      */
                                690                 :                : 
                                691                 :                :     /* search the lookup table */
                                692                 :                :     ent = (ReorderBufferTXNByIdEnt *)
                                693                 :         673285 :         hash_search(rb->by_txn,
                                694                 :                :                     &xid,
                                695                 :                :                     create ? HASH_ENTER : HASH_FIND,
                                696                 :                :                     &found);
                                697         [ +  + ]:         673285 :     if (found)
                                698                 :         668180 :         txn = ent->txn;
                                699         [ +  + ]:           5105 :     else if (create)
                                700                 :                :     {
                                701                 :                :         /* initialize the new entry, if creation was requested */
                                702         [ -  + ]:           3817 :         Assert(ent != NULL);
 2629 alvherre@alvh.no-ip.      703         [ -  + ]:           3817 :         Assert(lsn != InvalidXLogRecPtr);
                                704                 :                : 
  178 heikki.linnakangas@i      705                 :           3817 :         ent->txn = ReorderBufferAllocTXN(rb);
 4205 rhaas@postgresql.org      706                 :           3817 :         ent->txn->xid = xid;
                                707                 :           3817 :         txn = ent->txn;
                                708                 :           3817 :         txn->first_lsn = lsn;
                                709                 :           3817 :         txn->restart_decoding_lsn = rb->current_restart_decoding_lsn;
                                710                 :                : 
                                711         [ +  + ]:           3817 :         if (create_as_top)
                                712                 :                :         {
                                713                 :           3174 :             dlist_push_tail(&rb->toplevel_by_lsn, &txn->node);
                                714                 :           3174 :             AssertTXNLsnOrder(rb);
                                715                 :                :         }
                                716                 :                :     }
                                717                 :                :     else
                                718                 :           1288 :         txn = NULL;             /* not found and not asked to create */
                                719                 :                : 
                                720                 :                :     /* update cache */
                                721                 :         673285 :     rb->by_txn_last_xid = xid;
                                722                 :         673285 :     rb->by_txn_last_txn = txn;
                                723                 :                : 
                                724         [ +  + ]:         673285 :     if (is_new)
                                725                 :           1699 :         *is_new = !found;
                                726                 :                : 
 3450 andres@anarazel.de        727   [ +  +  -  + ]:         673285 :     Assert(!create || txn != NULL);
 4205 rhaas@postgresql.org      728                 :         673285 :     return txn;
                                729                 :                : }
                                730                 :                : 
                                731                 :                : /*
                                732                 :                :  * Record the partial change for the streaming of in-progress transactions.  We
                                733                 :                :  * can stream only complete changes so if we have a partial change like toast
                                734                 :                :  * table insert or speculative insert then we mark such a 'txn' so that it
                                735                 :                :  * can't be streamed.  We also ensure that if the changes in such a 'txn' can
                                736                 :                :  * be streamed and are above logical_decoding_work_mem threshold then we stream
                                737                 :                :  * them as soon as we have a complete change.
                                738                 :                :  */
                                739                 :                : static void
 1855 akapila@postgresql.o      740                 :        1268582 : ReorderBufferProcessPartialChange(ReorderBuffer *rb, ReorderBufferTXN *txn,
                                741                 :                :                                   ReorderBufferChange *change,
                                742                 :                :                                   bool toast_insert)
                                743                 :                : {
                                744                 :                :     ReorderBufferTXN *toptxn;
                                745                 :                : 
                                746                 :                :     /*
                                747                 :                :      * The partial changes need to be processed only while streaming
                                748                 :                :      * in-progress transactions.
                                749                 :                :      */
                                750         [ +  + ]:        1268582 :     if (!ReorderBufferCanStream(rb))
                                751                 :         949472 :         return;
                                752                 :                : 
                                753                 :                :     /* Get the top transaction. */
  904                           754         [ +  + ]:         319110 :     toptxn = rbtxn_get_toptxn(txn);
                                755                 :                : 
                                756                 :                :     /*
                                757                 :                :      * Indicate a partial change for toast inserts.  The change will be
                                758                 :                :      * considered as complete once we get the insert or update on the main
                                759                 :                :      * table and we are sure that the pending toast chunks are not required
                                760                 :                :      * anymore.
                                761                 :                :      *
                                762                 :                :      * If we allow streaming when there are pending toast chunks then such
                                763                 :                :      * chunks won't be released till the insert (multi_insert) is complete and
                                764                 :                :      * we expect the txn to have streamed all changes after streaming.  This
                                765                 :                :      * restriction is mainly to ensure the correctness of streamed
                                766                 :                :      * transactions and it doesn't seem worth uplifting such a restriction
                                767                 :                :      * just to allow this case because anyway we will stream the transaction
                                768                 :                :      * once such an insert is complete.
                                769                 :                :      */
 1855                           770         [ +  + ]:         319110 :     if (toast_insert)
 1563                           771                 :           1666 :         toptxn->txn_flags |= RBTXN_HAS_PARTIAL_CHANGE;
                                772         [ +  + ]:         317444 :     else if (rbtxn_has_partial_change(toptxn) &&
                                773   [ +  +  -  +  :             63 :              IsInsertOrUpdate(change->action) &&
                                              -  - ]
                                774         [ +  + ]:             63 :              change->data.tp.clear_toast_afterwards)
                                775                 :             43 :         toptxn->txn_flags &= ~RBTXN_HAS_PARTIAL_CHANGE;
                                776                 :                : 
                                777                 :                :     /*
                                778                 :                :      * Indicate a partial change for speculative inserts.  The change will be
                                779                 :                :      * considered as complete once we get the speculative confirm or abort
                                780                 :                :      * token.
                                781                 :                :      */
 1855                           782         [ -  + ]:         319110 :     if (IsSpecInsert(change->action))
 1563 akapila@postgresql.o      783                 :UBC           0 :         toptxn->txn_flags |= RBTXN_HAS_PARTIAL_CHANGE;
 1563 akapila@postgresql.o      784         [ +  + ]:CBC      319110 :     else if (rbtxn_has_partial_change(toptxn) &&
 1529                           785   [ +  -  -  + ]:           1686 :              IsSpecConfirmOrAbort(change->action))
 1563 akapila@postgresql.o      786                 :UBC           0 :         toptxn->txn_flags &= ~RBTXN_HAS_PARTIAL_CHANGE;
                                787                 :                : 
                                788                 :                :     /*
                                789                 :                :      * Stream the transaction if it is serialized before and the changes are
                                790                 :                :      * now complete in the top-level transaction.
                                791                 :                :      *
                                792                 :                :      * The reason for doing the streaming of such a transaction as soon as we
                                793                 :                :      * get the complete change for it is that previously it would have reached
                                794                 :                :      * the memory threshold and wouldn't get streamed because of incomplete
                                795                 :                :      * changes.  Delaying such transactions would increase apply lag for them.
                                796                 :                :      */
 1855 akapila@postgresql.o      797         [ +  + ]:CBC      319110 :     if (ReorderBufferCanStartStreaming(rb) &&
 1563                           798         [ +  + ]:         177722 :         !(rbtxn_has_partial_change(toptxn)) &&
 1003                           799         [ +  + ]:         176186 :         rbtxn_is_serialized(txn) &&
                                800         [ +  + ]:             38 :         rbtxn_has_streamable_change(toptxn))
 1855                           801                 :              8 :         ReorderBufferStreamTXN(rb, toptxn);
                                802                 :                : }
                                803                 :                : 
                                804                 :                : /*
                                805                 :                :  * Queue a change into a transaction so it can be replayed upon commit or will be
                                806                 :                :  * streamed when we reach logical_decoding_work_mem threshold.
                                807                 :                :  */
                                808                 :                : void
 4205 rhaas@postgresql.org      809                 :        1277991 : ReorderBufferQueueChange(ReorderBuffer *rb, TransactionId xid, XLogRecPtr lsn,
                                810                 :                :                          ReorderBufferChange *change, bool toast_insert)
                                811                 :                : {
                                812                 :                :     ReorderBufferTXN *txn;
                                813                 :                : 
                                814                 :        1277991 :     txn = ReorderBufferTXNByXid(rb, xid, true, NULL, lsn, true);
                                815                 :                : 
                                816                 :                :     /*
                                817                 :                :      * If we have detected that the transaction is aborted while streaming the
                                818                 :                :      * previous changes or by checking its CLOG, there is no point in
                                819                 :                :      * collecting further changes for it.
                                820                 :                :      */
  206 msawada@postgresql.o      821         [ +  + ]:        1277991 :     if (rbtxn_is_aborted(txn))
                                822                 :                :     {
                                823                 :                :         /*
                                824                 :                :          * We don't need to update memory accounting for this change as we
                                825                 :                :          * have not added it to the queue yet.
                                826                 :                :          */
  178 heikki.linnakangas@i      827                 :           9409 :         ReorderBufferFreeChange(rb, change, false);
 1855 akapila@postgresql.o      828                 :           9409 :         return;
                                829                 :                :     }
                                830                 :                : 
                                831                 :                :     /*
                                832                 :                :      * The changes that are sent downstream are considered streamable.  We
                                833                 :                :      * remember such transactions so that only those will later be considered
                                834                 :                :      * for streaming.
                                835                 :                :      */
 1003                           836         [ +  + ]:        1268582 :     if (change->action == REORDER_BUFFER_CHANGE_INSERT ||
                                837         [ +  + ]:         428710 :         change->action == REORDER_BUFFER_CHANGE_UPDATE ||
                                838         [ +  + ]:         268959 :         change->action == REORDER_BUFFER_CHANGE_DELETE ||
                                839         [ +  + ]:          66365 :         change->action == REORDER_BUFFER_CHANGE_INTERNAL_SPEC_INSERT ||
                                840         [ +  + ]:          48449 :         change->action == REORDER_BUFFER_CHANGE_TRUNCATE ||
                                841         [ +  + ]:          48406 :         change->action == REORDER_BUFFER_CHANGE_MESSAGE)
                                842                 :                :     {
  904                           843         [ +  + ]:        1220215 :         ReorderBufferTXN *toptxn = rbtxn_get_toptxn(txn);
                                844                 :                : 
 1003                           845                 :        1220215 :         toptxn->txn_flags |= RBTXN_HAS_STREAMABLE_CHANGE;
                                846                 :                :     }
                                847                 :                : 
 4205 rhaas@postgresql.org      848                 :        1268582 :     change->lsn = lsn;
 2121 akapila@postgresql.o      849                 :        1268582 :     change->txn = txn;
                                850                 :                : 
 4205 rhaas@postgresql.org      851         [ -  + ]:        1268582 :     Assert(InvalidXLogRecPtr != lsn);
                                852                 :        1268582 :     dlist_push_tail(&txn->changes, &change->node);
                                853                 :        1268582 :     txn->nentries++;
                                854                 :        1268582 :     txn->nentries_mem++;
                                855                 :                : 
                                856                 :                :     /* update memory accounting information */
  521 msawada@postgresql.o      857                 :        1268582 :     ReorderBufferChangeMemoryUpdate(rb, change, NULL, true,
                                858                 :                :                                     ReorderBufferChangeSize(change));
                                859                 :                : 
                                860                 :                :     /* process partial change */
 1855 akapila@postgresql.o      861                 :        1268582 :     ReorderBufferProcessPartialChange(rb, txn, change, toast_insert);
                                862                 :                : 
                                863                 :                :     /* check the memory limits and evict something if needed */
 2121                           864                 :        1268582 :     ReorderBufferCheckMemoryLimit(rb);
                                865                 :                : }
                                866                 :                : 
                                867                 :                : /*
                                868                 :                :  * A transactional message is queued to be processed upon commit and a
                                869                 :                :  * non-transactional message gets processed immediately.
                                870                 :                :  */
                                871                 :                : void
 3440 simon@2ndQuadrant.co      872                 :             47 : ReorderBufferQueueMessage(ReorderBuffer *rb, TransactionId xid,
                                873                 :                :                           Snapshot snap, XLogRecPtr lsn,
                                874                 :                :                           bool transactional, const char *prefix,
                                875                 :                :                           Size message_size, const char *message)
                                876                 :                : {
                                877         [ +  + ]:             47 :     if (transactional)
                                878                 :                :     {
                                879                 :                :         MemoryContext oldcontext;
                                880                 :                :         ReorderBufferChange *change;
                                881                 :                : 
                                882         [ -  + ]:             39 :         Assert(xid != InvalidTransactionId);
                                883                 :                : 
                                884                 :                :         /*
                                885                 :                :          * We don't expect snapshots for transactional changes - we'll use the
                                886                 :                :          * snapshot derived later during apply (unless the change gets
                                887                 :                :          * skipped).
                                888                 :                :          */
  927 tomas.vondra@postgre      889         [ -  + ]:             39 :         Assert(!snap);
                                890                 :                : 
 3440 simon@2ndQuadrant.co      891                 :             39 :         oldcontext = MemoryContextSwitchTo(rb->context);
                                892                 :                : 
  178 heikki.linnakangas@i      893                 :             39 :         change = ReorderBufferAllocChange(rb);
 3440 simon@2ndQuadrant.co      894                 :             39 :         change->action = REORDER_BUFFER_CHANGE_MESSAGE;
                                895                 :             39 :         change->data.msg.prefix = pstrdup(prefix);
                                896                 :             39 :         change->data.msg.message_size = message_size;
                                897                 :             39 :         change->data.msg.message = palloc(message_size);
                                898                 :             39 :         memcpy(change->data.msg.message, message, message_size);
                                899                 :                : 
 1855 akapila@postgresql.o      900                 :             39 :         ReorderBufferQueueChange(rb, xid, lsn, change, false);
                                901                 :                : 
 3440 simon@2ndQuadrant.co      902                 :             39 :         MemoryContextSwitchTo(oldcontext);
                                903                 :                :     }
                                904                 :                :     else
                                905                 :                :     {
 3376 rhaas@postgresql.org      906                 :              8 :         ReorderBufferTXN *txn = NULL;
 1085 pg@bowt.ie                907                 :              8 :         volatile Snapshot snapshot_now = snap;
                                908                 :                : 
                                909                 :                :         /* Non-transactional changes require a valid snapshot. */
  927 tomas.vondra@postgre      910         [ -  + ]:              8 :         Assert(snapshot_now);
                                911                 :                : 
 3440 simon@2ndQuadrant.co      912         [ +  + ]:              8 :         if (xid != InvalidTransactionId)
                                913                 :              3 :             txn = ReorderBufferTXNByXid(rb, xid, true, NULL, lsn, true);
                                914                 :                : 
                                915                 :                :         /* setup snapshot to allow catalog access */
                                916                 :              8 :         SetupHistoricSnapshot(snapshot_now, NULL);
                                917         [ +  - ]:              8 :         PG_TRY();
                                918                 :                :         {
                                919                 :              8 :             rb->message(rb, txn, lsn, false, prefix, message_size, message);
                                920                 :                : 
                                921                 :              8 :             TeardownHistoricSnapshot(false);
                                922                 :                :         }
 3440 simon@2ndQuadrant.co      923                 :UBC           0 :         PG_CATCH();
                                924                 :                :         {
                                925                 :              0 :             TeardownHistoricSnapshot(true);
                                926                 :              0 :             PG_RE_THROW();
                                927                 :                :         }
 3440 simon@2ndQuadrant.co      928         [ -  + ]:CBC           8 :         PG_END_TRY();
                                929                 :                :     }
                                930                 :             47 : }
                                931                 :                : 
                                932                 :                : /*
                                933                 :                :  * AssertTXNLsnOrder
                                934                 :                :  *      Verify LSN ordering of transaction lists in the reorderbuffer
                                935                 :                :  *
                                936                 :                :  * Other LSN-related invariants are checked too.
                                937                 :                :  *
                                938                 :                :  * No-op if assertions are not in use.
                                939                 :                :  */
                                940                 :                : static void
 4205 rhaas@postgresql.org      941                 :           7782 : AssertTXNLsnOrder(ReorderBuffer *rb)
                                942                 :                : {
                                943                 :                : #ifdef USE_ASSERT_CHECKING
 1052 akapila@postgresql.o      944                 :           7782 :     LogicalDecodingContext *ctx = rb->private_data;
                                945                 :                :     dlist_iter  iter;
 4205 rhaas@postgresql.org      946                 :           7782 :     XLogRecPtr  prev_first_lsn = InvalidXLogRecPtr;
 2629 alvherre@alvh.no-ip.      947                 :           7782 :     XLogRecPtr  prev_base_snap_lsn = InvalidXLogRecPtr;
                                948                 :                : 
                                949                 :                :     /*
                                950                 :                :      * Skip the verification if we don't reach the LSN at which we start
                                951                 :                :      * decoding the contents of transactions yet because until we reach the
                                952                 :                :      * LSN, we could have transactions that don't have the association between
                                953                 :                :      * the top-level transaction and subtransaction yet and consequently have
                                954                 :                :      * the same LSN.  We don't guarantee this association until we try to
                                955                 :                :      * decode the actual contents of transaction. The ordering of the records
                                956                 :                :      * prior to the start_decoding_at LSN should have been checked before the
                                957                 :                :      * restart.
                                958                 :                :      */
 1052 akapila@postgresql.o      959         [ +  + ]:           7782 :     if (SnapBuildXactNeedsSkip(ctx->snapshot_builder, ctx->reader->EndRecPtr))
                                960                 :           3779 :         return;
                                961                 :                : 
 4205 rhaas@postgresql.org      962   [ +  -  +  + ]:           7545 :     dlist_foreach(iter, &rb->toplevel_by_lsn)
                                963                 :                :     {
 2629 alvherre@alvh.no-ip.      964                 :           3542 :         ReorderBufferTXN *cur_txn = dlist_container(ReorderBufferTXN, node,
                                965                 :                :                                                     iter.cur);
                                966                 :                : 
                                967                 :                :         /* start LSN must be set */
 4205 rhaas@postgresql.org      968         [ -  + ]:           3542 :         Assert(cur_txn->first_lsn != InvalidXLogRecPtr);
                                969                 :                : 
                                970                 :                :         /* If there is an end LSN, it must be higher than start LSN */
                                971         [ +  + ]:           3542 :         if (cur_txn->end_lsn != InvalidXLogRecPtr)
                                972         [ -  + ]:             22 :             Assert(cur_txn->first_lsn <= cur_txn->end_lsn);
                                973                 :                : 
                                974                 :                :         /* Current initial LSN must be strictly higher than previous */
                                975         [ +  + ]:           3542 :         if (prev_first_lsn != InvalidXLogRecPtr)
                                976         [ -  + ]:            232 :             Assert(prev_first_lsn < cur_txn->first_lsn);
                                977                 :                : 
                                978                 :                :         /* known-as-subtxn txns must not be listed */
 2066 alvherre@alvh.no-ip.      979         [ -  + ]:           3542 :         Assert(!rbtxn_is_known_subxact(cur_txn));
                                980                 :                : 
 4205 rhaas@postgresql.org      981                 :           3542 :         prev_first_lsn = cur_txn->first_lsn;
                                982                 :                :     }
                                983                 :                : 
 2629 alvherre@alvh.no-ip.      984   [ +  -  +  + ]:           5969 :     dlist_foreach(iter, &rb->txns_by_base_snapshot_lsn)
                                985                 :                :     {
                                986                 :           1966 :         ReorderBufferTXN *cur_txn = dlist_container(ReorderBufferTXN,
                                987                 :                :                                                     base_snapshot_node,
                                988                 :                :                                                     iter.cur);
                                989                 :                : 
                                990                 :                :         /* base snapshot (and its LSN) must be set */
                                991         [ -  + ]:           1966 :         Assert(cur_txn->base_snapshot != NULL);
                                992         [ -  + ]:           1966 :         Assert(cur_txn->base_snapshot_lsn != InvalidXLogRecPtr);
                                993                 :                : 
                                994                 :                :         /* current LSN must be strictly higher than previous */
                                995         [ +  + ]:           1966 :         if (prev_base_snap_lsn != InvalidXLogRecPtr)
                                996         [ -  + ]:            176 :             Assert(prev_base_snap_lsn < cur_txn->base_snapshot_lsn);
                                997                 :                : 
                                998                 :                :         /* known-as-subtxn txns must not be listed */
 2066                           999         [ -  + ]:           1966 :         Assert(!rbtxn_is_known_subxact(cur_txn));
                               1000                 :                : 
 2629                          1001                 :           1966 :         prev_base_snap_lsn = cur_txn->base_snapshot_lsn;
                               1002                 :                :     }
                               1003                 :                : #endif
                               1004                 :                : }
                               1005                 :                : 
                               1006                 :                : /*
                               1007                 :                :  * AssertChangeLsnOrder
                               1008                 :                :  *
                               1009                 :                :  * Check ordering of changes in the (sub)transaction.
                               1010                 :                :  */
                               1011                 :                : static void
 1855 akapila@postgresql.o     1012                 :           2529 : AssertChangeLsnOrder(ReorderBufferTXN *txn)
                               1013                 :                : {
                               1014                 :                : #ifdef USE_ASSERT_CHECKING
                               1015                 :                :     dlist_iter  iter;
                               1016                 :           2529 :     XLogRecPtr  prev_lsn = txn->first_lsn;
                               1017                 :                : 
                               1018   [ +  -  +  + ]:         193584 :     dlist_foreach(iter, &txn->changes)
                               1019                 :                :     {
                               1020                 :                :         ReorderBufferChange *cur_change;
                               1021                 :                : 
                               1022                 :         191055 :         cur_change = dlist_container(ReorderBufferChange, node, iter.cur);
                               1023                 :                : 
                               1024         [ -  + ]:         191055 :         Assert(txn->first_lsn != InvalidXLogRecPtr);
                               1025         [ -  + ]:         191055 :         Assert(cur_change->lsn != InvalidXLogRecPtr);
                               1026         [ -  + ]:         191055 :         Assert(txn->first_lsn <= cur_change->lsn);
                               1027                 :                : 
                               1028         [ +  + ]:         191055 :         if (txn->end_lsn != InvalidXLogRecPtr)
                               1029         [ -  + ]:          32970 :             Assert(cur_change->lsn <= txn->end_lsn);
                               1030                 :                : 
                               1031         [ -  + ]:         191055 :         Assert(prev_lsn <= cur_change->lsn);
                               1032                 :                : 
                               1033                 :         191055 :         prev_lsn = cur_change->lsn;
                               1034                 :                :     }
                               1035                 :                : #endif
                               1036                 :           2529 : }
                               1037                 :                : 
                               1038                 :                : /*
                               1039                 :                :  * ReorderBufferGetOldestTXN
                               1040                 :                :  *      Return oldest transaction in reorderbuffer
                               1041                 :                :  */
                               1042                 :                : ReorderBufferTXN *
 4205 rhaas@postgresql.org     1043                 :            399 : ReorderBufferGetOldestTXN(ReorderBuffer *rb)
                               1044                 :                : {
                               1045                 :                :     ReorderBufferTXN *txn;
                               1046                 :                : 
 2629 alvherre@alvh.no-ip.     1047                 :            399 :     AssertTXNLsnOrder(rb);
                               1048                 :                : 
 4205 rhaas@postgresql.org     1049         [ +  + ]:            399 :     if (dlist_is_empty(&rb->toplevel_by_lsn))
                               1050                 :            344 :         return NULL;
                               1051                 :                : 
                               1052                 :             55 :     txn = dlist_head_element(ReorderBufferTXN, node, &rb->toplevel_by_lsn);
                               1053                 :                : 
 2066 alvherre@alvh.no-ip.     1054         [ -  + ]:             55 :     Assert(!rbtxn_is_known_subxact(txn));
 4205 rhaas@postgresql.org     1055         [ -  + ]:             55 :     Assert(txn->first_lsn != InvalidXLogRecPtr);
                               1056                 :             55 :     return txn;
                               1057                 :                : }
                               1058                 :                : 
                               1059                 :                : /*
                               1060                 :                :  * ReorderBufferGetOldestXmin
                               1061                 :                :  *      Return oldest Xmin in reorderbuffer
                               1062                 :                :  *
                               1063                 :                :  * Returns oldest possibly running Xid from the point of view of snapshots
                               1064                 :                :  * used in the transactions kept by reorderbuffer, or InvalidTransactionId if
                               1065                 :                :  * there are none.
                               1066                 :                :  *
                               1067                 :                :  * Since snapshots are assigned monotonically, this equals the Xmin of the
                               1068                 :                :  * base snapshot with minimal base_snapshot_lsn.
                               1069                 :                :  */
                               1070                 :                : TransactionId
 2629 alvherre@alvh.no-ip.     1071                 :            417 : ReorderBufferGetOldestXmin(ReorderBuffer *rb)
                               1072                 :                : {
                               1073                 :                :     ReorderBufferTXN *txn;
                               1074                 :                : 
                               1075                 :            417 :     AssertTXNLsnOrder(rb);
                               1076                 :                : 
                               1077         [ +  + ]:            417 :     if (dlist_is_empty(&rb->txns_by_base_snapshot_lsn))
                               1078                 :            371 :         return InvalidTransactionId;
                               1079                 :                : 
                               1080                 :             46 :     txn = dlist_head_element(ReorderBufferTXN, base_snapshot_node,
                               1081                 :                :                              &rb->txns_by_base_snapshot_lsn);
                               1082                 :             46 :     return txn->base_snapshot->xmin;
                               1083                 :                : }
                               1084                 :                : 
                               1085                 :                : void
 4205 rhaas@postgresql.org     1086                 :            460 : ReorderBufferSetRestartPoint(ReorderBuffer *rb, XLogRecPtr ptr)
                               1087                 :                : {
                               1088                 :            460 :     rb->current_restart_decoding_lsn = ptr;
                               1089                 :            460 : }
                               1090                 :                : 
                               1091                 :                : /*
                               1092                 :                :  * ReorderBufferAssignChild
                               1093                 :                :  *
                               1094                 :                :  * Make note that we know that subxid is a subtransaction of xid, seen as of
                               1095                 :                :  * the given lsn.
                               1096                 :                :  */
                               1097                 :                : void
                               1098                 :            829 : ReorderBufferAssignChild(ReorderBuffer *rb, TransactionId xid,
                               1099                 :                :                          TransactionId subxid, XLogRecPtr lsn)
                               1100                 :                : {
                               1101                 :                :     ReorderBufferTXN *txn;
                               1102                 :                :     ReorderBufferTXN *subtxn;
                               1103                 :                :     bool        new_top;
                               1104                 :                :     bool        new_sub;
                               1105                 :                : 
                               1106                 :            829 :     txn = ReorderBufferTXNByXid(rb, xid, true, &new_top, lsn, true);
                               1107                 :            829 :     subtxn = ReorderBufferTXNByXid(rb, subxid, true, &new_sub, lsn, false);
                               1108                 :                : 
 2629 alvherre@alvh.no-ip.     1109         [ +  + ]:            829 :     if (!new_sub)
                               1110                 :                :     {
 2066                          1111         [ +  - ]:            186 :         if (rbtxn_is_known_subxact(subtxn))
                               1112                 :                :         {
                               1113                 :                :             /* already associated, nothing to do */
 2629                          1114                 :            186 :             return;
                               1115                 :                :         }
                               1116                 :                :         else
                               1117                 :                :         {
                               1118                 :                :             /*
                               1119                 :                :              * We already saw this transaction, but initially added it to the
                               1120                 :                :              * list of top-level txns.  Now that we know it's not top-level,
                               1121                 :                :              * remove it from there.
                               1122                 :                :              */
 2629 alvherre@alvh.no-ip.     1123                 :UBC           0 :             dlist_delete(&subtxn->node);
                               1124                 :                :         }
                               1125                 :                :     }
                               1126                 :                : 
 2066 alvherre@alvh.no-ip.     1127                 :CBC         643 :     subtxn->txn_flags |= RBTXN_IS_SUBXACT;
 2629                          1128                 :            643 :     subtxn->toplevel_xid = xid;
                               1129         [ -  + ]:            643 :     Assert(subtxn->nsubtxns == 0);
                               1130                 :                : 
                               1131                 :                :     /* set the reference to top-level transaction */
 1871 akapila@postgresql.o     1132                 :            643 :     subtxn->toptxn = txn;
                               1133                 :                : 
                               1134                 :                :     /* add to subtransaction list */
 2629 alvherre@alvh.no-ip.     1135                 :            643 :     dlist_push_tail(&txn->subtxns, &subtxn->node);
                               1136                 :            643 :     txn->nsubtxns++;
                               1137                 :                : 
                               1138                 :                :     /* Possibly transfer the subtxn's snapshot to its top-level txn. */
                               1139                 :            643 :     ReorderBufferTransferSnapToParent(txn, subtxn);
                               1140                 :                : 
                               1141                 :                :     /* Verify LSN-ordering invariant */
                               1142                 :            643 :     AssertTXNLsnOrder(rb);
                               1143                 :                : }
                               1144                 :                : 
                               1145                 :                : /*
                               1146                 :                :  * ReorderBufferTransferSnapToParent
                               1147                 :                :  *      Transfer base snapshot from subtxn to top-level txn, if needed
                               1148                 :                :  *
                               1149                 :                :  * This is done if the top-level txn doesn't have a base snapshot, or if the
                               1150                 :                :  * subtxn's base snapshot has an earlier LSN than the top-level txn's base
                               1151                 :                :  * snapshot's LSN.  This can happen if there are no changes in the toplevel
                               1152                 :                :  * txn but there are some in the subtxn, or the first change in subtxn has
                               1153                 :                :  * earlier LSN than first change in the top-level txn and we learned about
                               1154                 :                :  * their kinship only now.
                               1155                 :                :  *
                               1156                 :                :  * The subtransaction's snapshot is cleared regardless of the transfer
                               1157                 :                :  * happening, since it's not needed anymore in either case.
                               1158                 :                :  *
                               1159                 :                :  * We do this as soon as we become aware of their kinship, to avoid queueing
                               1160                 :                :  * extra snapshots to txns known-as-subtxns -- only top-level txns will
                               1161                 :                :  * receive further snapshots.
                               1162                 :                :  */
                               1163                 :                : static void
                               1164                 :            647 : ReorderBufferTransferSnapToParent(ReorderBufferTXN *txn,
                               1165                 :                :                                   ReorderBufferTXN *subtxn)
                               1166                 :                : {
                               1167         [ -  + ]:            647 :     Assert(subtxn->toplevel_xid == txn->xid);
                               1168                 :                : 
                               1169         [ -  + ]:            647 :     if (subtxn->base_snapshot != NULL)
                               1170                 :                :     {
 2629 alvherre@alvh.no-ip.     1171         [ #  # ]:UBC           0 :         if (txn->base_snapshot == NULL ||
                               1172         [ #  # ]:              0 :             subtxn->base_snapshot_lsn < txn->base_snapshot_lsn)
                               1173                 :                :         {
                               1174                 :                :             /*
                               1175                 :                :              * If the toplevel transaction already has a base snapshot but
                               1176                 :                :              * it's newer than the subxact's, purge it.
                               1177                 :                :              */
                               1178         [ #  # ]:              0 :             if (txn->base_snapshot != NULL)
                               1179                 :                :             {
                               1180                 :              0 :                 SnapBuildSnapDecRefcount(txn->base_snapshot);
                               1181                 :              0 :                 dlist_delete(&txn->base_snapshot_node);
                               1182                 :                :             }
                               1183                 :                : 
                               1184                 :                :             /*
                               1185                 :                :              * The snapshot is now the top transaction's; transfer it, and
                               1186                 :                :              * adjust the list position of the top transaction in the list by
                               1187                 :                :              * moving it to where the subtransaction is.
                               1188                 :                :              */
                               1189                 :              0 :             txn->base_snapshot = subtxn->base_snapshot;
                               1190                 :              0 :             txn->base_snapshot_lsn = subtxn->base_snapshot_lsn;
                               1191                 :              0 :             dlist_insert_before(&subtxn->base_snapshot_node,
                               1192                 :                :                                 &txn->base_snapshot_node);
                               1193                 :                : 
                               1194                 :                :             /*
                               1195                 :                :              * The subtransaction doesn't have a snapshot anymore (so it
                               1196                 :                :              * mustn't be in the list.)
                               1197                 :                :              */
                               1198                 :              0 :             subtxn->base_snapshot = NULL;
                               1199                 :              0 :             subtxn->base_snapshot_lsn = InvalidXLogRecPtr;
                               1200                 :              0 :             dlist_delete(&subtxn->base_snapshot_node);
                               1201                 :                :         }
                               1202                 :                :         else
                               1203                 :                :         {
                               1204                 :                :             /* Base snap of toplevel is fine, so subxact's is not needed */
                               1205                 :              0 :             SnapBuildSnapDecRefcount(subtxn->base_snapshot);
                               1206                 :              0 :             dlist_delete(&subtxn->base_snapshot_node);
                               1207                 :              0 :             subtxn->base_snapshot = NULL;
                               1208                 :              0 :             subtxn->base_snapshot_lsn = InvalidXLogRecPtr;
                               1209                 :                :         }
                               1210                 :                :     }
 4205 rhaas@postgresql.org     1211                 :CBC         647 : }
                               1212                 :                : 
                               1213                 :                : /*
                               1214                 :                :  * Associate a subtransaction with its toplevel transaction at commit
                               1215                 :                :  * time. There may be no further changes added after this.
                               1216                 :                :  */
                               1217                 :                : void
                               1218                 :            267 : ReorderBufferCommitChild(ReorderBuffer *rb, TransactionId xid,
                               1219                 :                :                          TransactionId subxid, XLogRecPtr commit_lsn,
                               1220                 :                :                          XLogRecPtr end_lsn)
                               1221                 :                : {
                               1222                 :                :     ReorderBufferTXN *subtxn;
                               1223                 :                : 
                               1224                 :            267 :     subtxn = ReorderBufferTXNByXid(rb, subxid, false, NULL,
                               1225                 :                :                                    InvalidXLogRecPtr, false);
                               1226                 :                : 
                               1227                 :                :     /*
                               1228                 :                :      * No need to do anything if that subtxn didn't contain any changes
                               1229                 :                :      */
                               1230         [ +  + ]:            267 :     if (!subtxn)
                               1231                 :             81 :         return;
                               1232                 :                : 
                               1233                 :            186 :     subtxn->final_lsn = commit_lsn;
                               1234                 :            186 :     subtxn->end_lsn = end_lsn;
                               1235                 :                : 
                               1236                 :                :     /*
                               1237                 :                :      * Assign this subxact as a child of the toplevel xact (no-op if already
                               1238                 :                :      * done.)
                               1239                 :                :      */
 2629 alvherre@alvh.no-ip.     1240                 :            186 :     ReorderBufferAssignChild(rb, xid, subxid, InvalidXLogRecPtr);
                               1241                 :                : }
                               1242                 :                : 
                               1243                 :                : 
                               1244                 :                : /*
                               1245                 :                :  * Support for efficiently iterating over a transaction's and its
                               1246                 :                :  * subtransactions' changes.
                               1247                 :                :  *
                               1248                 :                :  * We do by doing a k-way merge between transactions/subtransactions. For that
                               1249                 :                :  * we model the current heads of the different transactions as a binary heap
                               1250                 :                :  * so we easily know which (sub-)transaction has the change with the smallest
                               1251                 :                :  * lsn next.
                               1252                 :                :  *
                               1253                 :                :  * We assume the changes in individual transactions are already sorted by LSN.
                               1254                 :                :  */
                               1255                 :                : 
                               1256                 :                : /*
                               1257                 :                :  * Binary heap comparison function.
                               1258                 :                :  */
                               1259                 :                : static int
 4205 rhaas@postgresql.org     1260                 :          51568 : ReorderBufferIterCompare(Datum a, Datum b, void *arg)
                               1261                 :                : {
                               1262                 :          51568 :     ReorderBufferIterTXNState *state = (ReorderBufferIterTXNState *) arg;
                               1263                 :          51568 :     XLogRecPtr  pos_a = state->entries[DatumGetInt32(a)].lsn;
                               1264                 :          51568 :     XLogRecPtr  pos_b = state->entries[DatumGetInt32(b)].lsn;
                               1265                 :                : 
                               1266         [ +  + ]:          51568 :     if (pos_a < pos_b)
                               1267                 :          50712 :         return 1;
                               1268         [ -  + ]:            856 :     else if (pos_a == pos_b)
 4205 rhaas@postgresql.org     1269                 :UBC           0 :         return 0;
 4205 rhaas@postgresql.org     1270                 :CBC         856 :     return -1;
                               1271                 :                : }
                               1272                 :                : 
                               1273                 :                : /*
                               1274                 :                :  * Allocate & initialize an iterator which iterates in lsn order over a
                               1275                 :                :  * transaction and all its subtransactions.
                               1276                 :                :  *
                               1277                 :                :  * Note: The iterator state is returned through iter_state parameter rather
                               1278                 :                :  * than the function's return value.  This is because the state gets cleaned up
                               1279                 :                :  * in a PG_CATCH block in the caller, so we want to make sure the caller gets
                               1280                 :                :  * back the state even if this function throws an exception.
                               1281                 :                :  */
                               1282                 :                : static void
 2093 akapila@postgresql.o     1283                 :           2066 : ReorderBufferIterTXNInit(ReorderBuffer *rb, ReorderBufferTXN *txn,
                               1284                 :                :                          ReorderBufferIterTXNState *volatile *iter_state)
                               1285                 :                : {
 4205 rhaas@postgresql.org     1286                 :           2066 :     Size        nr_txns = 0;
                               1287                 :                :     ReorderBufferIterTXNState *state;
                               1288                 :                :     dlist_iter  cur_txn_i;
                               1289                 :                :     int32       off;
                               1290                 :                : 
 2093 akapila@postgresql.o     1291                 :           2066 :     *iter_state = NULL;
                               1292                 :                : 
                               1293                 :                :     /* Check ordering of changes in the toplevel transaction. */
 1855                          1294                 :           2066 :     AssertChangeLsnOrder(txn);
                               1295                 :                : 
                               1296                 :                :     /*
                               1297                 :                :      * Calculate the size of our heap: one element for every transaction that
                               1298                 :                :      * contains changes.  (Besides the transactions already in the reorder
                               1299                 :                :      * buffer, we count the one we were directly passed.)
                               1300                 :                :      */
 4205 rhaas@postgresql.org     1301         [ +  + ]:           2066 :     if (txn->nentries > 0)
                               1302                 :           1884 :         nr_txns++;
                               1303                 :                : 
                               1304   [ +  -  +  + ]:           2529 :     dlist_foreach(cur_txn_i, &txn->subtxns)
                               1305                 :                :     {
                               1306                 :                :         ReorderBufferTXN *cur_txn;
                               1307                 :                : 
                               1308                 :            463 :         cur_txn = dlist_container(ReorderBufferTXN, node, cur_txn_i.cur);
                               1309                 :                : 
                               1310                 :                :         /* Check ordering of changes in this subtransaction. */
 1855 akapila@postgresql.o     1311                 :            463 :         AssertChangeLsnOrder(cur_txn);
                               1312                 :                : 
 4205 rhaas@postgresql.org     1313         [ +  + ]:            463 :         if (cur_txn->nentries > 0)
                               1314                 :            301 :             nr_txns++;
                               1315                 :                :     }
                               1316                 :                : 
                               1317                 :                :     /* allocate iteration state */
                               1318                 :                :     state = (ReorderBufferIterTXNState *)
                               1319                 :           2066 :         MemoryContextAllocZero(rb->context,
                               1320                 :                :                                sizeof(ReorderBufferIterTXNState) +
                               1321                 :           2066 :                                sizeof(ReorderBufferIterTXNEntry) * nr_txns);
                               1322                 :                : 
                               1323                 :           2066 :     state->nr_txns = nr_txns;
                               1324                 :           2066 :     dlist_init(&state->old_change);
                               1325                 :                : 
                               1326         [ +  + ]:           4251 :     for (off = 0; off < state->nr_txns; off++)
                               1327                 :                :     {
 2093 akapila@postgresql.o     1328                 :           2185 :         state->entries[off].file.vfd = -1;
 4205 rhaas@postgresql.org     1329                 :           2185 :         state->entries[off].segno = 0;
                               1330                 :                :     }
                               1331                 :                : 
                               1332                 :                :     /* allocate heap */
                               1333                 :           2066 :     state->heap = binaryheap_allocate(state->nr_txns,
                               1334                 :                :                                       ReorderBufferIterCompare,
                               1335                 :                :                                       state);
                               1336                 :                : 
                               1337                 :                :     /* Now that the state fields are initialized, it is safe to return it. */
 2093 akapila@postgresql.o     1338                 :           2066 :     *iter_state = state;
                               1339                 :                : 
                               1340                 :                :     /*
                               1341                 :                :      * Now insert items into the binary heap, in an unordered fashion.  (We
                               1342                 :                :      * will run a heap assembly step at the end; this is more efficient.)
                               1343                 :                :      */
                               1344                 :                : 
 4205 rhaas@postgresql.org     1345                 :           2066 :     off = 0;
                               1346                 :                : 
                               1347                 :                :     /* add toplevel transaction if it contains changes */
                               1348         [ +  + ]:           2066 :     if (txn->nentries > 0)
                               1349                 :                :     {
                               1350                 :                :         ReorderBufferChange *cur_change;
                               1351                 :                : 
 2066 alvherre@alvh.no-ip.     1352         [ +  + ]:           1884 :         if (rbtxn_is_serialized(txn))
                               1353                 :                :         {
                               1354                 :                :             /* serialize remaining changes */
 3260 andres@anarazel.de       1355                 :             22 :             ReorderBufferSerializeTXN(rb, txn);
 2093 akapila@postgresql.o     1356                 :             22 :             ReorderBufferRestoreChanges(rb, txn, &state->entries[off].file,
                               1357                 :                :                                         &state->entries[off].segno);
                               1358                 :                :         }
                               1359                 :                : 
 4205 rhaas@postgresql.org     1360                 :           1884 :         cur_change = dlist_head_element(ReorderBufferChange, node,
                               1361                 :                :                                         &txn->changes);
                               1362                 :                : 
                               1363                 :           1884 :         state->entries[off].lsn = cur_change->lsn;
                               1364                 :           1884 :         state->entries[off].change = cur_change;
                               1365                 :           1884 :         state->entries[off].txn = txn;
                               1366                 :                : 
                               1367                 :           1884 :         binaryheap_add_unordered(state->heap, Int32GetDatum(off++));
                               1368                 :                :     }
                               1369                 :                : 
                               1370                 :                :     /* add subtransactions if they contain changes */
                               1371   [ +  -  +  + ]:           2529 :     dlist_foreach(cur_txn_i, &txn->subtxns)
                               1372                 :                :     {
                               1373                 :                :         ReorderBufferTXN *cur_txn;
                               1374                 :                : 
                               1375                 :            463 :         cur_txn = dlist_container(ReorderBufferTXN, node, cur_txn_i.cur);
                               1376                 :                : 
                               1377         [ +  + ]:            463 :         if (cur_txn->nentries > 0)
                               1378                 :                :         {
                               1379                 :                :             ReorderBufferChange *cur_change;
                               1380                 :                : 
 2066 alvherre@alvh.no-ip.     1381         [ +  + ]:            301 :             if (rbtxn_is_serialized(cur_txn))
                               1382                 :                :             {
                               1383                 :                :                 /* serialize remaining changes */
 3260 andres@anarazel.de       1384                 :             17 :                 ReorderBufferSerializeTXN(rb, cur_txn);
 4205 rhaas@postgresql.org     1385                 :             17 :                 ReorderBufferRestoreChanges(rb, cur_txn,
                               1386                 :                :                                             &state->entries[off].file,
                               1387                 :                :                                             &state->entries[off].segno);
                               1388                 :                :             }
                               1389                 :            301 :             cur_change = dlist_head_element(ReorderBufferChange, node,
                               1390                 :                :                                             &cur_txn->changes);
                               1391                 :                : 
                               1392                 :            301 :             state->entries[off].lsn = cur_change->lsn;
                               1393                 :            301 :             state->entries[off].change = cur_change;
                               1394                 :            301 :             state->entries[off].txn = cur_txn;
                               1395                 :                : 
                               1396                 :            301 :             binaryheap_add_unordered(state->heap, Int32GetDatum(off++));
                               1397                 :                :         }
                               1398                 :                :     }
                               1399                 :                : 
                               1400                 :                :     /* assemble a valid binary heap */
                               1401                 :           2066 :     binaryheap_build(state->heap);
                               1402                 :           2066 : }
                               1403                 :                : 
                               1404                 :                : /*
                               1405                 :                :  * Return the next change when iterating over a transaction and its
                               1406                 :                :  * subtransactions.
                               1407                 :                :  *
                               1408                 :                :  * Returns NULL when no further changes exist.
                               1409                 :                :  */
                               1410                 :                : static ReorderBufferChange *
                               1411                 :         361401 : ReorderBufferIterTXNNext(ReorderBuffer *rb, ReorderBufferIterTXNState *state)
                               1412                 :                : {
                               1413                 :                :     ReorderBufferChange *change;
                               1414                 :                :     ReorderBufferIterTXNEntry *entry;
                               1415                 :                :     int32       off;
                               1416                 :                : 
                               1417                 :                :     /* nothing there anymore */
   67 nathan@postgresql.or     1418         [ +  + ]:GNC      361401 :     if (binaryheap_empty(state->heap))
 4205 rhaas@postgresql.org     1419                 :CBC        2055 :         return NULL;
                               1420                 :                : 
                               1421                 :         359346 :     off = DatumGetInt32(binaryheap_first(state->heap));
                               1422                 :         359346 :     entry = &state->entries[off];
                               1423                 :                : 
                               1424                 :                :     /* free memory we might have "leaked" in the previous *Next call */
                               1425         [ +  + ]:         359346 :     if (!dlist_is_empty(&state->old_change))
                               1426                 :                :     {
                               1427                 :             44 :         change = dlist_container(ReorderBufferChange, node,
                               1428                 :                :                                  dlist_pop_head_node(&state->old_change));
  178 heikki.linnakangas@i     1429                 :             44 :         ReorderBufferFreeChange(rb, change, true);
 4205 rhaas@postgresql.org     1430         [ -  + ]:             44 :         Assert(dlist_is_empty(&state->old_change));
                               1431                 :                :     }
                               1432                 :                : 
                               1433                 :         359346 :     change = entry->change;
                               1434                 :                : 
                               1435                 :                :     /*
                               1436                 :                :      * update heap with information about which transaction has the next
                               1437                 :                :      * relevant change in LSN order
                               1438                 :                :      */
                               1439                 :                : 
                               1440                 :                :     /* there are in-memory changes */
                               1441         [ +  + ]:         359346 :     if (dlist_has_next(&entry->txn->changes, &entry->change->node))
                               1442                 :                :     {
                               1443                 :         357129 :         dlist_node *next = dlist_next_node(&entry->txn->changes, &change->node);
                               1444                 :         357129 :         ReorderBufferChange *next_change =
  841 tgl@sss.pgh.pa.us        1445                 :         357129 :             dlist_container(ReorderBufferChange, node, next);
                               1446                 :                : 
                               1447                 :                :         /* txn stays the same */
 4205 rhaas@postgresql.org     1448                 :         357129 :         state->entries[off].lsn = next_change->lsn;
                               1449                 :         357129 :         state->entries[off].change = next_change;
                               1450                 :                : 
                               1451                 :         357129 :         binaryheap_replace_first(state->heap, Int32GetDatum(off));
                               1452                 :         357129 :         return change;
                               1453                 :                :     }
                               1454                 :                : 
                               1455                 :                :     /* try to load changes from disk */
                               1456         [ +  + ]:           2217 :     if (entry->txn->nentries != entry->txn->nentries_mem)
                               1457                 :                :     {
                               1458                 :                :         /*
                               1459                 :                :          * Ugly: restoring changes will reuse *Change records, thus delete the
                               1460                 :                :          * current one from the per-tx list and only free in the next call.
                               1461                 :                :          */
                               1462                 :             63 :         dlist_delete(&change->node);
                               1463                 :             63 :         dlist_push_tail(&state->old_change, &change->node);
                               1464                 :                : 
                               1465                 :                :         /*
                               1466                 :                :          * Update the total bytes processed by the txn for which we are
                               1467                 :                :          * releasing the current set of changes and restoring the new set of
                               1468                 :                :          * changes.
                               1469                 :                :          */
 1587 akapila@postgresql.o     1470                 :             63 :         rb->totalBytes += entry->txn->size;
 2093                          1471         [ +  + ]:             63 :         if (ReorderBufferRestoreChanges(rb, entry->txn, &entry->file,
                               1472                 :                :                                         &state->entries[off].segno))
                               1473                 :                :         {
                               1474                 :                :             /* successfully restored changes from disk */
                               1475                 :                :             ReorderBufferChange *next_change =
  841 tgl@sss.pgh.pa.us        1476                 :             35 :                 dlist_head_element(ReorderBufferChange, node,
                               1477                 :                :                                    &entry->txn->changes);
                               1478                 :                : 
 4205 rhaas@postgresql.org     1479         [ -  + ]:             35 :             elog(DEBUG2, "restored %u/%u changes from disk",
                               1480                 :                :                  (uint32) entry->txn->nentries_mem,
                               1481                 :                :                  (uint32) entry->txn->nentries);
                               1482                 :                : 
                               1483         [ -  + ]:             35 :             Assert(entry->txn->nentries_mem);
                               1484                 :                :             /* txn stays the same */
                               1485                 :             35 :             state->entries[off].lsn = next_change->lsn;
                               1486                 :             35 :             state->entries[off].change = next_change;
                               1487                 :             35 :             binaryheap_replace_first(state->heap, Int32GetDatum(off));
                               1488                 :                : 
                               1489                 :             35 :             return change;
                               1490                 :                :         }
                               1491                 :                :     }
                               1492                 :                : 
                               1493                 :                :     /* ok, no changes there anymore, remove */
                               1494                 :           2182 :     binaryheap_remove_first(state->heap);
                               1495                 :                : 
                               1496                 :           2182 :     return change;
                               1497                 :                : }
                               1498                 :                : 
                               1499                 :                : /*
                               1500                 :                :  * Deallocate the iterator
                               1501                 :                :  */
                               1502                 :                : static void
                               1503                 :           2064 : ReorderBufferIterTXNFinish(ReorderBuffer *rb,
                               1504                 :                :                            ReorderBufferIterTXNState *state)
                               1505                 :                : {
                               1506                 :                :     int32       off;
                               1507                 :                : 
                               1508         [ +  + ]:           4247 :     for (off = 0; off < state->nr_txns; off++)
                               1509                 :                :     {
 2093 akapila@postgresql.o     1510         [ -  + ]:           2183 :         if (state->entries[off].file.vfd != -1)
 2093 akapila@postgresql.o     1511                 :UBC           0 :             FileClose(state->entries[off].file.vfd);
                               1512                 :                :     }
                               1513                 :                : 
                               1514                 :                :     /* free memory we might have "leaked" in the last *Next call */
 4205 rhaas@postgresql.org     1515         [ +  + ]:CBC        2064 :     if (!dlist_is_empty(&state->old_change))
                               1516                 :                :     {
                               1517                 :                :         ReorderBufferChange *change;
                               1518                 :                : 
                               1519                 :             18 :         change = dlist_container(ReorderBufferChange, node,
                               1520                 :                :                                  dlist_pop_head_node(&state->old_change));
  178 heikki.linnakangas@i     1521                 :             18 :         ReorderBufferFreeChange(rb, change, true);
 4205 rhaas@postgresql.org     1522         [ -  + ]:             18 :         Assert(dlist_is_empty(&state->old_change));
                               1523                 :                :     }
                               1524                 :                : 
                               1525                 :           2064 :     binaryheap_free(state->heap);
                               1526                 :           2064 :     pfree(state);
                               1527                 :           2064 : }
                               1528                 :                : 
                               1529                 :                : /*
                               1530                 :                :  * Cleanup the contents of a transaction, usually after the transaction
                               1531                 :                :  * committed or aborted.
                               1532                 :                :  */
                               1533                 :                : static void
                               1534                 :           3756 : ReorderBufferCleanupTXN(ReorderBuffer *rb, ReorderBufferTXN *txn)
                               1535                 :                : {
                               1536                 :                :     bool        found;
                               1537                 :                :     dlist_mutable_iter iter;
  376 msawada@postgresql.o     1538                 :           3756 :     Size        mem_freed = 0;
                               1539                 :                : 
                               1540                 :                :     /* cleanup subtransactions & their changes */
 4205 rhaas@postgresql.org     1541   [ +  -  +  + ]:           3941 :     dlist_foreach_modify(iter, &txn->subtxns)
                               1542                 :                :     {
                               1543                 :                :         ReorderBufferTXN *subtxn;
                               1544                 :                : 
                               1545                 :            185 :         subtxn = dlist_container(ReorderBufferTXN, node, iter.cur);
                               1546                 :                : 
                               1547                 :                :         /*
                               1548                 :                :          * Subtransactions are always associated to the toplevel TXN, even if
                               1549                 :                :          * they originally were happening inside another subtxn, so we won't
                               1550                 :                :          * ever recurse more than one level deep here.
                               1551                 :                :          */
 2066 alvherre@alvh.no-ip.     1552         [ -  + ]:            185 :         Assert(rbtxn_is_known_subxact(subtxn));
 4205 rhaas@postgresql.org     1553         [ -  + ]:            185 :         Assert(subtxn->nsubtxns == 0);
                               1554                 :                : 
                               1555                 :            185 :         ReorderBufferCleanupTXN(rb, subtxn);
                               1556                 :                :     }
                               1557                 :                : 
                               1558                 :                :     /* cleanup changes in the txn */
                               1559   [ +  -  +  + ]:          71202 :     dlist_foreach_modify(iter, &txn->changes)
                               1560                 :                :     {
                               1561                 :                :         ReorderBufferChange *change;
                               1562                 :                : 
                               1563                 :          67446 :         change = dlist_container(ReorderBufferChange, node, iter.cur);
                               1564                 :                : 
                               1565                 :                :         /* Check we're not mixing changes from different transactions. */
 2121 akapila@postgresql.o     1566         [ -  + ]:          67446 :         Assert(change->txn == txn);
                               1567                 :                : 
                               1568                 :                :         /*
                               1569                 :                :          * Instead of updating the memory counter for individual changes, we
                               1570                 :                :          * sum up the size of memory to free so we can update the memory
                               1571                 :                :          * counter all together below. This saves costs of maintaining the
                               1572                 :                :          * max-heap.
                               1573                 :                :          */
  376 msawada@postgresql.o     1574                 :          67446 :         mem_freed += ReorderBufferChangeSize(change);
                               1575                 :                : 
  178 heikki.linnakangas@i     1576                 :          67446 :         ReorderBufferFreeChange(rb, change, false);
                               1577                 :                :     }
                               1578                 :                : 
                               1579                 :                :     /* Update the memory counter */
  376 msawada@postgresql.o     1580                 :           3756 :     ReorderBufferChangeMemoryUpdate(rb, NULL, txn, false, mem_freed);
                               1581                 :                : 
                               1582                 :                :     /*
                               1583                 :                :      * Cleanup the tuplecids we stored for decoding catalog snapshot access.
                               1584                 :                :      * They are always stored in the toplevel transaction.
                               1585                 :                :      */
 4205 rhaas@postgresql.org     1586   [ +  -  +  + ]:          27723 :     dlist_foreach_modify(iter, &txn->tuplecids)
                               1587                 :                :     {
                               1588                 :                :         ReorderBufferChange *change;
                               1589                 :                : 
                               1590                 :          23967 :         change = dlist_container(ReorderBufferChange, node, iter.cur);
                               1591                 :                : 
                               1592                 :                :         /* Check we're not mixing changes from different transactions. */
 2121 akapila@postgresql.o     1593         [ -  + ]:          23967 :         Assert(change->txn == txn);
 4201 tgl@sss.pgh.pa.us        1594         [ -  + ]:          23967 :         Assert(change->action == REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID);
                               1595                 :                : 
  178 heikki.linnakangas@i     1596                 :          23967 :         ReorderBufferFreeChange(rb, change, true);
                               1597                 :                :     }
                               1598                 :                : 
                               1599                 :                :     /*
                               1600                 :                :      * Cleanup the base snapshot, if set.
                               1601                 :                :      */
 4205 rhaas@postgresql.org     1602         [ +  + ]:           3756 :     if (txn->base_snapshot != NULL)
                               1603                 :                :     {
                               1604                 :           3097 :         SnapBuildSnapDecRefcount(txn->base_snapshot);
 2629 alvherre@alvh.no-ip.     1605                 :           3097 :         dlist_delete(&txn->base_snapshot_node);
                               1606                 :                :     }
                               1607                 :                : 
                               1608                 :                :     /*
                               1609                 :                :      * Cleanup the snapshot for the last streamed run.
                               1610                 :                :      */
 1855 akapila@postgresql.o     1611         [ +  + ]:           3756 :     if (txn->snapshot_now != NULL)
                               1612                 :                :     {
                               1613         [ -  + ]:             66 :         Assert(rbtxn_is_streamed(txn));
                               1614                 :             66 :         ReorderBufferFreeSnap(rb, txn->snapshot_now);
                               1615                 :                :     }
                               1616                 :                : 
                               1617                 :                :     /*
                               1618                 :                :      * Remove TXN from its containing lists.
                               1619                 :                :      *
                               1620                 :                :      * Note: if txn is known as subxact, we are deleting the TXN from its
                               1621                 :                :      * parent's list of known subxacts; this leaves the parent's nsubxacts
                               1622                 :                :      * count too high, but we don't care.  Otherwise, we are deleting the TXN
                               1623                 :                :      * from the LSN-ordered list of toplevel TXNs. We remove the TXN from the
                               1624                 :                :      * list of catalog modifying transactions as well.
                               1625                 :                :      */
 3289 tgl@sss.pgh.pa.us        1626                 :           3756 :     dlist_delete(&txn->node);
 1122 akapila@postgresql.o     1627         [ +  + ]:           3756 :     if (rbtxn_has_catalog_changes(txn))
 1039 drowley@postgresql.o     1628                 :           1219 :         dclist_delete_from(&rb->catchange_txns, &txn->catchange_node);
                               1629                 :                : 
                               1630                 :                :     /* now remove reference from buffer */
  841 tgl@sss.pgh.pa.us        1631                 :           3756 :     hash_search(rb->by_txn, &txn->xid, HASH_REMOVE, &found);
 4205 rhaas@postgresql.org     1632         [ -  + ]:           3756 :     Assert(found);
                               1633                 :                : 
                               1634                 :                :     /* remove entries spilled to disk */
 2066 alvherre@alvh.no-ip.     1635         [ +  + ]:           3756 :     if (rbtxn_is_serialized(txn))
 4205 rhaas@postgresql.org     1636                 :            224 :         ReorderBufferRestoreCleanup(rb, txn);
                               1637                 :                : 
                               1638                 :                :     /* deallocate */
  178 heikki.linnakangas@i     1639                 :           3756 :     ReorderBufferFreeTXN(rb, txn);
 4205 rhaas@postgresql.org     1640                 :           3756 : }
                               1641                 :                : 
                               1642                 :                : /*
                               1643                 :                :  * Discard changes from a transaction (and subtransactions), either after
                               1644                 :                :  * streaming, decoding them at PREPARE, or detecting the transaction abort.
                               1645                 :                :  * Keep the remaining info - transactions, tuplecids, invalidations and
                               1646                 :                :  * snapshots.
                               1647                 :                :  *
                               1648                 :                :  * We additionally remove tuplecids after decoding the transaction at prepare
                               1649                 :                :  * time as we only need to perform invalidation at rollback or commit prepared.
                               1650                 :                :  *
                               1651                 :                :  * 'txn_prepared' indicates that we have decoded the transaction at prepare
                               1652                 :                :  * time.
                               1653                 :                :  */
                               1654                 :                : static void
 1706 akapila@postgresql.o     1655                 :           1073 : ReorderBufferTruncateTXN(ReorderBuffer *rb, ReorderBufferTXN *txn, bool txn_prepared)
                               1656                 :                : {
                               1657                 :                :     dlist_mutable_iter iter;
  376 msawada@postgresql.o     1658                 :           1073 :     Size        mem_freed = 0;
                               1659                 :                : 
                               1660                 :                :     /* cleanup subtransactions & their changes */
 1855 akapila@postgresql.o     1661   [ +  -  +  + ]:           1370 :     dlist_foreach_modify(iter, &txn->subtxns)
                               1662                 :                :     {
                               1663                 :                :         ReorderBufferTXN *subtxn;
                               1664                 :                : 
                               1665                 :            297 :         subtxn = dlist_container(ReorderBufferTXN, node, iter.cur);
                               1666                 :                : 
                               1667                 :                :         /*
                               1668                 :                :          * Subtransactions are always associated to the toplevel TXN, even if
                               1669                 :                :          * they originally were happening inside another subtxn, so we won't
                               1670                 :                :          * ever recurse more than one level deep here.
                               1671                 :                :          */
                               1672         [ -  + ]:            297 :         Assert(rbtxn_is_known_subxact(subtxn));
                               1673         [ -  + ]:            297 :         Assert(subtxn->nsubtxns == 0);
                               1674                 :                : 
  206 msawada@postgresql.o     1675                 :            297 :         ReorderBufferMaybeMarkTXNStreamed(rb, subtxn);
 1706 akapila@postgresql.o     1676                 :            297 :         ReorderBufferTruncateTXN(rb, subtxn, txn_prepared);
                               1677                 :                :     }
                               1678                 :                : 
                               1679                 :                :     /* cleanup changes in the txn */
 1855                          1680   [ +  -  +  + ]:         164004 :     dlist_foreach_modify(iter, &txn->changes)
                               1681                 :                :     {
                               1682                 :                :         ReorderBufferChange *change;
                               1683                 :                : 
                               1684                 :         162931 :         change = dlist_container(ReorderBufferChange, node, iter.cur);
                               1685                 :                : 
                               1686                 :                :         /* Check we're not mixing changes from different transactions. */
                               1687         [ -  + ]:         162931 :         Assert(change->txn == txn);
                               1688                 :                : 
                               1689                 :                :         /* remove the change from its containing list */
                               1690                 :         162931 :         dlist_delete(&change->node);
                               1691                 :                : 
                               1692                 :                :         /*
                               1693                 :                :          * Instead of updating the memory counter for individual changes, we
                               1694                 :                :          * sum up the size of memory to free so we can update the memory
                               1695                 :                :          * counter all together below. This saves costs of maintaining the
                               1696                 :                :          * max-heap.
                               1697                 :                :          */
  376 msawada@postgresql.o     1698                 :         162931 :         mem_freed += ReorderBufferChangeSize(change);
                               1699                 :                : 
  178 heikki.linnakangas@i     1700                 :         162931 :         ReorderBufferFreeChange(rb, change, false);
                               1701                 :                :     }
                               1702                 :                : 
                               1703                 :                :     /* Update the memory counter */
  376 msawada@postgresql.o     1704                 :           1073 :     ReorderBufferChangeMemoryUpdate(rb, NULL, txn, false, mem_freed);
                               1705                 :                : 
 1706 akapila@postgresql.o     1706         [ +  + ]:           1073 :     if (txn_prepared)
                               1707                 :                :     {
                               1708                 :                :         /*
                               1709                 :                :          * If this is a prepared txn, cleanup the tuplecids we stored for
                               1710                 :                :          * decoding catalog snapshot access. They are always stored in the
                               1711                 :                :          * toplevel transaction.
                               1712                 :                :          */
                               1713   [ +  -  +  + ]:            182 :         dlist_foreach_modify(iter, &txn->tuplecids)
                               1714                 :                :         {
                               1715                 :                :             ReorderBufferChange *change;
                               1716                 :                : 
                               1717                 :            123 :             change = dlist_container(ReorderBufferChange, node, iter.cur);
                               1718                 :                : 
                               1719                 :                :             /* Check we're not mixing changes from different transactions. */
                               1720         [ -  + ]:            123 :             Assert(change->txn == txn);
                               1721         [ -  + ]:            123 :             Assert(change->action == REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID);
                               1722                 :                : 
                               1723                 :                :             /* Remove the change from its containing list. */
                               1724                 :            123 :             dlist_delete(&change->node);
                               1725                 :                : 
  178 heikki.linnakangas@i     1726                 :            123 :             ReorderBufferFreeChange(rb, change, true);
                               1727                 :                :         }
                               1728                 :                :     }
                               1729                 :                : 
                               1730                 :                :     /*
                               1731                 :                :      * Destroy the (relfilelocator, ctid) hashtable, so that we don't leak any
                               1732                 :                :      * memory. We could also keep the hash table and update it with new ctid
                               1733                 :                :      * values, but this seems simpler and good enough for now.
                               1734                 :                :      */
 1855 akapila@postgresql.o     1735         [ +  + ]:           1073 :     if (txn->tuplecid_hash != NULL)
                               1736                 :                :     {
                               1737                 :             51 :         hash_destroy(txn->tuplecid_hash);
                               1738                 :             51 :         txn->tuplecid_hash = NULL;
                               1739                 :                :     }
                               1740                 :                : 
                               1741                 :                :     /* If this txn is serialized then clean the disk space. */
                               1742         [ +  + ]:           1073 :     if (rbtxn_is_serialized(txn))
                               1743                 :                :     {
                               1744                 :              8 :         ReorderBufferRestoreCleanup(rb, txn);
                               1745                 :              8 :         txn->txn_flags &= ~RBTXN_IS_SERIALIZED;
                               1746                 :                : 
                               1747                 :                :         /*
                               1748                 :                :          * We set this flag to indicate if the transaction is ever serialized.
                               1749                 :                :          * We need this to accurately update the stats as otherwise the same
                               1750                 :                :          * transaction can be counted as serialized multiple times.
                               1751                 :                :          */
 1794                          1752                 :              8 :         txn->txn_flags |= RBTXN_IS_SERIALIZED_CLEAR;
                               1753                 :                :     }
                               1754                 :                : 
                               1755                 :                :     /* also reset the number of entries in the transaction */
 1855                          1756                 :           1073 :     txn->nentries_mem = 0;
                               1757                 :           1073 :     txn->nentries = 0;
                               1758                 :           1073 : }
                               1759                 :                : 
                               1760                 :                : /*
                               1761                 :                :  * Check the transaction status by CLOG lookup and discard all changes if
                               1762                 :                :  * the transaction is aborted. The transaction status is cached in
                               1763                 :                :  * txn->txn_flags so we can skip future changes and avoid CLOG lookups on the
                               1764                 :                :  * next call.
                               1765                 :                :  *
                               1766                 :                :  * Return true if the transaction is aborted, otherwise return false.
                               1767                 :                :  *
                               1768                 :                :  * When the 'debug_logical_replication_streaming' is set to "immediate", we
                               1769                 :                :  * don't check the transaction status, meaning the caller will always process
                               1770                 :                :  * this transaction.
                               1771                 :                :  */
                               1772                 :                : static bool
  206 msawada@postgresql.o     1773                 :           3732 : ReorderBufferCheckAndTruncateAbortedTXN(ReorderBuffer *rb, ReorderBufferTXN *txn)
                               1774                 :                : {
                               1775                 :                :     /* Quick return for regression tests */
                               1776         [ +  + ]:           3732 :     if (unlikely(debug_logical_replication_streaming == DEBUG_LOGICAL_REP_STREAMING_IMMEDIATE))
                               1777                 :            962 :         return false;
                               1778                 :                : 
                               1779                 :                :     /*
                               1780                 :                :      * Quick return if the transaction status is already known.
                               1781                 :                :      */
                               1782                 :                : 
                               1783         [ +  + ]:           2770 :     if (rbtxn_is_committed(txn))
                               1784                 :           2326 :         return false;
                               1785         [ -  + ]:            444 :     if (rbtxn_is_aborted(txn))
                               1786                 :                :     {
                               1787                 :                :         /* Already-aborted transactions should not have any changes */
  206 msawada@postgresql.o     1788         [ #  # ]:UBC           0 :         Assert(txn->size == 0);
                               1789                 :                : 
                               1790                 :              0 :         return true;
                               1791                 :                :     }
                               1792                 :                : 
                               1793                 :                :     /* Otherwise, check the transaction status using CLOG lookup */
                               1794                 :                : 
  206 msawada@postgresql.o     1795         [ +  + ]:CBC         444 :     if (TransactionIdIsInProgress(txn->xid))
                               1796                 :            254 :         return false;
                               1797                 :                : 
                               1798         [ +  + ]:            190 :     if (TransactionIdDidCommit(txn->xid))
                               1799                 :                :     {
                               1800                 :                :         /*
                               1801                 :                :          * Remember the transaction is committed so that we can skip CLOG
                               1802                 :                :          * check next time, avoiding the pressure on CLOG lookup.
                               1803                 :                :          */
                               1804         [ -  + ]:            181 :         Assert(!rbtxn_is_aborted(txn));
                               1805                 :            181 :         txn->txn_flags |= RBTXN_IS_COMMITTED;
                               1806                 :            181 :         return false;
                               1807                 :                :     }
                               1808                 :                : 
                               1809                 :                :     /*
                               1810                 :                :      * The transaction aborted. We discard both the changes collected so far
                               1811                 :                :      * and the toast reconstruction data. The full cleanup will happen as part
                               1812                 :                :      * of decoding ABORT record of this transaction.
                               1813                 :                :      */
                               1814                 :              9 :     ReorderBufferTruncateTXN(rb, txn, rbtxn_is_prepared(txn));
                               1815                 :              9 :     ReorderBufferToastReset(rb, txn);
                               1816                 :                : 
                               1817                 :                :     /* All changes should be discarded */
                               1818         [ -  + ]:              9 :     Assert(txn->size == 0);
                               1819                 :                : 
                               1820                 :                :     /*
                               1821                 :                :      * Mark the transaction as aborted so we can ignore future changes of this
                               1822                 :                :      * transaction.
                               1823                 :                :      */
                               1824         [ -  + ]:              9 :     Assert(!rbtxn_is_committed(txn));
                               1825                 :              9 :     txn->txn_flags |= RBTXN_IS_ABORTED;
                               1826                 :                : 
                               1827                 :              9 :     return true;
                               1828                 :                : }
                               1829                 :                : 
                               1830                 :                : /*
                               1831                 :                :  * Build a hash with a (relfilelocator, ctid) -> (cmin, cmax) mapping for use by
                               1832                 :                :  * HeapTupleSatisfiesHistoricMVCC.
                               1833                 :                :  */
                               1834                 :                : static void
 4205 rhaas@postgresql.org     1835                 :           2066 : ReorderBufferBuildTupleCidHash(ReorderBuffer *rb, ReorderBufferTXN *txn)
                               1836                 :                : {
                               1837                 :                :     dlist_iter  iter;
                               1838                 :                :     HASHCTL     hash_ctl;
                               1839                 :                : 
 2066 alvherre@alvh.no-ip.     1840   [ +  +  +  + ]:           2066 :     if (!rbtxn_has_catalog_changes(txn) || dlist_is_empty(&txn->tuplecids))
 4205 rhaas@postgresql.org     1841                 :           1425 :         return;
                               1842                 :                : 
                               1843                 :            641 :     hash_ctl.keysize = sizeof(ReorderBufferTupleCidKey);
                               1844                 :            641 :     hash_ctl.entrysize = sizeof(ReorderBufferTupleCidEnt);
                               1845                 :            641 :     hash_ctl.hcxt = rb->context;
                               1846                 :                : 
                               1847                 :                :     /*
                               1848                 :                :      * create the hash with the exact number of to-be-stored tuplecids from
                               1849                 :                :      * the start
                               1850                 :                :      */
                               1851                 :            641 :     txn->tuplecid_hash =
                               1852                 :            641 :         hash_create("ReorderBufferTupleCid", txn->ntuplecids, &hash_ctl,
                               1853                 :                :                     HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
                               1854                 :                : 
                               1855   [ +  -  +  + ]:          12603 :     dlist_foreach(iter, &txn->tuplecids)
                               1856                 :                :     {
                               1857                 :                :         ReorderBufferTupleCidKey key;
                               1858                 :                :         ReorderBufferTupleCidEnt *ent;
                               1859                 :                :         bool        found;
                               1860                 :                :         ReorderBufferChange *change;
                               1861                 :                : 
                               1862                 :          11962 :         change = dlist_container(ReorderBufferChange, node, iter.cur);
                               1863                 :                : 
 4201 tgl@sss.pgh.pa.us        1864         [ -  + ]:          11962 :         Assert(change->action == REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID);
                               1865                 :                : 
                               1866                 :                :         /* be careful about padding */
 4205 rhaas@postgresql.org     1867                 :          11962 :         memset(&key, 0, sizeof(ReorderBufferTupleCidKey));
                               1868                 :                : 
 1158                          1869                 :          11962 :         key.rlocator = change->data.tuplecid.locator;
                               1870                 :                : 
 4201 tgl@sss.pgh.pa.us        1871                 :          11962 :         ItemPointerCopy(&change->data.tuplecid.tid,
                               1872                 :                :                         &key.tid);
                               1873                 :                : 
                               1874                 :                :         ent = (ReorderBufferTupleCidEnt *)
  943 peter@eisentraut.org     1875                 :          11962 :             hash_search(txn->tuplecid_hash, &key, HASH_ENTER, &found);
 4205 rhaas@postgresql.org     1876         [ +  + ]:          11962 :         if (!found)
                               1877                 :                :         {
 4201 tgl@sss.pgh.pa.us        1878                 :          10324 :             ent->cmin = change->data.tuplecid.cmin;
                               1879                 :          10324 :             ent->cmax = change->data.tuplecid.cmax;
                               1880                 :          10324 :             ent->combocid = change->data.tuplecid.combocid;
                               1881                 :                :         }
                               1882                 :                :         else
                               1883                 :                :         {
                               1884                 :                :             /*
                               1885                 :                :              * Maybe we already saw this tuple before in this transaction, but
                               1886                 :                :              * if so it must have the same cmin.
                               1887                 :                :              */
                               1888         [ -  + ]:           1638 :             Assert(ent->cmin == change->data.tuplecid.cmin);
                               1889                 :                : 
                               1890                 :                :             /*
                               1891                 :                :              * cmax may be initially invalid, but once set it can only grow,
                               1892                 :                :              * and never become invalid again.
                               1893                 :                :              */
 2398 alvherre@alvh.no-ip.     1894   [ +  +  +  -  :           1638 :             Assert((ent->cmax == InvalidCommandId) ||
                                              -  + ]
                               1895                 :                :                    ((change->data.tuplecid.cmax != InvalidCommandId) &&
                               1896                 :                :                     (change->data.tuplecid.cmax > ent->cmax)));
 4201 tgl@sss.pgh.pa.us        1897                 :           1638 :             ent->cmax = change->data.tuplecid.cmax;
                               1898                 :                :         }
                               1899                 :                :     }
                               1900                 :                : }
                               1901                 :                : 
                               1902                 :                : /*
                               1903                 :                :  * Copy a provided snapshot so we can modify it privately. This is needed so
                               1904                 :                :  * that catalog modifying transactions can look into intermediate catalog
                               1905                 :                :  * states.
                               1906                 :                :  */
                               1907                 :                : static Snapshot
 4205 rhaas@postgresql.org     1908                 :           1914 : ReorderBufferCopySnap(ReorderBuffer *rb, Snapshot orig_snap,
                               1909                 :                :                       ReorderBufferTXN *txn, CommandId cid)
                               1910                 :                : {
                               1911                 :                :     Snapshot    snap;
                               1912                 :                :     dlist_iter  iter;
                               1913                 :           1914 :     int         i = 0;
                               1914                 :                :     Size        size;
                               1915                 :                : 
                               1916                 :           1914 :     size = sizeof(SnapshotData) +
                               1917                 :           1914 :         sizeof(TransactionId) * orig_snap->xcnt +
                               1918                 :           1914 :         sizeof(TransactionId) * (txn->nsubtxns + 1);
                               1919                 :                : 
                               1920                 :           1914 :     snap = MemoryContextAllocZero(rb->context, size);
                               1921                 :           1914 :     memcpy(snap, orig_snap, sizeof(SnapshotData));
                               1922                 :                : 
                               1923                 :           1914 :     snap->copied = true;
 3796 heikki.linnakangas@i     1924                 :           1914 :     snap->active_count = 1;      /* mark as active so nobody frees it */
                               1925                 :           1914 :     snap->regd_count = 0;
 4205 rhaas@postgresql.org     1926                 :           1914 :     snap->xip = (TransactionId *) (snap + 1);
                               1927                 :                : 
                               1928                 :           1914 :     memcpy(snap->xip, orig_snap->xip, sizeof(TransactionId) * snap->xcnt);
                               1929                 :                : 
                               1930                 :                :     /*
                               1931                 :                :      * snap->subxip contains all txids that belong to our transaction which we
                               1932                 :                :      * need to check via cmin/cmax. That's why we store the toplevel
                               1933                 :                :      * transaction in there as well.
                               1934                 :                :      */
                               1935                 :           1914 :     snap->subxip = snap->xip + snap->xcnt;
                               1936                 :           1914 :     snap->subxip[i++] = txn->xid;
                               1937                 :                : 
                               1938                 :                :     /*
                               1939                 :                :      * txn->nsubtxns isn't decreased when subtransactions abort, so count
                               1940                 :                :      * manually. Since it's an upper boundary it is safe to use it for the
                               1941                 :                :      * allocation above.
                               1942                 :                :      */
                               1943                 :           1914 :     snap->subxcnt = 1;
                               1944                 :                : 
                               1945   [ +  -  +  + ]:           2223 :     dlist_foreach(iter, &txn->subtxns)
                               1946                 :                :     {
                               1947                 :                :         ReorderBufferTXN *sub_txn;
                               1948                 :                : 
                               1949                 :            309 :         sub_txn = dlist_container(ReorderBufferTXN, node, iter.cur);
                               1950                 :            309 :         snap->subxip[i++] = sub_txn->xid;
                               1951                 :            309 :         snap->subxcnt++;
                               1952                 :                :     }
                               1953                 :                : 
                               1954                 :                :     /* sort so we can bsearch() later */
                               1955                 :           1914 :     qsort(snap->subxip, snap->subxcnt, sizeof(TransactionId), xidComparator);
                               1956                 :                : 
                               1957                 :                :     /* store the specified current CommandId */
                               1958                 :           1914 :     snap->curcid = cid;
                               1959                 :                : 
                               1960                 :           1914 :     return snap;
                               1961                 :                : }
                               1962                 :                : 
                               1963                 :                : /*
                               1964                 :                :  * Free a previously ReorderBufferCopySnap'ed snapshot
                               1965                 :                :  */
                               1966                 :                : static void
                               1967                 :           3099 : ReorderBufferFreeSnap(ReorderBuffer *rb, Snapshot snap)
                               1968                 :                : {
                               1969         [ +  + ]:           3099 :     if (snap->copied)
                               1970                 :           1910 :         pfree(snap);
                               1971                 :                :     else
                               1972                 :           1189 :         SnapBuildSnapDecRefcount(snap);
                               1973                 :           3099 : }
                               1974                 :                : 
                               1975                 :                : /*
                               1976                 :                :  * If the transaction was (partially) streamed, we need to prepare or commit
                               1977                 :                :  * it in a 'streamed' way.  That is, we first stream the remaining part of the
                               1978                 :                :  * transaction, and then invoke stream_prepare or stream_commit message as per
                               1979                 :                :  * the case.
                               1980                 :                :  */
                               1981                 :                : static void
 1855 akapila@postgresql.o     1982                 :             66 : ReorderBufferStreamCommit(ReorderBuffer *rb, ReorderBufferTXN *txn)
                               1983                 :                : {
                               1984                 :                :     /* we should only call this for previously streamed transactions */
                               1985         [ -  + ]:             66 :     Assert(rbtxn_is_streamed(txn));
                               1986                 :                : 
                               1987                 :             66 :     ReorderBufferStreamTXN(rb, txn);
                               1988                 :                : 
  206 msawada@postgresql.o     1989         [ +  + ]:             66 :     if (rbtxn_is_prepared(txn))
                               1990                 :                :     {
                               1991                 :                :         /*
                               1992                 :                :          * Note, we send stream prepare even if a concurrent abort is
                               1993                 :                :          * detected. See DecodePrepare for more information.
                               1994                 :                :          */
                               1995         [ -  + ]:             15 :         Assert(!rbtxn_sent_prepare(txn));
 1706 akapila@postgresql.o     1996                 :             15 :         rb->stream_prepare(rb, txn, txn->final_lsn);
  206 msawada@postgresql.o     1997                 :             15 :         txn->txn_flags |= RBTXN_SENT_PREPARE;
                               1998                 :                : 
                               1999                 :                :         /*
                               2000                 :                :          * This is a PREPARED transaction, part of a two-phase commit. The
                               2001                 :                :          * full cleanup will happen as part of the COMMIT PREPAREDs, so now
                               2002                 :                :          * just truncate txn by removing changes and tuplecids.
                               2003                 :                :          */
 1706 akapila@postgresql.o     2004                 :             15 :         ReorderBufferTruncateTXN(rb, txn, true);
                               2005                 :                :         /* Reset the CheckXidAlive */
                               2006                 :             15 :         CheckXidAlive = InvalidTransactionId;
                               2007                 :                :     }
                               2008                 :                :     else
                               2009                 :                :     {
                               2010                 :             51 :         rb->stream_commit(rb, txn, txn->final_lsn);
                               2011                 :             51 :         ReorderBufferCleanupTXN(rb, txn);
                               2012                 :                :     }
 1855                          2013                 :             66 : }
                               2014                 :                : 
                               2015                 :                : /*
                               2016                 :                :  * Set xid to detect concurrent aborts.
                               2017                 :                :  *
                               2018                 :                :  * While streaming an in-progress transaction or decoding a prepared
                               2019                 :                :  * transaction there is a possibility that the (sub)transaction might get
                               2020                 :                :  * aborted concurrently.  In such case if the (sub)transaction has catalog
                               2021                 :                :  * update then we might decode the tuple using wrong catalog version.  For
                               2022                 :                :  * example, suppose there is one catalog tuple with (xmin: 500, xmax: 0).  Now,
                               2023                 :                :  * the transaction 501 updates the catalog tuple and after that we will have
                               2024                 :                :  * two tuples (xmin: 500, xmax: 501) and (xmin: 501, xmax: 0).  Now, if 501 is
                               2025                 :                :  * aborted and some other transaction say 502 updates the same catalog tuple
                               2026                 :                :  * then the first tuple will be changed to (xmin: 500, xmax: 502).  So, the
                               2027                 :                :  * problem is that when we try to decode the tuple inserted/updated in 501
                               2028                 :                :  * after the catalog update, we will see the catalog tuple with (xmin: 500,
                               2029                 :                :  * xmax: 502) as visible because it will consider that the tuple is deleted by
                               2030                 :                :  * xid 502 which is not visible to our snapshot.  And when we will try to
                               2031                 :                :  * decode with that catalog tuple, it can lead to a wrong result or a crash.
                               2032                 :                :  * So, it is necessary to detect concurrent aborts to allow streaming of
                               2033                 :                :  * in-progress transactions or decoding of prepared transactions.
                               2034                 :                :  *
                               2035                 :                :  * For detecting the concurrent abort we set CheckXidAlive to the current
                               2036                 :                :  * (sub)transaction's xid for which this change belongs to.  And, during
                               2037                 :                :  * catalog scan we can check the status of the xid and if it is aborted we will
                               2038                 :                :  * report a specific error so that we can stop streaming current transaction
                               2039                 :                :  * and discard the already streamed changes on such an error.  We might have
                               2040                 :                :  * already streamed some of the changes for the aborted (sub)transaction, but
                               2041                 :                :  * that is fine because when we decode the abort we will stream abort message
                               2042                 :                :  * to truncate the changes in the subscriber. Similarly, for prepared
                               2043                 :                :  * transactions, we stop decoding if concurrent abort is detected and then
                               2044                 :                :  * rollback the changes when rollback prepared is encountered. See
                               2045                 :                :  * DecodePrepare.
                               2046                 :                :  */
                               2047                 :                : static inline void
                               2048                 :         177870 : SetupCheckXidLive(TransactionId xid)
                               2049                 :                : {
                               2050                 :                :     /*
                               2051                 :                :      * If the input transaction id is already set as a CheckXidAlive then
                               2052                 :                :      * nothing to do.
                               2053                 :                :      */
                               2054         [ +  + ]:         177870 :     if (TransactionIdEquals(CheckXidAlive, xid))
 4205 rhaas@postgresql.org     2055                 :         101456 :         return;
                               2056                 :                : 
                               2057                 :                :     /*
                               2058                 :                :      * setup CheckXidAlive if it's not committed yet.  We don't check if the
                               2059                 :                :      * xid is aborted.  That will happen during catalog access.
                               2060                 :                :      */
 1855 akapila@postgresql.o     2061         [ +  + ]:          76414 :     if (!TransactionIdDidCommit(xid))
                               2062                 :            409 :         CheckXidAlive = xid;
                               2063                 :                :     else
                               2064                 :          76005 :         CheckXidAlive = InvalidTransactionId;
                               2065                 :                : }
                               2066                 :                : 
                               2067                 :                : /*
                               2068                 :                :  * Helper function for ReorderBufferProcessTXN for applying change.
                               2069                 :                :  */
                               2070                 :                : static inline void
                               2071                 :         337003 : ReorderBufferApplyChange(ReorderBuffer *rb, ReorderBufferTXN *txn,
                               2072                 :                :                          Relation relation, ReorderBufferChange *change,
                               2073                 :                :                          bool streaming)
                               2074                 :                : {
                               2075         [ +  + ]:         337003 :     if (streaming)
                               2076                 :         176006 :         rb->stream_change(rb, txn, relation, change);
                               2077                 :                :     else
                               2078                 :         160997 :         rb->apply_change(rb, txn, relation, change);
                               2079                 :         337000 : }
                               2080                 :                : 
                               2081                 :                : /*
                               2082                 :                :  * Helper function for ReorderBufferProcessTXN for applying the truncate.
                               2083                 :                :  */
                               2084                 :                : static inline void
                               2085                 :             22 : ReorderBufferApplyTruncate(ReorderBuffer *rb, ReorderBufferTXN *txn,
                               2086                 :                :                            int nrelations, Relation *relations,
                               2087                 :                :                            ReorderBufferChange *change, bool streaming)
                               2088                 :                : {
                               2089         [ -  + ]:             22 :     if (streaming)
 1855 akapila@postgresql.o     2090                 :UBC           0 :         rb->stream_truncate(rb, txn, nrelations, relations, change);
                               2091                 :                :     else
 1855 akapila@postgresql.o     2092                 :CBC          22 :         rb->apply_truncate(rb, txn, nrelations, relations, change);
                               2093                 :             22 : }
                               2094                 :                : 
                               2095                 :                : /*
                               2096                 :                :  * Helper function for ReorderBufferProcessTXN for applying the message.
                               2097                 :                :  */
                               2098                 :                : static inline void
                               2099                 :             11 : ReorderBufferApplyMessage(ReorderBuffer *rb, ReorderBufferTXN *txn,
                               2100                 :                :                           ReorderBufferChange *change, bool streaming)
                               2101                 :                : {
                               2102         [ +  + ]:             11 :     if (streaming)
                               2103                 :              3 :         rb->stream_message(rb, txn, change->lsn, true,
                               2104                 :              3 :                            change->data.msg.prefix,
                               2105                 :                :                            change->data.msg.message_size,
                               2106                 :              3 :                            change->data.msg.message);
                               2107                 :                :     else
                               2108                 :              8 :         rb->message(rb, txn, change->lsn, true,
                               2109                 :              8 :                     change->data.msg.prefix,
                               2110                 :                :                     change->data.msg.message_size,
                               2111                 :              8 :                     change->data.msg.message);
                               2112                 :             11 : }
                               2113                 :                : 
                               2114                 :                : /*
                               2115                 :                :  * Function to store the command id and snapshot at the end of the current
                               2116                 :                :  * stream so that we can reuse the same while sending the next stream.
                               2117                 :                :  */
                               2118                 :                : static inline void
                               2119                 :            725 : ReorderBufferSaveTXNSnapshot(ReorderBuffer *rb, ReorderBufferTXN *txn,
                               2120                 :                :                              Snapshot snapshot_now, CommandId command_id)
                               2121                 :                : {
                               2122                 :            725 :     txn->command_id = command_id;
                               2123                 :                : 
                               2124                 :                :     /* Avoid copying if it's already copied. */
                               2125         [ +  - ]:            725 :     if (snapshot_now->copied)
                               2126                 :            725 :         txn->snapshot_now = snapshot_now;
                               2127                 :                :     else
 1855 akapila@postgresql.o     2128                 :UBC           0 :         txn->snapshot_now = ReorderBufferCopySnap(rb, snapshot_now,
                               2129                 :                :                                                   txn, command_id);
 1855 akapila@postgresql.o     2130                 :CBC         725 : }
                               2131                 :                : 
                               2132                 :                : /*
                               2133                 :                :  * Mark the given transaction as streamed if it's a top-level transaction
                               2134                 :                :  * or has changes.
                               2135                 :                :  */
                               2136                 :                : static void
  206 msawada@postgresql.o     2137                 :           1022 : ReorderBufferMaybeMarkTXNStreamed(ReorderBuffer *rb, ReorderBufferTXN *txn)
                               2138                 :                : {
                               2139                 :                :     /*
                               2140                 :                :      * The top-level transaction, is marked as streamed always, even if it
                               2141                 :                :      * does not contain any changes (that is, when all the changes are in
                               2142                 :                :      * subtransactions).
                               2143                 :                :      *
                               2144                 :                :      * For subtransactions, we only mark them as streamed when there are
                               2145                 :                :      * changes in them.
                               2146                 :                :      *
                               2147                 :                :      * We do it this way because of aborts - we don't want to send aborts for
                               2148                 :                :      * XIDs the downstream is not aware of. And of course, it always knows
                               2149                 :                :      * about the top-level xact (we send the XID in all messages), but we
                               2150                 :                :      * never stream XIDs of empty subxacts.
                               2151                 :                :      */
                               2152   [ +  +  +  + ]:           1022 :     if (rbtxn_is_toptxn(txn) || (txn->nentries_mem != 0))
                               2153                 :            860 :         txn->txn_flags |= RBTXN_IS_STREAMED;
                               2154                 :           1022 : }
                               2155                 :                : 
                               2156                 :                : /*
                               2157                 :                :  * Helper function for ReorderBufferProcessTXN to handle the concurrent
                               2158                 :                :  * abort of the streaming transaction.  This resets the TXN such that it
                               2159                 :                :  * can be used to stream the remaining data of transaction being processed.
                               2160                 :                :  * This can happen when the subtransaction is aborted and we still want to
                               2161                 :                :  * continue processing the main or other subtransactions data.
                               2162                 :                :  */
                               2163                 :                : static void
 1855 akapila@postgresql.o     2164                 :              8 : ReorderBufferResetTXN(ReorderBuffer *rb, ReorderBufferTXN *txn,
                               2165                 :                :                       Snapshot snapshot_now,
                               2166                 :                :                       CommandId command_id,
                               2167                 :                :                       XLogRecPtr last_lsn,
                               2168                 :                :                       ReorderBufferChange *specinsert)
                               2169                 :                : {
                               2170                 :                :     /* Discard the changes that we just streamed */
  206 msawada@postgresql.o     2171                 :              8 :     ReorderBufferTruncateTXN(rb, txn, rbtxn_is_prepared(txn));
                               2172                 :                : 
                               2173                 :                :     /* Free all resources allocated for toast reconstruction */
 1855 akapila@postgresql.o     2174                 :              8 :     ReorderBufferToastReset(rb, txn);
                               2175                 :                : 
                               2176                 :                :     /* Return the spec insert change if it is not NULL */
                               2177         [ -  + ]:              8 :     if (specinsert != NULL)
                               2178                 :                :     {
  178 heikki.linnakangas@i     2179                 :UBC           0 :         ReorderBufferFreeChange(rb, specinsert, true);
 1855 akapila@postgresql.o     2180                 :              0 :         specinsert = NULL;
                               2181                 :                :     }
                               2182                 :                : 
                               2183                 :                :     /*
                               2184                 :                :      * For the streaming case, stop the stream and remember the command ID and
                               2185                 :                :      * snapshot for the streaming run.
                               2186                 :                :      */
 1706 akapila@postgresql.o     2187         [ +  - ]:CBC           8 :     if (rbtxn_is_streamed(txn))
                               2188                 :                :     {
                               2189                 :              8 :         rb->stream_stop(rb, txn, last_lsn);
                               2190                 :              8 :         ReorderBufferSaveTXNSnapshot(rb, txn, snapshot_now, command_id);
                               2191                 :                :     }
                               2192                 :                : 
                               2193                 :                :     /* All changes must be deallocated */
  376 msawada@postgresql.o     2194         [ -  + ]:              8 :     Assert(txn->size == 0);
 1855 akapila@postgresql.o     2195                 :              8 : }
                               2196                 :                : 
                               2197                 :                : /*
                               2198                 :                :  * Helper function for ReorderBufferReplay and ReorderBufferStreamTXN.
                               2199                 :                :  *
                               2200                 :                :  * Send data of a transaction (and its subtransactions) to the
                               2201                 :                :  * output plugin. We iterate over the top and subtransactions (using a k-way
                               2202                 :                :  * merge) and replay the changes in lsn order.
                               2203                 :                :  *
                               2204                 :                :  * If streaming is true then data will be sent using stream API.
                               2205                 :                :  *
                               2206                 :                :  * Note: "volatile" markers on some parameters are to avoid trouble with
                               2207                 :                :  * PG_TRY inside the function.
                               2208                 :                :  */
                               2209                 :                : static void
                               2210                 :           2066 : ReorderBufferProcessTXN(ReorderBuffer *rb, ReorderBufferTXN *txn,
                               2211                 :                :                         XLogRecPtr commit_lsn,
                               2212                 :                :                         volatile Snapshot snapshot_now,
                               2213                 :                :                         volatile CommandId command_id,
                               2214                 :                :                         bool streaming)
                               2215                 :                : {
                               2216                 :                :     bool        using_subtxn;
                               2217                 :           2066 :     MemoryContext ccxt = CurrentMemoryContext;
                               2218                 :           2066 :     ReorderBufferIterTXNState *volatile iterstate = NULL;
                               2219                 :           2066 :     volatile XLogRecPtr prev_lsn = InvalidXLogRecPtr;
                               2220                 :           2066 :     ReorderBufferChange *volatile specinsert = NULL;
                               2221                 :           2066 :     volatile bool stream_started = false;
                               2222                 :           2066 :     ReorderBufferTXN *volatile curtxn = NULL;
                               2223                 :                : 
                               2224                 :                :     /* build data to be able to lookup the CommandIds of catalog tuples */
 4205 rhaas@postgresql.org     2225                 :           2066 :     ReorderBufferBuildTupleCidHash(rb, txn);
                               2226                 :                : 
                               2227                 :                :     /* setup the initial snapshot */
                               2228                 :           2066 :     SetupHistoricSnapshot(snapshot_now, txn->tuplecid_hash);
                               2229                 :                : 
                               2230                 :                :     /*
                               2231                 :                :      * Decoding needs access to syscaches et al., which in turn use
                               2232                 :                :      * heavyweight locks and such. Thus we need to have enough state around to
                               2233                 :                :      * keep track of those.  The easiest way is to simply use a transaction
                               2234                 :                :      * internally.  That also allows us to easily enforce that nothing writes
                               2235                 :                :      * to the database by checking for xid assignments.
                               2236                 :                :      *
                               2237                 :                :      * When we're called via the SQL SRF there's already a transaction
                               2238                 :                :      * started, so start an explicit subtransaction there.
                               2239                 :                :      */
 3877 tgl@sss.pgh.pa.us        2240                 :           2066 :     using_subtxn = IsTransactionOrTransactionBlock();
                               2241                 :                : 
 4205 rhaas@postgresql.org     2242         [ +  + ]:           2066 :     PG_TRY();
                               2243                 :                :     {
                               2244                 :                :         ReorderBufferChange *change;
  941 akapila@postgresql.o     2245                 :           2066 :         int         changes_count = 0;  /* used to accumulate the number of
                               2246                 :                :                                          * changes */
                               2247                 :                : 
 3950 andres@anarazel.de       2248         [ +  + ]:           2066 :         if (using_subtxn)
 1855 akapila@postgresql.o     2249         [ +  + ]:            485 :             BeginInternalSubTransaction(streaming ? "stream" : "replay");
                               2250                 :                :         else
 4205 rhaas@postgresql.org     2251                 :           1581 :             StartTransactionCommand();
                               2252                 :                : 
                               2253                 :                :         /*
                               2254                 :                :          * We only need to send begin/begin-prepare for non-streamed
                               2255                 :                :          * transactions.
                               2256                 :                :          */
 1855 akapila@postgresql.o     2257         [ +  + ]:           2066 :         if (!streaming)
                               2258                 :                :         {
  206 msawada@postgresql.o     2259         [ +  + ]:           1341 :             if (rbtxn_is_prepared(txn))
 1706 akapila@postgresql.o     2260                 :             27 :                 rb->begin_prepare(rb, txn);
                               2261                 :                :             else
                               2262                 :           1314 :                 rb->begin(rb, txn);
                               2263                 :                :         }
                               2264                 :                : 
 2093                          2265                 :           2066 :         ReorderBufferIterTXNInit(rb, txn, &iterstate);
 3877 tgl@sss.pgh.pa.us        2266         [ +  + ]:         363467 :         while ((change = ReorderBufferIterTXNNext(rb, iterstate)) != NULL)
                               2267                 :                :         {
 4205 rhaas@postgresql.org     2268                 :         359346 :             Relation    relation = NULL;
                               2269                 :                :             Oid         reloid;
                               2270                 :                : 
 1110 akapila@postgresql.o     2271         [ -  + ]:         359346 :             CHECK_FOR_INTERRUPTS();
                               2272                 :                : 
                               2273                 :                :             /*
                               2274                 :                :              * We can't call start stream callback before processing first
                               2275                 :                :              * change.
                               2276                 :                :              */
 1855                          2277         [ +  + ]:         359346 :             if (prev_lsn == InvalidXLogRecPtr)
                               2278                 :                :             {
                               2279         [ +  + ]:           2027 :                 if (streaming)
                               2280                 :                :                 {
                               2281                 :            687 :                     txn->origin_id = change->origin_id;
                               2282                 :            687 :                     rb->stream_start(rb, txn, change->lsn);
                               2283                 :            687 :                     stream_started = true;
                               2284                 :                :                 }
                               2285                 :                :             }
                               2286                 :                : 
                               2287                 :                :             /*
                               2288                 :                :              * Enforce correct ordering of changes, merged from multiple
                               2289                 :                :              * subtransactions. The changes may have the same LSN due to
                               2290                 :                :              * MULTI_INSERT xlog records.
                               2291                 :                :              */
                               2292   [ +  +  -  + ]:         359346 :             Assert(prev_lsn == InvalidXLogRecPtr || prev_lsn <= change->lsn);
                               2293                 :                : 
                               2294                 :         359346 :             prev_lsn = change->lsn;
                               2295                 :                : 
                               2296                 :                :             /*
                               2297                 :                :              * Set the current xid to detect concurrent aborts. This is
                               2298                 :                :              * required for the cases when we decode the changes before the
                               2299                 :                :              * COMMIT record is processed.
                               2300                 :                :              */
  206 msawada@postgresql.o     2301   [ +  +  +  + ]:         359346 :             if (streaming || rbtxn_is_prepared(change->txn))
                               2302                 :                :             {
 1855 akapila@postgresql.o     2303                 :         177870 :                 curtxn = change->txn;
                               2304                 :         177870 :                 SetupCheckXidLive(curtxn->xid);
                               2305                 :                :             }
                               2306                 :                : 
 4201 tgl@sss.pgh.pa.us        2307   [ +  +  +  -  :         359346 :             switch (change->action)
                                     +  +  +  +  +  
                                              -  - ]
                               2308                 :                :             {
 3774 andres@anarazel.de       2309                 :           1782 :                 case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM:
                               2310                 :                : 
                               2311                 :                :                     /*
                               2312                 :                :                      * Confirmation for speculative insertion arrived. Simply
                               2313                 :                :                      * use as a normal record. It'll be cleaned up at the end
                               2314                 :                :                      * of INSERT processing.
                               2315                 :                :                      */
 2620 alvherre@alvh.no-ip.     2316         [ -  + ]:           1782 :                     if (specinsert == NULL)
 2620 alvherre@alvh.no-ip.     2317         [ #  # ]:UBC           0 :                         elog(ERROR, "invalid ordering of speculative insertion changes");
 3774 andres@anarazel.de       2318         [ -  + ]:CBC        1782 :                     Assert(specinsert->data.tp.oldtuple == NULL);
                               2319                 :           1782 :                     change = specinsert;
                               2320                 :           1782 :                     change->action = REORDER_BUFFER_CHANGE_INSERT;
                               2321                 :                : 
                               2322                 :                :                     /* intentionally fall through */
 4201 tgl@sss.pgh.pa.us        2323                 :         343503 :                 case REORDER_BUFFER_CHANGE_INSERT:
                               2324                 :                :                 case REORDER_BUFFER_CHANGE_UPDATE:
                               2325                 :                :                 case REORDER_BUFFER_CHANGE_DELETE:
 4205 rhaas@postgresql.org     2326         [ -  + ]:         343503 :                     Assert(snapshot_now);
                               2327                 :                : 
 1158                          2328                 :         343503 :                     reloid = RelidByRelfilenumber(change->data.tp.rlocator.spcOid,
                               2329                 :                :                                                   change->data.tp.rlocator.relNumber);
                               2330                 :                : 
                               2331                 :                :                     /*
                               2332                 :                :                      * Mapped catalog tuple without data, emitted while
                               2333                 :                :                      * catalog table was in the process of being rewritten. We
                               2334                 :                :                      * can fail to look up the relfilenumber, because the
                               2335                 :                :                      * relmapper has no "historic" view, in contrast to the
                               2336                 :                :                      * normal catalog during decoding. Thus repeated rewrites
                               2337                 :                :                      * can cause a lookup failure. That's OK because we do not
                               2338                 :                :                      * decode catalog changes anyway. Normally such tuples
                               2339                 :                :                      * would be skipped over below, but we can't identify
                               2340                 :                :                      * whether the table should be logically logged without
                               2341                 :                :                      * mapping the relfilenumber to the oid.
                               2342                 :                :                      */
 4205                          2343         [ +  + ]:         343495 :                     if (reloid == InvalidOid &&
 4201 tgl@sss.pgh.pa.us        2344         [ +  - ]:             83 :                         change->data.tp.newtuple == NULL &&
                               2345         [ +  - ]:             83 :                         change->data.tp.oldtuple == NULL)
 3774 andres@anarazel.de       2346                 :             83 :                         goto change_done;
 4205 rhaas@postgresql.org     2347         [ -  + ]:         343412 :                     else if (reloid == InvalidOid)
 1158 rhaas@postgresql.org     2348         [ #  # ]:UBC           0 :                         elog(ERROR, "could not map filenumber \"%s\" to relation OID",
                               2349                 :                :                              relpathperm(change->data.tp.rlocator,
                               2350                 :                :                                          MAIN_FORKNUM).str);
                               2351                 :                : 
 4205 rhaas@postgresql.org     2352                 :CBC      343412 :                     relation = RelationIdGetRelation(reloid);
                               2353                 :                : 
 2190 tgl@sss.pgh.pa.us        2354         [ -  + ]:         343412 :                     if (!RelationIsValid(relation))
 1158 rhaas@postgresql.org     2355         [ #  # ]:UBC           0 :                         elog(ERROR, "could not open relation with OID %u (for filenumber \"%s\")",
                               2356                 :                :                              reloid,
                               2357                 :                :                              relpathperm(change->data.tp.rlocator,
                               2358                 :                :                                          MAIN_FORKNUM).str);
                               2359                 :                : 
 3774 andres@anarazel.de       2360   [ +  -  +  -  :CBC      343412 :                     if (!RelationIsLogicallyLogged(relation))
                                     -  +  -  -  -  
                                        -  +  -  +  
                                                 + ]
                               2361                 :           4322 :                         goto change_done;
                               2362                 :                : 
                               2363                 :                :                     /*
                               2364                 :                :                      * Ignore temporary heaps created during DDL unless the
                               2365                 :                :                      * plugin has asked for them.
                               2366                 :                :                      */
 2726 peter_e@gmx.net          2367   [ +  +  +  + ]:         339090 :                     if (relation->rd_rel->relrewrite && !rb->output_rewrites)
                               2368                 :             26 :                         goto change_done;
                               2369                 :                : 
                               2370                 :                :                     /*
                               2371                 :                :                      * For now ignore sequence changes entirely. Most of the
                               2372                 :                :                      * time they don't log changes using records we
                               2373                 :                :                      * understand, so it doesn't make sense to handle the few
                               2374                 :                :                      * cases we do.
                               2375                 :                :                      */
 3774 andres@anarazel.de       2376         [ -  + ]:         339064 :                     if (relation->rd_rel->relkind == RELKIND_SEQUENCE)
 3774 andres@anarazel.de       2377                 :UBC           0 :                         goto change_done;
                               2378                 :                : 
                               2379                 :                :                     /* user-triggered change */
 3774 andres@anarazel.de       2380         [ +  + ]:CBC      339064 :                     if (!IsToastRelation(relation))
                               2381                 :                :                     {
                               2382                 :         337003 :                         ReorderBufferToastReplace(rb, txn, relation, change);
 1855 akapila@postgresql.o     2383                 :         337003 :                         ReorderBufferApplyChange(rb, txn, relation, change,
                               2384                 :                :                                                  streaming);
                               2385                 :                : 
                               2386                 :                :                         /*
                               2387                 :                :                          * Only clear reassembled toast chunks if we're sure
                               2388                 :                :                          * they're not required anymore. The creator of the
                               2389                 :                :                          * tuple tells us.
                               2390                 :                :                          */
 3774 andres@anarazel.de       2391         [ +  + ]:         337000 :                         if (change->data.tp.clear_toast_afterwards)
                               2392                 :         336779 :                             ReorderBufferToastReset(rb, txn);
                               2393                 :                :                     }
                               2394                 :                :                     /* we're not interested in toast deletions */
                               2395         [ +  + ]:           2061 :                     else if (change->action == REORDER_BUFFER_CHANGE_INSERT)
                               2396                 :                :                     {
                               2397                 :                :                         /*
                               2398                 :                :                          * Need to reassemble the full toasted Datum in
                               2399                 :                :                          * memory, to ensure the chunks don't get reused till
                               2400                 :                :                          * we're done remove it from the list of this
                               2401                 :                :                          * transaction's changes. Otherwise it will get
                               2402                 :                :                          * freed/reused while restoring spooled data from
                               2403                 :                :                          * disk.
                               2404                 :                :                          */
 2474 tomas.vondra@postgre     2405         [ -  + ]:           1830 :                         Assert(change->data.tp.newtuple != NULL);
                               2406                 :                : 
                               2407                 :           1830 :                         dlist_delete(&change->node);
                               2408                 :           1830 :                         ReorderBufferToastAppendChunk(rb, txn, relation,
                               2409                 :                :                                                       change);
                               2410                 :                :                     }
                               2411                 :                : 
 3759 bruce@momjian.us         2412                 :            231 :             change_done:
                               2413                 :                : 
                               2414                 :                :                     /*
                               2415                 :                :                      * If speculative insertion was confirmed, the record
                               2416                 :                :                      * isn't needed anymore.
                               2417                 :                :                      */
 3774 andres@anarazel.de       2418         [ +  + ]:         343492 :                     if (specinsert != NULL)
                               2419                 :                :                     {
  178 heikki.linnakangas@i     2420                 :           1782 :                         ReorderBufferFreeChange(rb, specinsert, true);
 3774 andres@anarazel.de       2421                 :           1782 :                         specinsert = NULL;
                               2422                 :                :                     }
                               2423                 :                : 
 1855 akapila@postgresql.o     2424         [ +  + ]:         343492 :                     if (RelationIsValid(relation))
                               2425                 :                :                     {
 3774 andres@anarazel.de       2426                 :         343409 :                         RelationClose(relation);
                               2427                 :         343409 :                         relation = NULL;
                               2428                 :                :                     }
 4205 rhaas@postgresql.org     2429                 :         343492 :                     break;
                               2430                 :                : 
 3774 andres@anarazel.de       2431                 :           1782 :                 case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_INSERT:
                               2432                 :                : 
                               2433                 :                :                     /*
                               2434                 :                :                      * Speculative insertions are dealt with by delaying the
                               2435                 :                :                      * processing of the insert until the confirmation record
                               2436                 :                :                      * arrives. For that we simply unlink the record from the
                               2437                 :                :                      * chain, so it does not get freed/reused while restoring
                               2438                 :                :                      * spooled data from disk.
                               2439                 :                :                      *
                               2440                 :                :                      * This is safe in the face of concurrent catalog changes
                               2441                 :                :                      * because the relevant relation can't be changed between
                               2442                 :                :                      * speculative insertion and confirmation due to
                               2443                 :                :                      * CheckTableNotInUse() and locking.
                               2444                 :                :                      */
                               2445                 :                : 
                               2446                 :                :                     /* clear out a pending (and thus failed) speculation */
                               2447         [ -  + ]:           1782 :                     if (specinsert != NULL)
                               2448                 :                :                     {
  178 heikki.linnakangas@i     2449                 :UBC           0 :                         ReorderBufferFreeChange(rb, specinsert, true);
 3774 andres@anarazel.de       2450                 :              0 :                         specinsert = NULL;
                               2451                 :                :                     }
                               2452                 :                : 
                               2453                 :                :                     /* and memorize the pending insertion */
 3774 andres@anarazel.de       2454                 :CBC        1782 :                     dlist_delete(&change->node);
                               2455                 :           1782 :                     specinsert = change;
                               2456                 :           1782 :                     break;
                               2457                 :                : 
 1544 akapila@postgresql.o     2458                 :UBC           0 :                 case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_ABORT:
                               2459                 :                : 
                               2460                 :                :                     /*
                               2461                 :                :                      * Abort for speculative insertion arrived. So cleanup the
                               2462                 :                :                      * specinsert tuple and toast hash.
                               2463                 :                :                      *
                               2464                 :                :                      * Note that we get the spec abort change for each toast
                               2465                 :                :                      * entry but we need to perform the cleanup only the first
                               2466                 :                :                      * time we get it for the main table.
                               2467                 :                :                      */
                               2468         [ #  # ]:              0 :                     if (specinsert != NULL)
                               2469                 :                :                     {
                               2470                 :                :                         /*
                               2471                 :                :                          * We must clean the toast hash before processing a
                               2472                 :                :                          * completely new tuple to avoid confusion about the
                               2473                 :                :                          * previous tuple's toast chunks.
                               2474                 :                :                          */
                               2475         [ #  # ]:              0 :                         Assert(change->data.tp.clear_toast_afterwards);
                               2476                 :              0 :                         ReorderBufferToastReset(rb, txn);
                               2477                 :                : 
                               2478                 :                :                         /* We don't need this record anymore. */
  178 heikki.linnakangas@i     2479                 :              0 :                         ReorderBufferFreeChange(rb, specinsert, true);
 1544 akapila@postgresql.o     2480                 :              0 :                         specinsert = NULL;
                               2481                 :                :                     }
                               2482                 :              0 :                     break;
                               2483                 :                : 
 2709 peter_e@gmx.net          2484                 :CBC          22 :                 case REORDER_BUFFER_CHANGE_TRUNCATE:
                               2485                 :                :                     {
                               2486                 :                :                         int         i;
 2690 tgl@sss.pgh.pa.us        2487                 :             22 :                         int         nrelids = change->data.truncate.nrelids;
                               2488                 :             22 :                         int         nrelations = 0;
                               2489                 :                :                         Relation   *relations;
                               2490                 :                : 
                               2491                 :             22 :                         relations = palloc0(nrelids * sizeof(Relation));
                               2492         [ +  + ]:             64 :                         for (i = 0; i < nrelids; i++)
                               2493                 :                :                         {
                               2494                 :             42 :                             Oid         relid = change->data.truncate.relids[i];
                               2495                 :                :                             Relation    rel;
                               2496                 :                : 
 1067 drowley@postgresql.o     2497                 :             42 :                             rel = RelationIdGetRelation(relid);
                               2498                 :                : 
                               2499         [ -  + ]:             42 :                             if (!RelationIsValid(rel))
 2690 tgl@sss.pgh.pa.us        2500         [ #  # ]:UBC           0 :                                 elog(ERROR, "could not open relation with OID %u", relid);
                               2501                 :                : 
 1067 drowley@postgresql.o     2502   [ +  -  +  -  :CBC          42 :                             if (!RelationIsLogicallyLogged(rel))
                                     -  +  -  -  -  
                                        -  +  -  -  
                                                 + ]
 2690 tgl@sss.pgh.pa.us        2503                 :UBC           0 :                                 continue;
                               2504                 :                : 
 1067 drowley@postgresql.o     2505                 :CBC          42 :                             relations[nrelations++] = rel;
                               2506                 :                :                         }
                               2507                 :                : 
                               2508                 :                :                         /* Apply the truncate. */
 1855 akapila@postgresql.o     2509                 :             22 :                         ReorderBufferApplyTruncate(rb, txn, nrelations,
                               2510                 :                :                                                    relations, change,
                               2511                 :                :                                                    streaming);
                               2512                 :                : 
 2690 tgl@sss.pgh.pa.us        2513         [ +  + ]:             64 :                         for (i = 0; i < nrelations; i++)
                               2514                 :             42 :                             RelationClose(relations[i]);
                               2515                 :                : 
                               2516                 :             22 :                         break;
                               2517                 :                :                     }
                               2518                 :                : 
 3440 simon@2ndQuadrant.co     2519                 :             11 :                 case REORDER_BUFFER_CHANGE_MESSAGE:
 1855 akapila@postgresql.o     2520                 :             11 :                     ReorderBufferApplyMessage(rb, txn, change, streaming);
 3440 simon@2ndQuadrant.co     2521                 :             11 :                     break;
                               2522                 :                : 
 1787 akapila@postgresql.o     2523                 :           2349 :                 case REORDER_BUFFER_CHANGE_INVALIDATION:
                               2524                 :                :                     /* Execute the invalidation messages locally */
 1212 alvherre@alvh.no-ip.     2525                 :           2349 :                     ReorderBufferExecuteInvalidations(change->data.inval.ninvalidations,
                               2526                 :                :                                                       change->data.inval.invalidations);
 1787 akapila@postgresql.o     2527                 :           2349 :                     break;
                               2528                 :                : 
 4205 rhaas@postgresql.org     2529                 :            622 :                 case REORDER_BUFFER_CHANGE_INTERNAL_SNAPSHOT:
                               2530                 :                :                     /* get rid of the old */
                               2531                 :            622 :                     TeardownHistoricSnapshot(false);
                               2532                 :                : 
                               2533         [ +  + ]:            622 :                     if (snapshot_now->copied)
                               2534                 :                :                     {
                               2535                 :            597 :                         ReorderBufferFreeSnap(rb, snapshot_now);
                               2536                 :            597 :                         snapshot_now =
 4201 tgl@sss.pgh.pa.us        2537                 :            597 :                             ReorderBufferCopySnap(rb, change->data.snapshot,
                               2538                 :                :                                                   txn, command_id);
                               2539                 :                :                     }
                               2540                 :                : 
                               2541                 :                :                     /*
                               2542                 :                :                      * Restored from disk, need to be careful not to double
                               2543                 :                :                      * free. We could introduce refcounting for that, but for
                               2544                 :                :                      * now this seems infrequent enough not to care.
                               2545                 :                :                      */
                               2546         [ -  + ]:             25 :                     else if (change->data.snapshot->copied)
                               2547                 :                :                     {
 4205 rhaas@postgresql.org     2548                 :UBC           0 :                         snapshot_now =
 4201 tgl@sss.pgh.pa.us        2549                 :              0 :                             ReorderBufferCopySnap(rb, change->data.snapshot,
                               2550                 :                :                                                   txn, command_id);
                               2551                 :                :                     }
                               2552                 :                :                     else
                               2553                 :                :                     {
 4201 tgl@sss.pgh.pa.us        2554                 :CBC          25 :                         snapshot_now = change->data.snapshot;
                               2555                 :                :                     }
                               2556                 :                : 
                               2557                 :                :                     /* and continue with the new one */
 4205 rhaas@postgresql.org     2558                 :            622 :                     SetupHistoricSnapshot(snapshot_now, txn->tuplecid_hash);
                               2559                 :            622 :                     break;
                               2560                 :                : 
                               2561                 :          11057 :                 case REORDER_BUFFER_CHANGE_INTERNAL_COMMAND_ID:
 4201 tgl@sss.pgh.pa.us        2562         [ -  + ]:          11057 :                     Assert(change->data.command_id != InvalidCommandId);
                               2563                 :                : 
                               2564         [ +  + ]:          11057 :                     if (command_id < change->data.command_id)
                               2565                 :                :                     {
                               2566                 :           2023 :                         command_id = change->data.command_id;
                               2567                 :                : 
 4205 rhaas@postgresql.org     2568         [ +  + ]:           2023 :                         if (!snapshot_now->copied)
                               2569                 :                :                         {
                               2570                 :                :                             /* we don't use the global one anymore */
                               2571                 :            592 :                             snapshot_now = ReorderBufferCopySnap(rb, snapshot_now,
                               2572                 :                :                                                                  txn, command_id);
                               2573                 :                :                         }
                               2574                 :                : 
                               2575                 :           2023 :                         snapshot_now->curcid = command_id;
                               2576                 :                : 
                               2577                 :           2023 :                         TeardownHistoricSnapshot(false);
                               2578                 :           2023 :                         SetupHistoricSnapshot(snapshot_now, txn->tuplecid_hash);
                               2579                 :                :                     }
                               2580                 :                : 
                               2581                 :          11057 :                     break;
                               2582                 :                : 
 4205 rhaas@postgresql.org     2583                 :UBC           0 :                 case REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID:
                               2584         [ #  # ]:              0 :                     elog(ERROR, "tuplecid value in changequeue");
                               2585                 :                :                     break;
                               2586                 :                :             }
                               2587                 :                : 
                               2588                 :                :             /*
                               2589                 :                :              * It is possible that the data is not sent to downstream for a
                               2590                 :                :              * long time either because the output plugin filtered it or there
                               2591                 :                :              * is a DDL that generates a lot of data that is not processed by
                               2592                 :                :              * the plugin. So, in such cases, the downstream can timeout. To
                               2593                 :                :              * avoid that we try to send a keepalive message if required.
                               2594                 :                :              * Trying to send a keepalive message after every change has some
                               2595                 :                :              * overhead, but testing showed there is no noticeable overhead if
                               2596                 :                :              * we do it after every ~100 changes.
                               2597                 :                :              */
                               2598                 :                : #define CHANGES_THRESHOLD 100
                               2599                 :                : 
  941 akapila@postgresql.o     2600         [ +  + ]:CBC      359335 :             if (++changes_count >= CHANGES_THRESHOLD)
                               2601                 :                :             {
   35 michael@paquier.xyz      2602                 :           3128 :                 rb->update_progress_txn(rb, txn, prev_lsn);
  941 akapila@postgresql.o     2603                 :           3128 :                 changes_count = 0;
                               2604                 :                :             }
                               2605                 :                :         }
                               2606                 :                : 
                               2607                 :                :         /* speculative insertion record must be freed by now */
 1544                          2608         [ -  + ]:           2055 :         Assert(!specinsert);
                               2609                 :                : 
                               2610                 :                :         /* clean up the iterator */
 4205 rhaas@postgresql.org     2611                 :           2055 :         ReorderBufferIterTXNFinish(rb, iterstate);
 3878 tgl@sss.pgh.pa.us        2612                 :           2055 :         iterstate = NULL;
                               2613                 :                : 
                               2614                 :                :         /*
                               2615                 :                :          * Update total transaction count and total bytes processed by the
                               2616                 :                :          * transaction and its subtransactions. Ensure to not count the
                               2617                 :                :          * streamed transaction multiple times.
                               2618                 :                :          *
                               2619                 :                :          * Note that the statistics computation has to be done after
                               2620                 :                :          * ReorderBufferIterTXNFinish as it releases the serialized change
                               2621                 :                :          * which we have already accounted in ReorderBufferIterTXNNext.
                               2622                 :                :          */
 1604 akapila@postgresql.o     2623         [ +  + ]:           2055 :         if (!rbtxn_is_streamed(txn))
                               2624                 :           1406 :             rb->totalTxns++;
                               2625                 :                : 
 1587                          2626                 :           2055 :         rb->totalBytes += txn->total_size;
                               2627                 :                : 
                               2628                 :                :         /*
                               2629                 :                :          * Done with current changes, send the last message for this set of
                               2630                 :                :          * changes depending upon streaming mode.
                               2631                 :                :          */
 1855                          2632         [ +  + ]:           2055 :         if (streaming)
                               2633                 :                :         {
                               2634         [ +  + ]:            717 :             if (stream_started)
                               2635                 :                :             {
                               2636                 :            679 :                 rb->stream_stop(rb, txn, prev_lsn);
                               2637                 :            679 :                 stream_started = false;
                               2638                 :                :             }
                               2639                 :                :         }
                               2640                 :                :         else
                               2641                 :                :         {
                               2642                 :                :             /*
                               2643                 :                :              * Call either PREPARE (for two-phase transactions) or COMMIT (for
                               2644                 :                :              * regular ones).
                               2645                 :                :              */
  206 msawada@postgresql.o     2646         [ +  + ]:           1338 :             if (rbtxn_is_prepared(txn))
                               2647                 :                :             {
                               2648         [ -  + ]:             27 :                 Assert(!rbtxn_sent_prepare(txn));
 1706 akapila@postgresql.o     2649                 :             27 :                 rb->prepare(rb, txn, commit_lsn);
  206 msawada@postgresql.o     2650                 :             27 :                 txn->txn_flags |= RBTXN_SENT_PREPARE;
                               2651                 :                :             }
                               2652                 :                :             else
 1706 akapila@postgresql.o     2653                 :           1311 :                 rb->commit(rb, txn, commit_lsn);
                               2654                 :                :         }
                               2655                 :                : 
                               2656                 :                :         /* this is just a sanity check against bad output plugin behaviour */
 4205 rhaas@postgresql.org     2657         [ -  + ]:           2051 :         if (GetCurrentTransactionIdIfAny() != InvalidTransactionId)
 4147 tgl@sss.pgh.pa.us        2658         [ #  # ]:UBC           0 :             elog(ERROR, "output plugin used XID %u",
                               2659                 :                :                  GetCurrentTransactionId());
                               2660                 :                : 
                               2661                 :                :         /*
                               2662                 :                :          * Remember the command ID and snapshot for the next set of changes in
                               2663                 :                :          * streaming mode.
                               2664                 :                :          */
 1855 akapila@postgresql.o     2665         [ +  + ]:CBC        2051 :         if (streaming)
                               2666                 :            717 :             ReorderBufferSaveTXNSnapshot(rb, txn, snapshot_now, command_id);
                               2667         [ +  + ]:           1334 :         else if (snapshot_now->copied)
                               2668                 :            592 :             ReorderBufferFreeSnap(rb, snapshot_now);
                               2669                 :                : 
                               2670                 :                :         /* cleanup */
 4205 rhaas@postgresql.org     2671                 :           2051 :         TeardownHistoricSnapshot(false);
                               2672                 :                : 
                               2673                 :                :         /*
                               2674                 :                :          * Aborting the current (sub-)transaction as a whole has the right
                               2675                 :                :          * semantics. We want all locks acquired in here to be released, not
                               2676                 :                :          * reassigned to the parent and we do not want any database access
                               2677                 :                :          * have persistent effects.
                               2678                 :                :          */
 3950 andres@anarazel.de       2679                 :           2051 :         AbortCurrentTransaction();
                               2680                 :                : 
                               2681                 :                :         /* make sure there's no cache pollution */
   82 msawada@postgresql.o     2682         [ -  + ]:           2051 :         if (rbtxn_distr_inval_overflowed(txn))
                               2683                 :                :         {
   82 msawada@postgresql.o     2684         [ #  # ]:UBC           0 :             Assert(txn->ninvalidations_distributed == 0);
                               2685                 :              0 :             InvalidateSystemCaches();
                               2686                 :                :         }
                               2687                 :                :         else
                               2688                 :                :         {
   82 msawada@postgresql.o     2689                 :CBC        2051 :             ReorderBufferExecuteInvalidations(txn->ninvalidations, txn->invalidations);
                               2690                 :           2051 :             ReorderBufferExecuteInvalidations(txn->ninvalidations_distributed,
                               2691                 :                :                                               txn->invalidations_distributed);
                               2692                 :                :         }
                               2693                 :                : 
 3950 andres@anarazel.de       2694         [ +  + ]:           2051 :         if (using_subtxn)
 4205 rhaas@postgresql.org     2695                 :            481 :             RollbackAndReleaseCurrentSubTransaction();
                               2696                 :                : 
                               2697                 :                :         /*
                               2698                 :                :          * We are here due to one of the four reasons: 1. Decoding an
                               2699                 :                :          * in-progress txn. 2. Decoding a prepared txn. 3. Decoding of a
                               2700                 :                :          * prepared txn that was (partially) streamed. 4. Decoding a committed
                               2701                 :                :          * txn.
                               2702                 :                :          *
                               2703                 :                :          * For 1, we allow truncation of txn data by removing the changes
                               2704                 :                :          * already streamed but still keeping other things like invalidations,
                               2705                 :                :          * snapshot, and tuplecids. For 2 and 3, we indicate
                               2706                 :                :          * ReorderBufferTruncateTXN to do more elaborate truncation of txn
                               2707                 :                :          * data as the entire transaction has been decoded except for commit.
                               2708                 :                :          * For 4, as the entire txn has been decoded, we can fully clean up
                               2709                 :                :          * the TXN reorder buffer.
                               2710                 :                :          */
  206 msawada@postgresql.o     2711   [ +  +  +  + ]:           2051 :         if (streaming || rbtxn_is_prepared(txn))
                               2712                 :                :         {
                               2713         [ +  + ]:            744 :             if (streaming)
                               2714                 :            717 :                 ReorderBufferMaybeMarkTXNStreamed(rb, txn);
                               2715                 :                : 
                               2716                 :            744 :             ReorderBufferTruncateTXN(rb, txn, rbtxn_is_prepared(txn));
                               2717                 :                :             /* Reset the CheckXidAlive */
 1855 akapila@postgresql.o     2718                 :            744 :             CheckXidAlive = InvalidTransactionId;
                               2719                 :                :         }
                               2720                 :                :         else
                               2721                 :           1307 :             ReorderBufferCleanupTXN(rb, txn);
                               2722                 :                :     }
 4205 rhaas@postgresql.org     2723                 :              9 :     PG_CATCH();
                               2724                 :                :     {
 1855 akapila@postgresql.o     2725                 :              9 :         MemoryContext ecxt = MemoryContextSwitchTo(ccxt);
                               2726                 :              9 :         ErrorData  *errdata = CopyErrorData();
                               2727                 :                : 
                               2728                 :                :         /* TODO: Encapsulate cleanup from the PG_TRY and PG_CATCH blocks */
 4205 rhaas@postgresql.org     2729         [ +  - ]:              9 :         if (iterstate)
                               2730                 :              9 :             ReorderBufferIterTXNFinish(rb, iterstate);
                               2731                 :                : 
                               2732                 :              9 :         TeardownHistoricSnapshot(true);
                               2733                 :                : 
                               2734                 :                :         /*
                               2735                 :                :          * Force cache invalidation to happen outside of a valid transaction
                               2736                 :                :          * to prevent catalog access as we just caught an error.
                               2737                 :                :          */
 3950 andres@anarazel.de       2738                 :              9 :         AbortCurrentTransaction();
                               2739                 :                : 
                               2740                 :                :         /* make sure there's no cache pollution */
   82 msawada@postgresql.o     2741         [ -  + ]:              9 :         if (rbtxn_distr_inval_overflowed(txn))
                               2742                 :                :         {
   82 msawada@postgresql.o     2743         [ #  # ]:UBC           0 :             Assert(txn->ninvalidations_distributed == 0);
                               2744                 :              0 :             InvalidateSystemCaches();
                               2745                 :                :         }
                               2746                 :                :         else
                               2747                 :                :         {
   82 msawada@postgresql.o     2748                 :CBC           9 :             ReorderBufferExecuteInvalidations(txn->ninvalidations, txn->invalidations);
                               2749                 :              9 :             ReorderBufferExecuteInvalidations(txn->ninvalidations_distributed,
                               2750                 :                :                                               txn->invalidations_distributed);
                               2751                 :                :         }
                               2752                 :                : 
 3950 andres@anarazel.de       2753         [ +  + ]:              9 :         if (using_subtxn)
                               2754                 :              4 :             RollbackAndReleaseCurrentSubTransaction();
                               2755                 :                : 
                               2756                 :                :         /*
                               2757                 :                :          * The error code ERRCODE_TRANSACTION_ROLLBACK indicates a concurrent
                               2758                 :                :          * abort of the (sub)transaction we are streaming or preparing. We
                               2759                 :                :          * need to do the cleanup and return gracefully on this error, see
                               2760                 :                :          * SetupCheckXidLive.
                               2761                 :                :          *
                               2762                 :                :          * This error code can be thrown by one of the callbacks we call
                               2763                 :                :          * during decoding so we need to ensure that we return gracefully only
                               2764                 :                :          * when we are sending the data in streaming mode and the streaming is
                               2765                 :                :          * not finished yet or when we are sending the data out on a PREPARE
                               2766                 :                :          * during a two-phase commit.
                               2767                 :                :          */
 1584 akapila@postgresql.o     2768         [ +  + ]:              9 :         if (errdata->sqlerrcode == ERRCODE_TRANSACTION_ROLLBACK &&
  206 msawada@postgresql.o     2769   [ -  +  -  - ]:              8 :             (stream_started || rbtxn_is_prepared(txn)))
                               2770                 :                :         {
                               2771                 :                :             /* curtxn must be set for streaming or prepared transactions */
 1584 akapila@postgresql.o     2772         [ -  + ]:              8 :             Assert(curtxn);
                               2773                 :                : 
                               2774                 :                :             /* Cleanup the temporary error state. */
 1855                          2775                 :              8 :             FlushErrorState();
                               2776                 :              8 :             FreeErrorData(errdata);
                               2777                 :              8 :             errdata = NULL;
                               2778                 :                : 
                               2779                 :                :             /* Remember the transaction is aborted. */
  206 msawada@postgresql.o     2780         [ -  + ]:              8 :             Assert(!rbtxn_is_committed(curtxn));
                               2781                 :              8 :             curtxn->txn_flags |= RBTXN_IS_ABORTED;
                               2782                 :                : 
                               2783                 :                :             /* Mark the transaction is streamed if appropriate */
                               2784         [ +  - ]:              8 :             if (stream_started)
                               2785                 :              8 :                 ReorderBufferMaybeMarkTXNStreamed(rb, txn);
                               2786                 :                : 
                               2787                 :                :             /* Reset the TXN so that it is allowed to stream remaining data. */
 1855 akapila@postgresql.o     2788                 :              8 :             ReorderBufferResetTXN(rb, txn, snapshot_now,
                               2789                 :                :                                   command_id, prev_lsn,
                               2790                 :                :                                   specinsert);
                               2791                 :                :         }
                               2792                 :                :         else
                               2793                 :                :         {
                               2794                 :              1 :             ReorderBufferCleanupTXN(rb, txn);
                               2795                 :              1 :             MemoryContextSwitchTo(ecxt);
                               2796                 :              1 :             PG_RE_THROW();
                               2797                 :                :         }
                               2798                 :                :     }
                               2799         [ -  + ]:           2059 :     PG_END_TRY();
                               2800                 :           2059 : }
                               2801                 :                : 
                               2802                 :                : /*
                               2803                 :                :  * Perform the replay of a transaction and its non-aborted subtransactions.
                               2804                 :                :  *
                               2805                 :                :  * Subtransactions previously have to be processed by
                               2806                 :                :  * ReorderBufferCommitChild(), even if previously assigned to the toplevel
                               2807                 :                :  * transaction with ReorderBufferAssignChild.
                               2808                 :                :  *
                               2809                 :                :  * This interface is called once a prepare or toplevel commit is read for both
                               2810                 :                :  * streamed as well as non-streamed transactions.
                               2811                 :                :  */
                               2812                 :                : static void
 1706                          2813                 :           1410 : ReorderBufferReplay(ReorderBufferTXN *txn,
                               2814                 :                :                     ReorderBuffer *rb, TransactionId xid,
                               2815                 :                :                     XLogRecPtr commit_lsn, XLogRecPtr end_lsn,
                               2816                 :                :                     TimestampTz commit_time,
                               2817                 :                :                     RepOriginId origin_id, XLogRecPtr origin_lsn)
                               2818                 :                : {
                               2819                 :                :     Snapshot    snapshot_now;
 1855                          2820                 :           1410 :     CommandId   command_id = FirstCommandId;
                               2821                 :                : 
                               2822                 :           1410 :     txn->final_lsn = commit_lsn;
                               2823                 :           1410 :     txn->end_lsn = end_lsn;
 1515                          2824                 :           1410 :     txn->xact_time.commit_time = commit_time;
 1855                          2825                 :           1410 :     txn->origin_id = origin_id;
                               2826                 :           1410 :     txn->origin_lsn = origin_lsn;
                               2827                 :                : 
                               2828                 :                :     /*
                               2829                 :                :      * If the transaction was (partially) streamed, we need to commit it in a
                               2830                 :                :      * 'streamed' way. That is, we first stream the remaining part of the
                               2831                 :                :      * transaction, and then invoke stream_commit message.
                               2832                 :                :      *
                               2833                 :                :      * Called after everything (origin ID, LSN, ...) is stored in the
                               2834                 :                :      * transaction to avoid passing that information directly.
                               2835                 :                :      */
                               2836         [ +  + ]:           1410 :     if (rbtxn_is_streamed(txn))
                               2837                 :                :     {
                               2838                 :             66 :         ReorderBufferStreamCommit(rb, txn);
                               2839                 :             66 :         return;
                               2840                 :                :     }
                               2841                 :                : 
                               2842                 :                :     /*
                               2843                 :                :      * If this transaction has no snapshot, it didn't make any changes to the
                               2844                 :                :      * database, so there's nothing to decode.  Note that
                               2845                 :                :      * ReorderBufferCommitChild will have transferred any snapshots from
                               2846                 :                :      * subtransactions if there were any.
                               2847                 :                :      */
                               2848         [ +  + ]:           1344 :     if (txn->base_snapshot == NULL)
                               2849                 :                :     {
                               2850         [ -  + ]:              3 :         Assert(txn->ninvalidations == 0);
                               2851                 :                : 
                               2852                 :                :         /*
                               2853                 :                :          * Removing this txn before a commit might result in the computation
                               2854                 :                :          * of an incorrect restart_lsn. See SnapBuildProcessRunningXacts.
                               2855                 :                :          */
  206 msawada@postgresql.o     2856         [ +  - ]:              3 :         if (!rbtxn_is_prepared(txn))
 1706 akapila@postgresql.o     2857                 :              3 :             ReorderBufferCleanupTXN(rb, txn);
 1855                          2858                 :              3 :         return;
                               2859                 :                :     }
                               2860                 :                : 
                               2861                 :           1341 :     snapshot_now = txn->base_snapshot;
                               2862                 :                : 
                               2863                 :                :     /* Process and send the changes to output plugin. */
                               2864                 :           1341 :     ReorderBufferProcessTXN(rb, txn, commit_lsn, snapshot_now,
                               2865                 :                :                             command_id, false);
                               2866                 :                : }
                               2867                 :                : 
                               2868                 :                : /*
                               2869                 :                :  * Commit a transaction.
                               2870                 :                :  *
                               2871                 :                :  * See comments for ReorderBufferReplay().
                               2872                 :                :  */
                               2873                 :                : void
 1706                          2874                 :           1374 : ReorderBufferCommit(ReorderBuffer *rb, TransactionId xid,
                               2875                 :                :                     XLogRecPtr commit_lsn, XLogRecPtr end_lsn,
                               2876                 :                :                     TimestampTz commit_time,
                               2877                 :                :                     RepOriginId origin_id, XLogRecPtr origin_lsn)
                               2878                 :                : {
                               2879                 :                :     ReorderBufferTXN *txn;
                               2880                 :                : 
                               2881                 :           1374 :     txn = ReorderBufferTXNByXid(rb, xid, false, NULL, InvalidXLogRecPtr,
                               2882                 :                :                                 false);
                               2883                 :                : 
                               2884                 :                :     /* unknown transaction, nothing to replay */
                               2885         [ +  + ]:           1374 :     if (txn == NULL)
                               2886                 :              6 :         return;
                               2887                 :                : 
                               2888                 :           1368 :     ReorderBufferReplay(txn, rb, xid, commit_lsn, end_lsn, commit_time,
                               2889                 :                :                         origin_id, origin_lsn);
                               2890                 :                : }
                               2891                 :                : 
                               2892                 :                : /*
                               2893                 :                :  * Record the prepare information for a transaction. Also, mark the transaction
                               2894                 :                :  * as a prepared transaction.
                               2895                 :                :  */
                               2896                 :                : bool
                               2897                 :            143 : ReorderBufferRememberPrepareInfo(ReorderBuffer *rb, TransactionId xid,
                               2898                 :                :                                  XLogRecPtr prepare_lsn, XLogRecPtr end_lsn,
                               2899                 :                :                                  TimestampTz prepare_time,
                               2900                 :                :                                  RepOriginId origin_id, XLogRecPtr origin_lsn)
                               2901                 :                : {
                               2902                 :                :     ReorderBufferTXN *txn;
                               2903                 :                : 
                               2904                 :            143 :     txn = ReorderBufferTXNByXid(rb, xid, false, NULL, InvalidXLogRecPtr, false);
                               2905                 :                : 
                               2906                 :                :     /* unknown transaction, nothing to do */
                               2907         [ -  + ]:            143 :     if (txn == NULL)
 1706 akapila@postgresql.o     2908                 :UBC           0 :         return false;
                               2909                 :                : 
                               2910                 :                :     /*
                               2911                 :                :      * Remember the prepare information to be later used by commit prepared in
                               2912                 :                :      * case we skip doing prepare.
                               2913                 :                :      */
 1706 akapila@postgresql.o     2914                 :CBC         143 :     txn->final_lsn = prepare_lsn;
                               2915                 :            143 :     txn->end_lsn = end_lsn;
 1515                          2916                 :            143 :     txn->xact_time.prepare_time = prepare_time;
 1706                          2917                 :            143 :     txn->origin_id = origin_id;
                               2918                 :            143 :     txn->origin_lsn = origin_lsn;
                               2919                 :                : 
                               2920                 :                :     /* Mark this transaction as a prepared transaction */
  206 msawada@postgresql.o     2921         [ -  + ]:            143 :     Assert((txn->txn_flags & RBTXN_PREPARE_STATUS_MASK) == 0);
                               2922                 :            143 :     txn->txn_flags |= RBTXN_IS_PREPARED;
                               2923                 :                : 
 1706 akapila@postgresql.o     2924                 :            143 :     return true;
                               2925                 :                : }
                               2926                 :                : 
                               2927                 :                : /* Remember that we have skipped prepare */
                               2928                 :                : void
                               2929                 :            104 : ReorderBufferSkipPrepare(ReorderBuffer *rb, TransactionId xid)
                               2930                 :                : {
                               2931                 :                :     ReorderBufferTXN *txn;
                               2932                 :                : 
                               2933                 :            104 :     txn = ReorderBufferTXNByXid(rb, xid, false, NULL, InvalidXLogRecPtr, false);
                               2934                 :                : 
                               2935                 :                :     /* unknown transaction, nothing to do */
                               2936         [ -  + ]:            104 :     if (txn == NULL)
 1706 akapila@postgresql.o     2937                 :UBC           0 :         return;
                               2938                 :                : 
                               2939                 :                :     /* txn must have been marked as a prepared transaction */
  206 msawada@postgresql.o     2940         [ -  + ]:CBC         104 :     Assert((txn->txn_flags & RBTXN_PREPARE_STATUS_MASK) == RBTXN_IS_PREPARED);
 1706 akapila@postgresql.o     2941                 :            104 :     txn->txn_flags |= RBTXN_SKIPPED_PREPARE;
                               2942                 :                : }
                               2943                 :                : 
                               2944                 :                : /*
                               2945                 :                :  * Prepare a two-phase transaction.
                               2946                 :                :  *
                               2947                 :                :  * See comments for ReorderBufferReplay().
                               2948                 :                :  */
                               2949                 :                : void
                               2950                 :             39 : ReorderBufferPrepare(ReorderBuffer *rb, TransactionId xid,
                               2951                 :                :                      char *gid)
                               2952                 :                : {
                               2953                 :                :     ReorderBufferTXN *txn;
                               2954                 :                : 
                               2955                 :             39 :     txn = ReorderBufferTXNByXid(rb, xid, false, NULL, InvalidXLogRecPtr,
                               2956                 :                :                                 false);
                               2957                 :                : 
                               2958                 :                :     /* unknown transaction, nothing to replay */
                               2959         [ -  + ]:             39 :     if (txn == NULL)
 1706 akapila@postgresql.o     2960                 :UBC           0 :         return;
                               2961                 :                : 
                               2962                 :                :     /*
                               2963                 :                :      * txn must have been marked as a prepared transaction and must have
                               2964                 :                :      * neither been skipped nor sent a prepare. Also, the prepare info must
                               2965                 :                :      * have been updated in it by now.
                               2966                 :                :      */
  206 msawada@postgresql.o     2967         [ -  + ]:CBC          39 :     Assert((txn->txn_flags & RBTXN_PREPARE_STATUS_MASK) == RBTXN_IS_PREPARED);
 1706 akapila@postgresql.o     2968         [ -  + ]:             39 :     Assert(txn->final_lsn != InvalidXLogRecPtr);
                               2969                 :                : 
  206 msawada@postgresql.o     2970                 :             39 :     txn->gid = pstrdup(gid);
                               2971                 :                : 
 1706 akapila@postgresql.o     2972                 :             39 :     ReorderBufferReplay(txn, rb, xid, txn->final_lsn, txn->end_lsn,
 1515                          2973                 :             39 :                         txn->xact_time.prepare_time, txn->origin_id, txn->origin_lsn);
                               2974                 :                : 
                               2975                 :                :     /*
                               2976                 :                :      * Send a prepare if not already done so. This might occur if we have
                               2977                 :                :      * detected a concurrent abort while replaying the non-streaming
                               2978                 :                :      * transaction.
                               2979                 :                :      */
  206 msawada@postgresql.o     2980         [ -  + ]:             39 :     if (!rbtxn_sent_prepare(txn))
                               2981                 :                :     {
 1619 akapila@postgresql.o     2982                 :UBC           0 :         rb->prepare(rb, txn, txn->final_lsn);
  206 msawada@postgresql.o     2983                 :              0 :         txn->txn_flags |= RBTXN_SENT_PREPARE;
                               2984                 :                :     }
                               2985                 :                : }
                               2986                 :                : 
                               2987                 :                : /*
                               2988                 :                :  * This is used to handle COMMIT/ROLLBACK PREPARED.
                               2989                 :                :  */
                               2990                 :                : void
 1706 akapila@postgresql.o     2991                 :CBC          41 : ReorderBufferFinishPrepared(ReorderBuffer *rb, TransactionId xid,
                               2992                 :                :                             XLogRecPtr commit_lsn, XLogRecPtr end_lsn,
                               2993                 :                :                             XLogRecPtr two_phase_at,
                               2994                 :                :                             TimestampTz commit_time, RepOriginId origin_id,
                               2995                 :                :                             XLogRecPtr origin_lsn, char *gid, bool is_commit)
                               2996                 :                : {
                               2997                 :                :     ReorderBufferTXN *txn;
                               2998                 :                :     XLogRecPtr  prepare_end_lsn;
                               2999                 :                :     TimestampTz prepare_time;
                               3000                 :                : 
 1656                          3001                 :             41 :     txn = ReorderBufferTXNByXid(rb, xid, false, NULL, commit_lsn, false);
                               3002                 :                : 
                               3003                 :                :     /* unknown transaction, nothing to do */
 1706                          3004         [ -  + ]:             41 :     if (txn == NULL)
 1706 akapila@postgresql.o     3005                 :UBC           0 :         return;
                               3006                 :                : 
                               3007                 :                :     /*
                               3008                 :                :      * By this time the txn has the prepare record information, remember it to
                               3009                 :                :      * be later used for rollback.
                               3010                 :                :      */
 1706 akapila@postgresql.o     3011                 :CBC          41 :     prepare_end_lsn = txn->end_lsn;
 1515                          3012                 :             41 :     prepare_time = txn->xact_time.prepare_time;
                               3013                 :                : 
                               3014                 :                :     /* add the gid in the txn */
 1706                          3015                 :             41 :     txn->gid = pstrdup(gid);
                               3016                 :                : 
                               3017                 :                :     /*
                               3018                 :                :      * It is possible that this transaction is not decoded at prepare time
                               3019                 :                :      * either because by that time we didn't have a consistent snapshot, or
                               3020                 :                :      * two_phase was not enabled, or it was decoded earlier but we have
                               3021                 :                :      * restarted. We only need to send the prepare if it was not decoded
                               3022                 :                :      * earlier. We don't need to decode the xact for aborts if it is not done
                               3023                 :                :      * already.
                               3024                 :                :      */
 1515                          3025   [ +  +  +  - ]:             41 :     if ((txn->final_lsn < two_phase_at) && is_commit)
                               3026                 :                :     {
                               3027                 :                :         /*
                               3028                 :                :          * txn must have been marked as a prepared transaction and skipped but
                               3029                 :                :          * not sent a prepare. Also, the prepare info must have been updated
                               3030                 :                :          * in txn even if we skip prepare.
                               3031                 :                :          */
  206 msawada@postgresql.o     3032         [ -  + ]:              3 :         Assert((txn->txn_flags & RBTXN_PREPARE_STATUS_MASK) ==
                               3033                 :                :                (RBTXN_IS_PREPARED | RBTXN_SKIPPED_PREPARE));
 1706 akapila@postgresql.o     3034         [ -  + ]:              3 :         Assert(txn->final_lsn != InvalidXLogRecPtr);
                               3035                 :                : 
                               3036                 :                :         /*
                               3037                 :                :          * By this time the txn has the prepare record information and it is
                               3038                 :                :          * important to use that so that downstream gets the accurate
                               3039                 :                :          * information. If instead, we have passed commit information here
                               3040                 :                :          * then downstream can behave as it has already replayed commit
                               3041                 :                :          * prepared after the restart.
                               3042                 :                :          */
                               3043                 :              3 :         ReorderBufferReplay(txn, rb, xid, txn->final_lsn, txn->end_lsn,
 1515                          3044                 :              3 :                             txn->xact_time.prepare_time, txn->origin_id, txn->origin_lsn);
                               3045                 :                :     }
                               3046                 :                : 
 1706                          3047                 :             41 :     txn->final_lsn = commit_lsn;
                               3048                 :             41 :     txn->end_lsn = end_lsn;
 1515                          3049                 :             41 :     txn->xact_time.commit_time = commit_time;
 1706                          3050                 :             41 :     txn->origin_id = origin_id;
                               3051                 :             41 :     txn->origin_lsn = origin_lsn;
                               3052                 :                : 
                               3053         [ +  + ]:             41 :     if (is_commit)
                               3054                 :             31 :         rb->commit_prepared(rb, txn, commit_lsn);
                               3055                 :                :     else
                               3056                 :             10 :         rb->rollback_prepared(rb, txn, prepare_end_lsn, prepare_time);
                               3057                 :                : 
                               3058                 :                :     /* cleanup: make sure there's no cache pollution */
                               3059                 :             41 :     ReorderBufferExecuteInvalidations(txn->ninvalidations,
                               3060                 :                :                                       txn->invalidations);
                               3061                 :             41 :     ReorderBufferCleanupTXN(rb, txn);
                               3062                 :                : }
                               3063                 :                : 
                               3064                 :                : /*
                               3065                 :                :  * Abort a transaction that possibly has previous changes. Needs to be first
                               3066                 :                :  * called for subtransactions and then for the toplevel xid.
                               3067                 :                :  *
                               3068                 :                :  * NB: Transactions handled here have to have actively aborted (i.e. have
                               3069                 :                :  * produced an abort record). Implicitly aborted transactions are handled via
                               3070                 :                :  * ReorderBufferAbortOld(); transactions we're just not interested in, but
                               3071                 :                :  * which have committed are handled in ReorderBufferForget().
                               3072                 :                :  *
                               3073                 :                :  * This function purges this transaction and its contents from memory and
                               3074                 :                :  * disk.
                               3075                 :                :  */
                               3076                 :                : void
  971                          3077                 :            149 : ReorderBufferAbort(ReorderBuffer *rb, TransactionId xid, XLogRecPtr lsn,
                               3078                 :                :                    TimestampTz abort_time)
                               3079                 :                : {
                               3080                 :                :     ReorderBufferTXN *txn;
                               3081                 :                : 
 4205 rhaas@postgresql.org     3082                 :            149 :     txn = ReorderBufferTXNByXid(rb, xid, false, NULL, InvalidXLogRecPtr,
                               3083                 :                :                                 false);
                               3084                 :                : 
                               3085                 :                :     /* unknown, nothing to remove */
                               3086         [ -  + ]:            149 :     if (txn == NULL)
 4205 rhaas@postgresql.org     3087                 :UBC           0 :         return;
                               3088                 :                : 
  971 akapila@postgresql.o     3089                 :CBC         149 :     txn->xact_time.abort_time = abort_time;
                               3090                 :                : 
                               3091                 :                :     /* For streamed transactions notify the remote node about the abort. */
 1855                          3092         [ +  + ]:            149 :     if (rbtxn_is_streamed(txn))
                               3093                 :                :     {
                               3094                 :             30 :         rb->stream_abort(rb, txn, lsn);
                               3095                 :                : 
                               3096                 :                :         /*
                               3097                 :                :          * We might have decoded changes for this transaction that could load
                               3098                 :                :          * the cache as per the current transaction's view (consider DDL's
                               3099                 :                :          * happened in this transaction). We don't want the decoding of future
                               3100                 :                :          * transactions to use those cache entries so execute only the inval
                               3101                 :                :          * messages in this transaction.
                               3102                 :                :          */
                               3103         [ -  + ]:             30 :         if (txn->ninvalidations > 0)
 1855 akapila@postgresql.o     3104                 :UBC           0 :             ReorderBufferImmediateInvalidation(rb, txn->ninvalidations,
                               3105                 :                :                                                txn->invalidations);
                               3106                 :                :     }
                               3107                 :                : 
                               3108                 :                :     /* cosmetic... */
 4205 rhaas@postgresql.org     3109                 :CBC         149 :     txn->final_lsn = lsn;
                               3110                 :                : 
                               3111                 :                :     /* remove potential on-disk data, and deallocate */
                               3112                 :            149 :     ReorderBufferCleanupTXN(rb, txn);
                               3113                 :                : }
                               3114                 :                : 
                               3115                 :                : /*
                               3116                 :                :  * Abort all transactions that aren't actually running anymore because the
                               3117                 :                :  * server restarted.
                               3118                 :                :  *
                               3119                 :                :  * NB: These really have to be transactions that have aborted due to a server
                               3120                 :                :  * crash/immediate restart, as we don't deal with invalidations here.
                               3121                 :                :  */
                               3122                 :                : void
                               3123                 :           1417 : ReorderBufferAbortOld(ReorderBuffer *rb, TransactionId oldestRunningXid)
                               3124                 :                : {
                               3125                 :                :     dlist_mutable_iter it;
                               3126                 :                : 
                               3127                 :                :     /*
                               3128                 :                :      * Iterate through all (potential) toplevel TXNs and abort all that are
                               3129                 :                :      * older than what possibly can be running. Once we've found the first
                               3130                 :                :      * that is alive we stop, there might be some that acquired an xid earlier
                               3131                 :                :      * but started writing later, but it's unlikely and they will be cleaned
                               3132                 :                :      * up in a later call to this function.
                               3133                 :                :      */
                               3134   [ +  -  +  + ]:           1422 :     dlist_foreach_modify(it, &rb->toplevel_by_lsn)
                               3135                 :                :     {
                               3136                 :                :         ReorderBufferTXN *txn;
                               3137                 :                : 
                               3138                 :             61 :         txn = dlist_container(ReorderBufferTXN, node, it.cur);
                               3139                 :                : 
                               3140         [ +  + ]:             61 :         if (TransactionIdPrecedes(txn->xid, oldestRunningXid))
                               3141                 :                :         {
 3038 andres@anarazel.de       3142         [ -  + ]:              5 :             elog(DEBUG2, "aborting old transaction %u", txn->xid);
                               3143                 :                : 
                               3144                 :                :             /* Notify the remote node about the crash/immediate restart. */
  973 akapila@postgresql.o     3145         [ -  + ]:              5 :             if (rbtxn_is_streamed(txn))
  973 akapila@postgresql.o     3146                 :UBC           0 :                 rb->stream_abort(rb, txn, InvalidXLogRecPtr);
                               3147                 :                : 
                               3148                 :                :             /* remove potential on-disk data, and deallocate this tx */
 4205 rhaas@postgresql.org     3149                 :CBC           5 :             ReorderBufferCleanupTXN(rb, txn);
                               3150                 :                :         }
                               3151                 :                :         else
                               3152                 :             56 :             return;
                               3153                 :                :     }
                               3154                 :                : }
                               3155                 :                : 
                               3156                 :                : /*
                               3157                 :                :  * Forget the contents of a transaction if we aren't interested in its
                               3158                 :                :  * contents. Needs to be first called for subtransactions and then for the
                               3159                 :                :  * toplevel xid.
                               3160                 :                :  *
                               3161                 :                :  * This is significantly different to ReorderBufferAbort() because
                               3162                 :                :  * transactions that have committed need to be treated differently from aborted
                               3163                 :                :  * ones since they may have modified the catalog.
                               3164                 :                :  *
                               3165                 :                :  * Note that this is only allowed to be called in the moment a transaction
                               3166                 :                :  * commit has just been read, not earlier; otherwise later records referring
                               3167                 :                :  * to this xid might re-create the transaction incompletely.
                               3168                 :                :  */
                               3169                 :                : void
                               3170                 :           2575 : ReorderBufferForget(ReorderBuffer *rb, TransactionId xid, XLogRecPtr lsn)
                               3171                 :                : {
                               3172                 :                :     ReorderBufferTXN *txn;
                               3173                 :                : 
                               3174                 :           2575 :     txn = ReorderBufferTXNByXid(rb, xid, false, NULL, InvalidXLogRecPtr,
                               3175                 :                :                                 false);
                               3176                 :                : 
                               3177                 :                :     /* unknown, nothing to forget */
                               3178         [ +  + ]:           2575 :     if (txn == NULL)
                               3179                 :            561 :         return;
                               3180                 :                : 
                               3181                 :                :     /* this transaction mustn't be streamed */
 1003 akapila@postgresql.o     3182         [ -  + ]:           2014 :     Assert(!rbtxn_is_streamed(txn));
                               3183                 :                : 
                               3184                 :                :     /* cosmetic... */
 4205 rhaas@postgresql.org     3185                 :           2014 :     txn->final_lsn = lsn;
                               3186                 :                : 
                               3187                 :                :     /*
                               3188                 :                :      * Process only cache invalidation messages in this transaction if there
                               3189                 :                :      * are any. Even if we're not interested in the transaction's contents, it
                               3190                 :                :      * could have manipulated the catalog and we need to update the caches
                               3191                 :                :      * according to that.
                               3192                 :                :      */
                               3193   [ +  +  +  + ]:           2014 :     if (txn->base_snapshot != NULL && txn->ninvalidations > 0)
 3423 andres@anarazel.de       3194                 :            560 :         ReorderBufferImmediateInvalidation(rb, txn->ninvalidations,
                               3195                 :                :                                            txn->invalidations);
                               3196                 :                :     else
 4205 rhaas@postgresql.org     3197         [ -  + ]:           1454 :         Assert(txn->ninvalidations == 0);
                               3198                 :                : 
                               3199                 :                :     /* remove potential on-disk data, and deallocate */
                               3200                 :           2014 :     ReorderBufferCleanupTXN(rb, txn);
                               3201                 :                : }
                               3202                 :                : 
                               3203                 :                : /*
                               3204                 :                :  * Invalidate cache for those transactions that need to be skipped just in case
                               3205                 :                :  * catalogs were manipulated as part of the transaction.
                               3206                 :                :  *
                               3207                 :                :  * Note that this is a special-purpose function for prepared transactions where
                               3208                 :                :  * we don't want to clean up the TXN even when we decide to skip it. See
                               3209                 :                :  * DecodePrepare.
                               3210                 :                :  */
                               3211                 :                : void
 1706 akapila@postgresql.o     3212                 :            101 : ReorderBufferInvalidate(ReorderBuffer *rb, TransactionId xid, XLogRecPtr lsn)
                               3213                 :                : {
                               3214                 :                :     ReorderBufferTXN *txn;
                               3215                 :                : 
                               3216                 :            101 :     txn = ReorderBufferTXNByXid(rb, xid, false, NULL, InvalidXLogRecPtr,
                               3217                 :                :                                 false);
                               3218                 :                : 
                               3219                 :                :     /* unknown, nothing to do */
                               3220         [ -  + ]:            101 :     if (txn == NULL)
 1706 akapila@postgresql.o     3221                 :UBC           0 :         return;
                               3222                 :                : 
                               3223                 :                :     /*
                               3224                 :                :      * Process cache invalidation messages if there are any. Even if we're not
                               3225                 :                :      * interested in the transaction's contents, it could have manipulated the
                               3226                 :                :      * catalog and we need to update the caches according to that.
                               3227                 :                :      */
 1706 akapila@postgresql.o     3228   [ +  -  +  + ]:CBC         101 :     if (txn->base_snapshot != NULL && txn->ninvalidations > 0)
                               3229                 :             29 :         ReorderBufferImmediateInvalidation(rb, txn->ninvalidations,
                               3230                 :                :                                            txn->invalidations);
                               3231                 :                :     else
                               3232         [ -  + ]:             72 :         Assert(txn->ninvalidations == 0);
                               3233                 :                : }
                               3234                 :                : 
                               3235                 :                : 
                               3236                 :                : /*
                               3237                 :                :  * Execute invalidations happening outside the context of a decoded
                               3238                 :                :  * transaction. That currently happens either for xid-less commits
                               3239                 :                :  * (cf. RecordTransactionCommit()) or for invalidations in uninteresting
                               3240                 :                :  * transactions (via ReorderBufferForget()).
                               3241                 :                :  */
                               3242                 :                : void
 3423 andres@anarazel.de       3243                 :            598 : ReorderBufferImmediateInvalidation(ReorderBuffer *rb, uint32 ninvalidations,
                               3244                 :                :                                    SharedInvalidationMessage *invalidations)
                               3245                 :                : {
                               3246                 :            598 :     bool        use_subtxn = IsTransactionOrTransactionBlock();
                               3247                 :                :     int         i;
                               3248                 :                : 
                               3249         [ +  + ]:            598 :     if (use_subtxn)
                               3250                 :            429 :         BeginInternalSubTransaction("replay");
                               3251                 :                : 
                               3252                 :                :     /*
                               3253                 :                :      * Force invalidations to happen outside of a valid transaction - that way
                               3254                 :                :      * entries will just be marked as invalid without accessing the catalog.
                               3255                 :                :      * That's advantageous because we don't need to setup the full state
                               3256                 :                :      * necessary for catalog access.
                               3257                 :                :      */
                               3258         [ +  + ]:            598 :     if (use_subtxn)
                               3259                 :            429 :         AbortCurrentTransaction();
                               3260                 :                : 
                               3261         [ +  + ]:          25229 :     for (i = 0; i < ninvalidations; i++)
                               3262                 :          24631 :         LocalExecuteInvalidationMessage(&invalidations[i]);
                               3263                 :                : 
                               3264         [ +  + ]:            598 :     if (use_subtxn)
                               3265                 :            429 :         RollbackAndReleaseCurrentSubTransaction();
                               3266                 :            598 : }
                               3267                 :                : 
                               3268                 :                : /*
                               3269                 :                :  * Tell reorderbuffer about an xid seen in the WAL stream. Has to be called at
                               3270                 :                :  * least once for every xid in XLogRecord->xl_xid (other places in records
                               3271                 :                :  * may, but do not have to be passed through here).
                               3272                 :                :  *
                               3273                 :                :  * Reorderbuffer keeps some data structures about transactions in LSN order,
                               3274                 :                :  * for efficiency. To do that it has to know about when transactions are seen
                               3275                 :                :  * first in the WAL. As many types of records are not actually interesting for
                               3276                 :                :  * logical decoding, they do not necessarily pass through here.
                               3277                 :                :  */
                               3278                 :                : void
 3472                          3279                 :        1901641 : ReorderBufferProcessXid(ReorderBuffer *rb, TransactionId xid, XLogRecPtr lsn)
                               3280                 :                : {
                               3281                 :                :     /* many records won't have an xid assigned, centralize check here */
                               3282         [ +  + ]:        1901641 :     if (xid != InvalidTransactionId)
                               3283                 :        1899610 :         ReorderBufferTXNByXid(rb, xid, true, NULL, lsn, true);
 4205 rhaas@postgresql.org     3284                 :        1901641 : }
                               3285                 :                : 
                               3286                 :                : /*
                               3287                 :                :  * Add a new snapshot to this transaction that may only used after lsn 'lsn'
                               3288                 :                :  * because the previous snapshot doesn't describe the catalog correctly for
                               3289                 :                :  * following rows.
                               3290                 :                :  */
                               3291                 :                : void
                               3292                 :           1197 : ReorderBufferAddSnapshot(ReorderBuffer *rb, TransactionId xid,
                               3293                 :                :                          XLogRecPtr lsn, Snapshot snap)
                               3294                 :                : {
  178 heikki.linnakangas@i     3295                 :           1197 :     ReorderBufferChange *change = ReorderBufferAllocChange(rb);
                               3296                 :                : 
 4201 tgl@sss.pgh.pa.us        3297                 :           1197 :     change->data.snapshot = snap;
                               3298                 :           1197 :     change->action = REORDER_BUFFER_CHANGE_INTERNAL_SNAPSHOT;
                               3299                 :                : 
 1855 akapila@postgresql.o     3300                 :           1197 :     ReorderBufferQueueChange(rb, xid, lsn, change, false);
 4205 rhaas@postgresql.org     3301                 :           1197 : }
                               3302                 :                : 
                               3303                 :                : /*
                               3304                 :                :  * Set up the transaction's base snapshot.
                               3305                 :                :  *
                               3306                 :                :  * If we know that xid is a subtransaction, set the base snapshot on the
                               3307                 :                :  * top-level transaction instead.
                               3308                 :                :  */
                               3309                 :                : void
                               3310                 :           3149 : ReorderBufferSetBaseSnapshot(ReorderBuffer *rb, TransactionId xid,
                               3311                 :                :                              XLogRecPtr lsn, Snapshot snap)
                               3312                 :                : {
                               3313                 :                :     ReorderBufferTXN *txn;
                               3314                 :                :     bool        is_new;
                               3315                 :                : 
 1044 peter@eisentraut.org     3316         [ -  + ]:           3149 :     Assert(snap != NULL);
                               3317                 :                : 
                               3318                 :                :     /*
                               3319                 :                :      * Fetch the transaction to operate on.  If we know it's a subtransaction,
                               3320                 :                :      * operate on its top-level transaction instead.
                               3321                 :                :      */
 4205 rhaas@postgresql.org     3322                 :           3149 :     txn = ReorderBufferTXNByXid(rb, xid, true, &is_new, lsn, true);
 2066 alvherre@alvh.no-ip.     3323         [ +  + ]:           3149 :     if (rbtxn_is_known_subxact(txn))
 2629                          3324                 :             98 :         txn = ReorderBufferTXNByXid(rb, txn->toplevel_xid, false,
                               3325                 :                :                                     NULL, InvalidXLogRecPtr, false);
 4205 rhaas@postgresql.org     3326         [ -  + ]:           3149 :     Assert(txn->base_snapshot == NULL);
                               3327                 :                : 
                               3328                 :           3149 :     txn->base_snapshot = snap;
                               3329                 :           3149 :     txn->base_snapshot_lsn = lsn;
 2629 alvherre@alvh.no-ip.     3330                 :           3149 :     dlist_push_tail(&rb->txns_by_base_snapshot_lsn, &txn->base_snapshot_node);
                               3331                 :                : 
                               3332                 :           3149 :     AssertTXNLsnOrder(rb);
 4205 rhaas@postgresql.org     3333                 :           3149 : }
                               3334                 :                : 
                               3335                 :                : /*
                               3336                 :                :  * Access the catalog with this CommandId at this point in the changestream.
                               3337                 :                :  *
                               3338                 :                :  * May only be called for command ids > 1
                               3339                 :                :  */
                               3340                 :                : void
                               3341                 :          24222 : ReorderBufferAddNewCommandId(ReorderBuffer *rb, TransactionId xid,
                               3342                 :                :                              XLogRecPtr lsn, CommandId cid)
                               3343                 :                : {
  178 heikki.linnakangas@i     3344                 :          24222 :     ReorderBufferChange *change = ReorderBufferAllocChange(rb);
                               3345                 :                : 
 4201 tgl@sss.pgh.pa.us        3346                 :          24222 :     change->data.command_id = cid;
                               3347                 :          24222 :     change->action = REORDER_BUFFER_CHANGE_INTERNAL_COMMAND_ID;
                               3348                 :                : 
 1855 akapila@postgresql.o     3349                 :          24222 :     ReorderBufferQueueChange(rb, xid, lsn, change, false);
 4205 rhaas@postgresql.org     3350                 :          24222 : }
                               3351                 :                : 
                               3352                 :                : /*
                               3353                 :                :  * Update memory counters to account for the new or removed change.
                               3354                 :                :  *
                               3355                 :                :  * We update two counters - in the reorder buffer, and in the transaction
                               3356                 :                :  * containing the change. The reorder buffer counter allows us to quickly
                               3357                 :                :  * decide if we reached the memory limit, the transaction counter allows
                               3358                 :                :  * us to quickly pick the largest transaction for eviction.
                               3359                 :                :  *
                               3360                 :                :  * Either txn or change must be non-NULL at least. We update the memory
                               3361                 :                :  * counter of txn if it's non-NULL, otherwise change->txn.
                               3362                 :                :  *
                               3363                 :                :  * When streaming is enabled, we need to update the toplevel transaction
                               3364                 :                :  * counters instead - we don't really care about subtransactions as we
                               3365                 :                :  * can't stream them individually anyway, and we only pick toplevel
                               3366                 :                :  * transactions for eviction. So only toplevel transactions matter.
                               3367                 :                :  */
                               3368                 :                : static void
 2121 akapila@postgresql.o     3369                 :        1647642 : ReorderBufferChangeMemoryUpdate(ReorderBuffer *rb,
                               3370                 :                :                                 ReorderBufferChange *change,
                               3371                 :                :                                 ReorderBufferTXN *txn,
                               3372                 :                :                                 bool addition, Size sz)
                               3373                 :                : {
                               3374                 :                :     ReorderBufferTXN *toptxn;
                               3375                 :                : 
  521 msawada@postgresql.o     3376   [ +  +  -  + ]:        1647642 :     Assert(txn || change);
                               3377                 :                : 
                               3378                 :                :     /*
                               3379                 :                :      * Ignore tuple CID changes, because those are not evicted when reaching
                               3380                 :                :      * memory limit. So we just don't count them, because it might easily
                               3381                 :                :      * trigger a pointless attempt to spill.
                               3382                 :                :      */
                               3383   [ +  +  +  + ]:        1647642 :     if (change && change->action == REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID)
 2121 akapila@postgresql.o     3384                 :          24090 :         return;
                               3385                 :                : 
  521 msawada@postgresql.o     3386         [ +  + ]:        1623552 :     if (sz == 0)
                               3387                 :            905 :         return;
                               3388                 :                : 
                               3389         [ +  + ]:        1622647 :     if (txn == NULL)
                               3390                 :        1615439 :         txn = change->txn;
                               3391         [ -  + ]:        1622647 :     Assert(txn != NULL);
                               3392                 :                : 
                               3393                 :                :     /*
                               3394                 :                :      * Update the total size in top level as well. This is later used to
                               3395                 :                :      * compute the decoding stats.
                               3396                 :                :      */
  904 akapila@postgresql.o     3397         [ +  + ]:        1622647 :     toptxn = rbtxn_get_toptxn(txn);
                               3398                 :                : 
 2121                          3399         [ +  + ]:        1622647 :     if (addition)
                               3400                 :                :     {
  513 msawada@postgresql.o     3401                 :        1442004 :         Size        oldsize = txn->size;
                               3402                 :                : 
 1855 akapila@postgresql.o     3403                 :        1442004 :         txn->size += sz;
 2121                          3404                 :        1442004 :         rb->size += sz;
                               3405                 :                : 
                               3406                 :                :         /* Update the total size in the top transaction. */
 1587                          3407                 :        1442004 :         toptxn->total_size += sz;
                               3408                 :                : 
                               3409                 :                :         /* Update the max-heap */
  513 msawada@postgresql.o     3410         [ +  + ]:        1442004 :         if (oldsize != 0)
                               3411                 :        1434731 :             pairingheap_remove(rb->txn_heap, &txn->txn_node);
                               3412                 :        1442004 :         pairingheap_add(rb->txn_heap, &txn->txn_node);
                               3413                 :                :     }
                               3414                 :                :     else
                               3415                 :                :     {
 1855 akapila@postgresql.o     3416   [ +  -  -  + ]:         180643 :         Assert((rb->size >= sz) && (txn->size >= sz));
                               3417                 :         180643 :         txn->size -= sz;
 2121                          3418                 :         180643 :         rb->size -= sz;
                               3419                 :                : 
                               3420                 :                :         /* Update the total size in the top transaction. */
 1587                          3421                 :         180643 :         toptxn->total_size -= sz;
                               3422                 :                : 
                               3423                 :                :         /* Update the max-heap */
  513 msawada@postgresql.o     3424                 :         180643 :         pairingheap_remove(rb->txn_heap, &txn->txn_node);
                               3425         [ +  + ]:         180643 :         if (txn->size != 0)
                               3426                 :         173407 :             pairingheap_add(rb->txn_heap, &txn->txn_node);
                               3427                 :                :     }
                               3428                 :                : 
 1855 akapila@postgresql.o     3429         [ -  + ]:        1622647 :     Assert(txn->size <= rb->size);
                               3430                 :                : }
                               3431                 :                : 
                               3432                 :                : /*
                               3433                 :                :  * Add new (relfilelocator, tid) -> (cmin, cmax) mappings.
                               3434                 :                :  *
                               3435                 :                :  * We do not include this change type in memory accounting, because we
                               3436                 :                :  * keep CIDs in a separate list and do not evict them when reaching
                               3437                 :                :  * the memory limit.
                               3438                 :                :  */
                               3439                 :                : void
 4205 rhaas@postgresql.org     3440                 :          24222 : ReorderBufferAddNewTupleCids(ReorderBuffer *rb, TransactionId xid,
                               3441                 :                :                              XLogRecPtr lsn, RelFileLocator locator,
                               3442                 :                :                              ItemPointerData tid, CommandId cmin,
                               3443                 :                :                              CommandId cmax, CommandId combocid)
                               3444                 :                : {
  178 heikki.linnakangas@i     3445                 :          24222 :     ReorderBufferChange *change = ReorderBufferAllocChange(rb);
                               3446                 :                :     ReorderBufferTXN *txn;
                               3447                 :                : 
 4205 rhaas@postgresql.org     3448                 :          24222 :     txn = ReorderBufferTXNByXid(rb, xid, true, NULL, lsn, true);
                               3449                 :                : 
 1158                          3450                 :          24222 :     change->data.tuplecid.locator = locator;
 4201 tgl@sss.pgh.pa.us        3451                 :          24222 :     change->data.tuplecid.tid = tid;
                               3452                 :          24222 :     change->data.tuplecid.cmin = cmin;
                               3453                 :          24222 :     change->data.tuplecid.cmax = cmax;
                               3454                 :          24222 :     change->data.tuplecid.combocid = combocid;
 4205 rhaas@postgresql.org     3455                 :          24222 :     change->lsn = lsn;
 2121 akapila@postgresql.o     3456                 :          24222 :     change->txn = txn;
 4201 tgl@sss.pgh.pa.us        3457                 :          24222 :     change->action = REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID;
                               3458                 :                : 
 4205 rhaas@postgresql.org     3459                 :          24222 :     dlist_push_tail(&txn->tuplecids, &change->node);
                               3460                 :          24222 :     txn->ntuplecids++;
                               3461                 :          24222 : }
                               3462                 :                : 
                               3463                 :                : /*
                               3464                 :                :  * Add new invalidation messages to the reorder buffer queue.
                               3465                 :                :  */
                               3466                 :                : static void
   82 msawada@postgresql.o     3467                 :           5143 : ReorderBufferQueueInvalidations(ReorderBuffer *rb, TransactionId xid,
                               3468                 :                :                                 XLogRecPtr lsn, Size nmsgs,
                               3469                 :                :                                 SharedInvalidationMessage *msgs)
                               3470                 :                : {
                               3471                 :                :     ReorderBufferChange *change;
                               3472                 :                : 
                               3473                 :           5143 :     change = ReorderBufferAllocChange(rb);
                               3474                 :           5143 :     change->action = REORDER_BUFFER_CHANGE_INVALIDATION;
                               3475                 :           5143 :     change->data.inval.ninvalidations = nmsgs;
                               3476                 :           5143 :     change->data.inval.invalidations = (SharedInvalidationMessage *)
                               3477                 :           5143 :         palloc(sizeof(SharedInvalidationMessage) * nmsgs);
                               3478                 :           5143 :     memcpy(change->data.inval.invalidations, msgs,
                               3479                 :                :            sizeof(SharedInvalidationMessage) * nmsgs);
                               3480                 :                : 
                               3481                 :           5143 :     ReorderBufferQueueChange(rb, xid, lsn, change, false);
                               3482                 :           5143 : }
                               3483                 :                : 
                               3484                 :                : /*
                               3485                 :                :  * A helper function for ReorderBufferAddInvalidations() and
                               3486                 :                :  * ReorderBufferAddDistributedInvalidations() to accumulate the invalidation
                               3487                 :                :  * messages to the **invals_out.
                               3488                 :                :  */
                               3489                 :                : static void
                               3490                 :           5143 : ReorderBufferAccumulateInvalidations(SharedInvalidationMessage **invals_out,
                               3491                 :                :                                      uint32 *ninvals_out,
                               3492                 :                :                                      SharedInvalidationMessage *msgs_new,
                               3493                 :                :                                      Size nmsgs_new)
                               3494                 :                : {
                               3495         [ +  + ]:           5143 :     if (*ninvals_out == 0)
                               3496                 :                :     {
                               3497                 :           1210 :         *ninvals_out = nmsgs_new;
                               3498                 :           1210 :         *invals_out = (SharedInvalidationMessage *)
                               3499                 :           1210 :             palloc(sizeof(SharedInvalidationMessage) * nmsgs_new);
                               3500                 :           1210 :         memcpy(*invals_out, msgs_new, sizeof(SharedInvalidationMessage) * nmsgs_new);
                               3501                 :                :     }
                               3502                 :                :     else
                               3503                 :                :     {
                               3504                 :                :         /* Enlarge the array of inval messages */
                               3505                 :           3933 :         *invals_out = (SharedInvalidationMessage *)
                               3506                 :           3933 :             repalloc(*invals_out, sizeof(SharedInvalidationMessage) *
                               3507                 :           3933 :                      (*ninvals_out + nmsgs_new));
                               3508                 :           3933 :         memcpy(*invals_out + *ninvals_out, msgs_new,
                               3509                 :                :                nmsgs_new * sizeof(SharedInvalidationMessage));
                               3510                 :           3933 :         *ninvals_out += nmsgs_new;
                               3511                 :                :     }
                               3512                 :           5143 : }
                               3513                 :                : 
                               3514                 :                : /*
                               3515                 :                :  * Accumulate the invalidations for executing them later.
                               3516                 :                :  *
                               3517                 :                :  * This needs to be called for each XLOG_XACT_INVALIDATIONS message and
                               3518                 :                :  * accumulates all the invalidation messages in the toplevel transaction, if
                               3519                 :                :  * available, otherwise in the current transaction, as well as in the form of
                               3520                 :                :  * change in reorder buffer.  We require to record it in form of the change
                               3521                 :                :  * so that we can execute only the required invalidations instead of executing
                               3522                 :                :  * all the invalidations on each CommandId increment.  We also need to
                               3523                 :                :  * accumulate these in the txn buffer because in some cases where we skip
                               3524                 :                :  * processing the transaction (see ReorderBufferForget), we need to execute
                               3525                 :                :  * all the invalidations together.
                               3526                 :                :  */
                               3527                 :                : void
 4205 rhaas@postgresql.org     3528                 :           5113 : ReorderBufferAddInvalidations(ReorderBuffer *rb, TransactionId xid,
                               3529                 :                :                               XLogRecPtr lsn, Size nmsgs,
                               3530                 :                :                               SharedInvalidationMessage *msgs)
                               3531                 :                : {
                               3532                 :                :     ReorderBufferTXN *txn;
                               3533                 :                :     MemoryContext oldcontext;
                               3534                 :                : 
                               3535                 :           5113 :     txn = ReorderBufferTXNByXid(rb, xid, true, NULL, lsn, true);
                               3536                 :                : 
 1787 akapila@postgresql.o     3537                 :           5113 :     oldcontext = MemoryContextSwitchTo(rb->context);
                               3538                 :                : 
                               3539                 :                :     /*
                               3540                 :                :      * Collect all the invalidations under the top transaction, if available,
                               3541                 :                :      * so that we can execute them all together.  See comments atop this
                               3542                 :                :      * function.
                               3543                 :                :      */
  904                          3544         [ +  + ]:           5113 :     txn = rbtxn_get_toptxn(txn);
                               3545                 :                : 
 4205 rhaas@postgresql.org     3546         [ -  + ]:           5113 :     Assert(nmsgs > 0);
                               3547                 :                : 
   82 msawada@postgresql.o     3548                 :           5113 :     ReorderBufferAccumulateInvalidations(&txn->invalidations,
                               3549                 :                :                                          &txn->ninvalidations,
                               3550                 :                :                                          msgs, nmsgs);
                               3551                 :                : 
                               3552                 :           5113 :     ReorderBufferQueueInvalidations(rb, xid, lsn, nmsgs, msgs);
                               3553                 :                : 
                               3554                 :           5113 :     MemoryContextSwitchTo(oldcontext);
                               3555                 :           5113 : }
                               3556                 :                : 
                               3557                 :                : /*
                               3558                 :                :  * Accumulate the invalidations distributed by other committed transactions
                               3559                 :                :  * for executing them later.
                               3560                 :                :  *
                               3561                 :                :  * This function is similar to ReorderBufferAddInvalidations() but stores
                               3562                 :                :  * the given inval messages to the txn->invalidations_distributed with the
                               3563                 :                :  * overflow check.
                               3564                 :                :  *
                               3565                 :                :  * This needs to be called by committed transactions to distribute their
                               3566                 :                :  * inval messages to in-progress transactions.
                               3567                 :                :  */
                               3568                 :                : void
                               3569                 :             30 : ReorderBufferAddDistributedInvalidations(ReorderBuffer *rb, TransactionId xid,
                               3570                 :                :                                          XLogRecPtr lsn, Size nmsgs,
                               3571                 :                :                                          SharedInvalidationMessage *msgs)
                               3572                 :                : {
                               3573                 :                :     ReorderBufferTXN *txn;
                               3574                 :                :     MemoryContext oldcontext;
                               3575                 :                : 
                               3576                 :             30 :     txn = ReorderBufferTXNByXid(rb, xid, true, NULL, lsn, true);
                               3577                 :                : 
                               3578                 :             30 :     oldcontext = MemoryContextSwitchTo(rb->context);
                               3579                 :                : 
                               3580                 :                :     /*
                               3581                 :                :      * Collect all the invalidations under the top transaction, if available,
                               3582                 :                :      * so that we can execute them all together.  See comments
                               3583                 :                :      * ReorderBufferAddInvalidations.
                               3584                 :                :      */
                               3585         [ -  + ]:             30 :     txn = rbtxn_get_toptxn(txn);
                               3586                 :                : 
                               3587         [ -  + ]:             30 :     Assert(nmsgs > 0);
                               3588                 :                : 
                               3589         [ +  - ]:             30 :     if (!rbtxn_distr_inval_overflowed(txn))
                               3590                 :                :     {
                               3591                 :                :         /*
                               3592                 :                :          * Check the transaction has enough space for storing distributed
                               3593                 :                :          * invalidation messages.
                               3594                 :                :          */
                               3595         [ -  + ]:             30 :         if (txn->ninvalidations_distributed + nmsgs >= MAX_DISTR_INVAL_MSG_PER_TXN)
                               3596                 :                :         {
                               3597                 :                :             /*
                               3598                 :                :              * Mark the invalidation message as overflowed and free up the
                               3599                 :                :              * messages accumulated so far.
                               3600                 :                :              */
   82 msawada@postgresql.o     3601                 :UBC           0 :             txn->txn_flags |= RBTXN_DISTR_INVAL_OVERFLOWED;
                               3602                 :                : 
                               3603         [ #  # ]:              0 :             if (txn->invalidations_distributed)
                               3604                 :                :             {
                               3605                 :              0 :                 pfree(txn->invalidations_distributed);
                               3606                 :              0 :                 txn->invalidations_distributed = NULL;
                               3607                 :              0 :                 txn->ninvalidations_distributed = 0;
                               3608                 :                :             }
                               3609                 :                :         }
                               3610                 :                :         else
   82 msawada@postgresql.o     3611                 :CBC          30 :             ReorderBufferAccumulateInvalidations(&txn->invalidations_distributed,
                               3612                 :                :                                                  &txn->ninvalidations_distributed,
                               3613                 :                :                                                  msgs, nmsgs);
                               3614                 :                :     }
                               3615                 :                : 
                               3616                 :                :     /* Queue the invalidation messages into the transaction */
                               3617                 :             30 :     ReorderBufferQueueInvalidations(rb, xid, lsn, nmsgs, msgs);
                               3618                 :                : 
 1787 akapila@postgresql.o     3619                 :             30 :     MemoryContextSwitchTo(oldcontext);
 4205 rhaas@postgresql.org     3620                 :             30 : }
                               3621                 :                : 
                               3622                 :                : /*
                               3623                 :                :  * Apply all invalidations we know. Possibly we only need parts at this point
                               3624                 :                :  * in the changestream but we don't know which those are.
                               3625                 :                :  */
                               3626                 :                : static void
 1787 akapila@postgresql.o     3627                 :           6510 : ReorderBufferExecuteInvalidations(uint32 nmsgs, SharedInvalidationMessage *msgs)
                               3628                 :                : {
                               3629                 :                :     int         i;
                               3630                 :                : 
                               3631         [ +  + ]:          49004 :     for (i = 0; i < nmsgs; i++)
                               3632                 :          42494 :         LocalExecuteInvalidationMessage(&msgs[i]);
 4205 rhaas@postgresql.org     3633                 :           6510 : }
                               3634                 :                : 
                               3635                 :                : /*
                               3636                 :                :  * Mark a transaction as containing catalog changes
                               3637                 :                :  */
                               3638                 :                : void
                               3639                 :          29368 : ReorderBufferXidSetCatalogChanges(ReorderBuffer *rb, TransactionId xid,
                               3640                 :                :                                   XLogRecPtr lsn)
                               3641                 :                : {
                               3642                 :                :     ReorderBufferTXN *txn;
                               3643                 :                : 
                               3644                 :          29368 :     txn = ReorderBufferTXNByXid(rb, xid, true, NULL, lsn, true);
                               3645                 :                : 
 1122 akapila@postgresql.o     3646         [ +  + ]:          29368 :     if (!rbtxn_has_catalog_changes(txn))
                               3647                 :                :     {
                               3648                 :           1218 :         txn->txn_flags |= RBTXN_HAS_CATALOG_CHANGES;
 1039 drowley@postgresql.o     3649                 :           1218 :         dclist_push_tail(&rb->catchange_txns, &txn->catchange_node);
                               3650                 :                :     }
                               3651                 :                : 
                               3652                 :                :     /*
                               3653                 :                :      * Mark top-level transaction as having catalog changes too if one of its
                               3654                 :                :      * children has so that the ReorderBufferBuildTupleCidHash can
                               3655                 :                :      * conveniently check just top-level transaction and decide whether to
                               3656                 :                :      * build the hash table or not.
                               3657                 :                :      */
  904 akapila@postgresql.o     3658         [ +  + ]:          29368 :     if (rbtxn_is_subtxn(txn))
                               3659                 :                :     {
                               3660         [ +  - ]:            896 :         ReorderBufferTXN *toptxn = rbtxn_get_toptxn(txn);
                               3661                 :                : 
                               3662         [ +  + ]:            896 :         if (!rbtxn_has_catalog_changes(toptxn))
                               3663                 :                :         {
                               3664                 :             20 :             toptxn->txn_flags |= RBTXN_HAS_CATALOG_CHANGES;
                               3665                 :             20 :             dclist_push_tail(&rb->catchange_txns, &toptxn->catchange_node);
                               3666                 :                :         }
                               3667                 :                :     }
 1122                          3668                 :          29368 : }
                               3669                 :                : 
                               3670                 :                : /*
                               3671                 :                :  * Return palloc'ed array of the transactions that have changed catalogs.
                               3672                 :                :  * The returned array is sorted in xidComparator order.
                               3673                 :                :  *
                               3674                 :                :  * The caller must free the returned array when done with it.
                               3675                 :                :  */
                               3676                 :                : TransactionId *
                               3677                 :            282 : ReorderBufferGetCatalogChangesXacts(ReorderBuffer *rb)
                               3678                 :                : {
                               3679                 :                :     dlist_iter  iter;
                               3680                 :            282 :     TransactionId *xids = NULL;
                               3681                 :            282 :     size_t      xcnt = 0;
                               3682                 :                : 
                               3683                 :                :     /* Quick return if the list is empty */
 1039 drowley@postgresql.o     3684         [ +  + ]:            282 :     if (dclist_count(&rb->catchange_txns) == 0)
 1122 akapila@postgresql.o     3685                 :            274 :         return NULL;
                               3686                 :                : 
                               3687                 :                :     /* Initialize XID array */
 1039 drowley@postgresql.o     3688                 :              8 :     xids = (TransactionId *) palloc(sizeof(TransactionId) *
                               3689                 :              8 :                                     dclist_count(&rb->catchange_txns));
                               3690   [ +  -  +  + ]:             19 :     dclist_foreach(iter, &rb->catchange_txns)
                               3691                 :                :     {
                               3692                 :             11 :         ReorderBufferTXN *txn = dclist_container(ReorderBufferTXN,
                               3693                 :                :                                                  catchange_node,
                               3694                 :                :                                                  iter.cur);
                               3695                 :                : 
 1122 akapila@postgresql.o     3696         [ -  + ]:             11 :         Assert(rbtxn_has_catalog_changes(txn));
                               3697                 :                : 
                               3698                 :             11 :         xids[xcnt++] = txn->xid;
                               3699                 :                :     }
                               3700                 :                : 
                               3701                 :              8 :     qsort(xids, xcnt, sizeof(TransactionId), xidComparator);
                               3702                 :                : 
 1039 drowley@postgresql.o     3703         [ -  + ]:              8 :     Assert(xcnt == dclist_count(&rb->catchange_txns));
 1122 akapila@postgresql.o     3704                 :              8 :     return xids;
                               3705                 :                : }
                               3706                 :                : 
                               3707                 :                : /*
                               3708                 :                :  * Query whether a transaction is already *known* to contain catalog
                               3709                 :                :  * changes. This can be wrong until directly before the commit!
                               3710                 :                :  */
                               3711                 :                : bool
 4205 rhaas@postgresql.org     3712                 :           4246 : ReorderBufferXidHasCatalogChanges(ReorderBuffer *rb, TransactionId xid)
                               3713                 :                : {
                               3714                 :                :     ReorderBufferTXN *txn;
                               3715                 :                : 
                               3716                 :           4246 :     txn = ReorderBufferTXNByXid(rb, xid, false, NULL, InvalidXLogRecPtr,
                               3717                 :                :                                 false);
                               3718         [ +  + ]:           4246 :     if (txn == NULL)
                               3719                 :            651 :         return false;
                               3720                 :                : 
 2066 alvherre@alvh.no-ip.     3721                 :           3595 :     return rbtxn_has_catalog_changes(txn);
                               3722                 :                : }
                               3723                 :                : 
                               3724                 :                : /*
                               3725                 :                :  * ReorderBufferXidHasBaseSnapshot
                               3726                 :                :  *      Have we already set the base snapshot for the given txn/subtxn?
                               3727                 :                :  */
                               3728                 :                : bool
 4205 rhaas@postgresql.org     3729                 :        1258860 : ReorderBufferXidHasBaseSnapshot(ReorderBuffer *rb, TransactionId xid)
                               3730                 :                : {
                               3731                 :                :     ReorderBufferTXN *txn;
                               3732                 :                : 
 2629 alvherre@alvh.no-ip.     3733                 :        1258860 :     txn = ReorderBufferTXNByXid(rb, xid, false,
                               3734                 :                :                                 NULL, InvalidXLogRecPtr, false);
                               3735                 :                : 
                               3736                 :                :     /* transaction isn't known yet, ergo no snapshot */
 4205 rhaas@postgresql.org     3737         [ +  + ]:        1258860 :     if (txn == NULL)
                               3738                 :              3 :         return false;
                               3739                 :                : 
                               3740                 :                :     /* a known subtxn? operate on top-level txn instead */
 2066 alvherre@alvh.no-ip.     3741         [ +  + ]:        1258857 :     if (rbtxn_is_known_subxact(txn))
 2629                          3742                 :         332024 :         txn = ReorderBufferTXNByXid(rb, txn->toplevel_xid, false,
                               3743                 :                :                                     NULL, InvalidXLogRecPtr, false);
                               3744                 :                : 
 4205 rhaas@postgresql.org     3745                 :        1258857 :     return txn->base_snapshot != NULL;
                               3746                 :                : }
                               3747                 :                : 
                               3748                 :                : 
                               3749                 :                : /*
                               3750                 :                :  * ---------------------------------------
                               3751                 :                :  * Disk serialization support
                               3752                 :                :  * ---------------------------------------
                               3753                 :                :  */
                               3754                 :                : 
                               3755                 :                : /*
                               3756                 :                :  * Ensure the IO buffer is >= sz.
                               3757                 :                :  */
                               3758                 :                : static void
                               3759                 :        2405191 : ReorderBufferSerializeReserve(ReorderBuffer *rb, Size sz)
                               3760                 :                : {
                               3761         [ +  + ]:        2405191 :     if (!rb->outbufsize)
                               3762                 :                :     {
                               3763                 :             46 :         rb->outbuf = MemoryContextAlloc(rb->context, sz);
                               3764                 :             46 :         rb->outbufsize = sz;
                               3765                 :                :     }
                               3766         [ +  + ]:        2405145 :     else if (rb->outbufsize < sz)
                               3767                 :                :     {
                               3768                 :            264 :         rb->outbuf = repalloc(rb->outbuf, sz);
                               3769                 :            264 :         rb->outbufsize = sz;
                               3770                 :                :     }
                               3771                 :        2405191 : }
                               3772                 :                : 
                               3773                 :                : 
                               3774                 :                : /* Compare two transactions by size */
                               3775                 :                : static int
  513 msawada@postgresql.o     3776                 :         275314 : ReorderBufferTXNSizeCompare(const pairingheap_node *a, const pairingheap_node *b, void *arg)
                               3777                 :                : {
                               3778                 :         275314 :     const ReorderBufferTXN *ta = pairingheap_const_container(ReorderBufferTXN, txn_node, a);
                               3779                 :         275314 :     const ReorderBufferTXN *tb = pairingheap_const_container(ReorderBufferTXN, txn_node, b);
                               3780                 :                : 
  521                          3781         [ +  + ]:         275314 :     if (ta->size < tb->size)
                               3782                 :         192830 :         return -1;
                               3783         [ +  + ]:          82484 :     if (ta->size > tb->size)
                               3784                 :          81714 :         return 1;
                               3785                 :            770 :     return 0;
                               3786                 :                : }
                               3787                 :                : 
                               3788                 :                : /*
                               3789                 :                :  * Find the largest transaction (toplevel or subxact) to evict (spill to disk).
                               3790                 :                :  */
                               3791                 :                : static ReorderBufferTXN *
                               3792                 :           3081 : ReorderBufferLargestTXN(ReorderBuffer *rb)
                               3793                 :                : {
                               3794                 :                :     ReorderBufferTXN *largest;
                               3795                 :                : 
                               3796                 :                :     /* Get the largest transaction from the max-heap */
  513                          3797                 :           3081 :     largest = pairingheap_container(ReorderBufferTXN, txn_node,
                               3798                 :                :                                     pairingheap_first(rb->txn_heap));
                               3799                 :                : 
 2121 akapila@postgresql.o     3800         [ -  + ]:           3081 :     Assert(largest);
                               3801         [ -  + ]:           3081 :     Assert(largest->size > 0);
                               3802         [ -  + ]:           3081 :     Assert(largest->size <= rb->size);
                               3803                 :                : 
                               3804                 :           3081 :     return largest;
                               3805                 :                : }
                               3806                 :                : 
                               3807                 :                : /*
                               3808                 :                :  * Find the largest streamable (and non-aborted) toplevel transaction to evict
                               3809                 :                :  * (by streaming).
                               3810                 :                :  *
                               3811                 :                :  * This can be seen as an optimized version of ReorderBufferLargestTXN, which
                               3812                 :                :  * should give us the same transaction (because we don't update memory account
                               3813                 :                :  * for subtransaction with streaming, so it's always 0). But we can simply
                               3814                 :                :  * iterate over the limited number of toplevel transactions that have a base
                               3815                 :                :  * snapshot. There is no use of selecting a transaction that doesn't have base
                               3816                 :                :  * snapshot because we don't decode such transactions.  Also, we do not select
                               3817                 :                :  * the transaction which doesn't have any streamable change.
                               3818                 :                :  *
                               3819                 :                :  * Note that, we skip transactions that contain incomplete changes. There
                               3820                 :                :  * is a scope of optimization here such that we can select the largest
                               3821                 :                :  * transaction which has incomplete changes.  But that will make the code and
                               3822                 :                :  * design quite complex and that might not be worth the benefit.  If we plan to
                               3823                 :                :  * stream the transactions that contain incomplete changes then we need to
                               3824                 :                :  * find a way to partially stream/truncate the transaction changes in-memory
                               3825                 :                :  * and build a mechanism to partially truncate the spilled files.
                               3826                 :                :  * Additionally, whenever we partially stream the transaction we need to
                               3827                 :                :  * maintain the last streamed lsn and next time we need to restore from that
                               3828                 :                :  * segment and the offset in WAL.  As we stream the changes from the top
                               3829                 :                :  * transaction and restore them subtransaction wise, we need to even remember
                               3830                 :                :  * the subxact from where we streamed the last change.
                               3831                 :                :  */
                               3832                 :                : static ReorderBufferTXN *
 1003                          3833                 :            828 : ReorderBufferLargestStreamableTopTXN(ReorderBuffer *rb)
                               3834                 :                : {
                               3835                 :                :     dlist_iter  iter;
 1855                          3836                 :            828 :     Size        largest_size = 0;
                               3837                 :            828 :     ReorderBufferTXN *largest = NULL;
                               3838                 :                : 
                               3839                 :                :     /* Find the largest top-level transaction having a base snapshot. */
 1590                          3840   [ +  -  +  + ]:           1768 :     dlist_foreach(iter, &rb->txns_by_base_snapshot_lsn)
                               3841                 :                :     {
                               3842                 :                :         ReorderBufferTXN *txn;
                               3843                 :                : 
                               3844                 :            940 :         txn = dlist_container(ReorderBufferTXN, base_snapshot_node, iter.cur);
                               3845                 :                : 
                               3846                 :                :         /* must not be a subtxn */
                               3847         [ -  + ]:            940 :         Assert(!rbtxn_is_known_subxact(txn));
                               3848                 :                :         /* base_snapshot must be set */
                               3849         [ -  + ]:            940 :         Assert(txn->base_snapshot != NULL);
                               3850                 :                : 
                               3851                 :                :         /* Don't consider these kinds of transactions for eviction. */
  206 msawada@postgresql.o     3852         [ +  + ]:            940 :         if (rbtxn_has_partial_change(txn) ||
                               3853         [ +  + ]:            793 :             !rbtxn_has_streamable_change(txn) ||
                               3854         [ -  + ]:            763 :             rbtxn_is_aborted(txn))
                               3855                 :            177 :             continue;
                               3856                 :                : 
                               3857                 :                :         /* Find the largest of the eviction candidates. */
 1590 akapila@postgresql.o     3858   [ +  +  +  - ]:            763 :         if ((largest == NULL || txn->total_size > largest_size) &&
  206 msawada@postgresql.o     3859         [ +  + ]:            763 :             (txn->total_size > 0))
                               3860                 :                :         {
 1855 akapila@postgresql.o     3861                 :            717 :             largest = txn;
                               3862                 :            717 :             largest_size = txn->total_size;
                               3863                 :                :         }
                               3864                 :                :     }
                               3865                 :                : 
                               3866                 :            828 :     return largest;
                               3867                 :                : }
                               3868                 :                : 
                               3869                 :                : /*
                               3870                 :                :  * Check whether the logical_decoding_work_mem limit was reached, and if yes
                               3871                 :                :  * pick the largest (sub)transaction at-a-time to evict and spill its changes to
                               3872                 :                :  * disk or send to the output plugin until we reach under the memory limit.
                               3873                 :                :  *
                               3874                 :                :  * If debug_logical_replication_streaming is set to "immediate", stream or
                               3875                 :                :  * serialize the changes immediately.
                               3876                 :                :  *
                               3877                 :                :  * XXX At this point we select the transactions until we reach under the memory
                               3878                 :                :  * limit, but we might also adapt a more elaborate eviction strategy - for example
                               3879                 :                :  * evicting enough transactions to free certain fraction (e.g. 50%) of the memory
                               3880                 :                :  * limit.
                               3881                 :                :  */
                               3882                 :                : static void
 2121                          3883                 :        1268582 : ReorderBufferCheckMemoryLimit(ReorderBuffer *rb)
                               3884                 :                : {
                               3885                 :                :     ReorderBufferTXN *txn;
                               3886                 :                : 
                               3887                 :                :     /*
                               3888                 :                :      * Bail out if debug_logical_replication_streaming is buffered and we
                               3889                 :                :      * haven't exceeded the memory limit.
                               3890                 :                :      */
  739 peter@eisentraut.org     3891         [ +  + ]:        1268582 :     if (debug_logical_replication_streaming == DEBUG_LOGICAL_REP_STREAMING_BUFFERED &&
  218 tgl@sss.pgh.pa.us        3892         [ +  + ]:        1267617 :         rb->size < logical_decoding_work_mem * (Size) 1024)
 2121 akapila@postgresql.o     3893                 :        1264847 :         return;
                               3894                 :                : 
                               3895                 :                :     /*
                               3896                 :                :      * If debug_logical_replication_streaming is immediate, loop until there's
                               3897                 :                :      * no change. Otherwise, loop until we reach under the memory limit. One
                               3898                 :                :      * might think that just by evicting the largest (sub)transaction we will
                               3899                 :                :      * come under the memory limit based on assumption that the selected
                               3900                 :                :      * transaction is at least as large as the most recent change (which
                               3901                 :                :      * caused us to go over the memory limit). However, that is not true
                               3902                 :                :      * because a user can reduce the logical_decoding_work_mem to a smaller
                               3903                 :                :      * value before the most recent change.
                               3904                 :                :      */
  218 tgl@sss.pgh.pa.us        3905         [ +  + ]:           7467 :     while (rb->size >= logical_decoding_work_mem * (Size) 1024 ||
  739 peter@eisentraut.org     3906         [ +  + ]:           4697 :            (debug_logical_replication_streaming == DEBUG_LOGICAL_REP_STREAMING_IMMEDIATE &&
  985 akapila@postgresql.o     3907         [ +  + ]:           1927 :             rb->size > 0))
                               3908                 :                :     {
                               3909                 :                :         /*
                               3910                 :                :          * Pick the largest non-aborted transaction and evict it from memory
                               3911                 :                :          * by streaming, if possible.  Otherwise, spill to disk.
                               3912                 :                :          */
 1855                          3913   [ +  +  +  + ]:           4560 :         if (ReorderBufferCanStartStreaming(rb) &&
 1003                          3914                 :            828 :             (txn = ReorderBufferLargestStreamableTopTXN(rb)) != NULL)
                               3915                 :                :         {
                               3916                 :                :             /* we know there has to be one, because the size is not zero */
  904                          3917   [ +  -  -  + ]:            651 :             Assert(txn && rbtxn_is_toptxn(txn));
 1855                          3918         [ -  + ]:            651 :             Assert(txn->total_size > 0);
                               3919         [ -  + ]:            651 :             Assert(rb->size >= txn->total_size);
                               3920                 :                : 
                               3921                 :                :             /* skip the transaction if aborted */
  206 msawada@postgresql.o     3922         [ -  + ]:            651 :             if (ReorderBufferCheckAndTruncateAbortedTXN(rb, txn))
  206 msawada@postgresql.o     3923                 :UBC           0 :                 continue;
                               3924                 :                : 
 1855 akapila@postgresql.o     3925                 :CBC         651 :             ReorderBufferStreamTXN(rb, txn);
                               3926                 :                :         }
                               3927                 :                :         else
                               3928                 :                :         {
                               3929                 :                :             /*
                               3930                 :                :              * Pick the largest transaction (or subtransaction) and evict it
                               3931                 :                :              * from memory by serializing it to disk.
                               3932                 :                :              */
                               3933                 :           3081 :             txn = ReorderBufferLargestTXN(rb);
                               3934                 :                : 
                               3935                 :                :             /* we know there has to be one, because the size is not zero */
                               3936         [ -  + ]:           3081 :             Assert(txn);
                               3937         [ -  + ]:           3081 :             Assert(txn->size > 0);
                               3938         [ -  + ]:           3081 :             Assert(rb->size >= txn->size);
                               3939                 :                : 
                               3940                 :                :             /* skip the transaction if aborted */
  206 msawada@postgresql.o     3941         [ +  + ]:           3081 :             if (ReorderBufferCheckAndTruncateAbortedTXN(rb, txn))
                               3942                 :              9 :                 continue;
                               3943                 :                : 
 1855 akapila@postgresql.o     3944                 :           3072 :             ReorderBufferSerializeTXN(rb, txn);
                               3945                 :                :         }
                               3946                 :                : 
                               3947                 :                :         /*
                               3948                 :                :          * After eviction, the transaction should have no entries in memory,
                               3949                 :                :          * and should use 0 bytes for changes.
                               3950                 :                :          */
 1914                          3951         [ -  + ]:           3723 :         Assert(txn->size == 0);
                               3952         [ -  + ]:           3723 :         Assert(txn->nentries_mem == 0);
                               3953                 :                :     }
                               3954                 :                : 
                               3955                 :                :     /* We must be under the memory limit now. */
  218 tgl@sss.pgh.pa.us        3956         [ -  + ]:           3735 :     Assert(rb->size < logical_decoding_work_mem * (Size) 1024);
                               3957                 :                : }
                               3958                 :                : 
                               3959                 :                : /*
                               3960                 :                :  * Spill data of a large transaction (and its subtransactions) to disk.
                               3961                 :                :  */
                               3962                 :                : static void
 4205 rhaas@postgresql.org     3963                 :           3284 : ReorderBufferSerializeTXN(ReorderBuffer *rb, ReorderBufferTXN *txn)
                               3964                 :                : {
                               3965                 :                :     dlist_iter  subtxn_i;
                               3966                 :                :     dlist_mutable_iter change_i;
                               3967                 :           3284 :     int         fd = -1;
                               3968                 :           3284 :     XLogSegNo   curOpenSegNo = 0;
                               3969                 :           3284 :     Size        spilled = 0;
 1794 akapila@postgresql.o     3970                 :           3284 :     Size        size = txn->size;
                               3971                 :                : 
 4147 tgl@sss.pgh.pa.us        3972         [ -  + ]:           3284 :     elog(DEBUG2, "spill %u changes in XID %u to disk",
                               3973                 :                :          (uint32) txn->nentries_mem, txn->xid);
                               3974                 :                : 
                               3975                 :                :     /* do the same to all child TXs */
 4205 rhaas@postgresql.org     3976   [ +  -  +  + ]:           3457 :     dlist_foreach(subtxn_i, &txn->subtxns)
                               3977                 :                :     {
                               3978                 :                :         ReorderBufferTXN *subtxn;
                               3979                 :                : 
                               3980                 :            173 :         subtxn = dlist_container(ReorderBufferTXN, node, subtxn_i.cur);
                               3981                 :            173 :         ReorderBufferSerializeTXN(rb, subtxn);
                               3982                 :                :     }
                               3983                 :                : 
                               3984                 :                :     /* serialize changestream */
                               3985   [ +  -  +  + ]:        1041338 :     dlist_foreach_modify(change_i, &txn->changes)
                               3986                 :                :     {
                               3987                 :                :         ReorderBufferChange *change;
                               3988                 :                : 
                               3989                 :        1038054 :         change = dlist_container(ReorderBufferChange, node, change_i.cur);
                               3990                 :                : 
                               3991                 :                :         /*
                               3992                 :                :          * store in segment in which it belongs by start lsn, don't split over
                               3993                 :                :          * multiple segments tho
                               3994                 :                :          */
 2909 andres@anarazel.de       3995         [ +  + ]:        1038054 :         if (fd == -1 ||
                               3996         [ +  + ]:        1034934 :             !XLByteInSeg(change->lsn, curOpenSegNo, wal_segment_size))
                               3997                 :                :         {
                               3998                 :                :             char        path[MAXPGPATH];
                               3999                 :                : 
 4205 rhaas@postgresql.org     4000         [ +  + ]:           3136 :             if (fd != -1)
                               4001                 :             16 :                 CloseTransientFile(fd);
                               4002                 :                : 
 2909 andres@anarazel.de       4003                 :           3136 :             XLByteToSeg(change->lsn, curOpenSegNo, wal_segment_size);
                               4004                 :                : 
                               4005                 :                :             /*
                               4006                 :                :              * No need to care about TLIs here, only used during a single run,
                               4007                 :                :              * so each LSN only maps to a specific WAL record.
                               4008                 :                :              */
 2741 alvherre@alvh.no-ip.     4009                 :           3136 :             ReorderBufferSerializedPath(path, MyReplicationSlot, txn->xid,
                               4010                 :                :                                         curOpenSegNo);
                               4011                 :                : 
                               4012                 :                :             /* open segment, create it if necessary */
 4205 rhaas@postgresql.org     4013                 :           3136 :             fd = OpenTransientFile(path,
                               4014                 :                :                                    O_CREAT | O_WRONLY | O_APPEND | PG_BINARY);
                               4015                 :                : 
                               4016         [ -  + ]:           3136 :             if (fd < 0)
 4205 rhaas@postgresql.org     4017         [ #  # ]:UBC           0 :                 ereport(ERROR,
                               4018                 :                :                         (errcode_for_file_access(),
                               4019                 :                :                          errmsg("could not open file \"%s\": %m", path)));
                               4020                 :                :         }
                               4021                 :                : 
 4205 rhaas@postgresql.org     4022                 :CBC     1038054 :         ReorderBufferSerializeChange(rb, txn, fd, change);
                               4023                 :        1038054 :         dlist_delete(&change->node);
  178 heikki.linnakangas@i     4024                 :        1038054 :         ReorderBufferFreeChange(rb, change, false);
                               4025                 :                : 
 4205 rhaas@postgresql.org     4026                 :        1038054 :         spilled++;
                               4027                 :                :     }
                               4028                 :                : 
                               4029                 :                :     /* Update the memory counter */
  521 msawada@postgresql.o     4030                 :           3284 :     ReorderBufferChangeMemoryUpdate(rb, NULL, txn, false, size);
                               4031                 :                : 
                               4032                 :                :     /* update the statistics iff we have spilled anything */
 1794 akapila@postgresql.o     4033         [ +  + ]:           3284 :     if (spilled)
                               4034                 :                :     {
                               4035                 :           3120 :         rb->spillCount += 1;
                               4036                 :           3120 :         rb->spillBytes += size;
                               4037                 :                : 
                               4038                 :                :         /* don't consider already serialized transactions */
                               4039   [ +  +  +  - ]:           3120 :         rb->spillTxns += (rbtxn_is_serialized(txn) || rbtxn_is_serialized_clear(txn)) ? 0 : 1;
                               4040                 :                : 
                               4041                 :                :         /* update the decoding stats */
 1584                          4042                 :           3120 :         UpdateDecodingStats((LogicalDecodingContext *) rb->private_data);
                               4043                 :                :     }
                               4044                 :                : 
 4205 rhaas@postgresql.org     4045         [ -  + ]:           3284 :     Assert(spilled == txn->nentries_mem);
                               4046         [ -  + ]:           3284 :     Assert(dlist_is_empty(&txn->changes));
                               4047                 :           3284 :     txn->nentries_mem = 0;
 2066 alvherre@alvh.no-ip.     4048                 :           3284 :     txn->txn_flags |= RBTXN_IS_SERIALIZED;
                               4049                 :                : 
 4205 rhaas@postgresql.org     4050         [ +  + ]:           3284 :     if (fd != -1)
                               4051                 :           3120 :         CloseTransientFile(fd);
                               4052                 :           3284 : }
                               4053                 :                : 
                               4054                 :                : /*
                               4055                 :                :  * Serialize individual change to disk.
                               4056                 :                :  */
                               4057                 :                : static void
                               4058                 :        1038054 : ReorderBufferSerializeChange(ReorderBuffer *rb, ReorderBufferTXN *txn,
                               4059                 :                :                              int fd, ReorderBufferChange *change)
                               4060                 :                : {
                               4061                 :                :     ReorderBufferDiskChange *ondisk;
                               4062                 :        1038054 :     Size        sz = sizeof(ReorderBufferDiskChange);
                               4063                 :                : 
                               4064                 :        1038054 :     ReorderBufferSerializeReserve(rb, sz);
                               4065                 :                : 
                               4066                 :        1038054 :     ondisk = (ReorderBufferDiskChange *) rb->outbuf;
                               4067                 :        1038054 :     memcpy(&ondisk->change, change, sizeof(ReorderBufferChange));
                               4068                 :                : 
 4201 tgl@sss.pgh.pa.us        4069   [ +  +  +  +  :        1038054 :     switch (change->action)
                                           +  +  - ]
                               4070                 :                :     {
                               4071                 :                :             /* fall through these, they're all similar enough */
                               4072                 :        1020566 :         case REORDER_BUFFER_CHANGE_INSERT:
                               4073                 :                :         case REORDER_BUFFER_CHANGE_UPDATE:
                               4074                 :                :         case REORDER_BUFFER_CHANGE_DELETE:
                               4075                 :                :         case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_INSERT:
                               4076                 :                :             {
                               4077                 :                :                 char       *data;
                               4078                 :                :                 HeapTuple   oldtup,
                               4079                 :                :                             newtup;
 4205 rhaas@postgresql.org     4080                 :        1020566 :                 Size        oldlen = 0;
                               4081                 :        1020566 :                 Size        newlen = 0;
                               4082                 :                : 
 4201 tgl@sss.pgh.pa.us        4083                 :        1020566 :                 oldtup = change->data.tp.oldtuple;
                               4084                 :        1020566 :                 newtup = change->data.tp.newtuple;
                               4085                 :                : 
                               4086         [ +  + ]:        1020566 :                 if (oldtup)
                               4087                 :                :                 {
 3472 andres@anarazel.de       4088                 :          97001 :                     sz += sizeof(HeapTupleData);
  586 msawada@postgresql.o     4089                 :          97001 :                     oldlen = oldtup->t_len;
 3472 andres@anarazel.de       4090                 :          97001 :                     sz += oldlen;
                               4091                 :                :                 }
                               4092                 :                : 
 4201 tgl@sss.pgh.pa.us        4093         [ +  + ]:        1020566 :                 if (newtup)
                               4094                 :                :                 {
 3472 andres@anarazel.de       4095                 :         869850 :                     sz += sizeof(HeapTupleData);
  586 msawada@postgresql.o     4096                 :         869850 :                     newlen = newtup->t_len;
 3472 andres@anarazel.de       4097                 :         869850 :                     sz += newlen;
                               4098                 :                :                 }
                               4099                 :                : 
                               4100                 :                :                 /* make sure we have enough space */
 4205 rhaas@postgresql.org     4101                 :        1020566 :                 ReorderBufferSerializeReserve(rb, sz);
                               4102                 :                : 
                               4103                 :        1020566 :                 data = ((char *) rb->outbuf) + sizeof(ReorderBufferDiskChange);
                               4104                 :                :                 /* might have been reallocated above */
                               4105                 :        1020566 :                 ondisk = (ReorderBufferDiskChange *) rb->outbuf;
                               4106                 :                : 
                               4107         [ +  + ]:        1020566 :                 if (oldlen)
                               4108                 :                :                 {
  586 msawada@postgresql.o     4109                 :          97001 :                     memcpy(data, oldtup, sizeof(HeapTupleData));
 3472 andres@anarazel.de       4110                 :          97001 :                     data += sizeof(HeapTupleData);
                               4111                 :                : 
  586 msawada@postgresql.o     4112                 :          97001 :                     memcpy(data, oldtup->t_data, oldlen);
 4205 rhaas@postgresql.org     4113                 :          97001 :                     data += oldlen;
                               4114                 :                :                 }
                               4115                 :                : 
                               4116         [ +  + ]:        1020566 :                 if (newlen)
                               4117                 :                :                 {
  586 msawada@postgresql.o     4118                 :         869850 :                     memcpy(data, newtup, sizeof(HeapTupleData));
 3472 andres@anarazel.de       4119                 :         869850 :                     data += sizeof(HeapTupleData);
                               4120                 :                : 
  586 msawada@postgresql.o     4121                 :         869850 :                     memcpy(data, newtup->t_data, newlen);
 3470 andres@anarazel.de       4122                 :         869850 :                     data += newlen;
                               4123                 :                :                 }
 3440 simon@2ndQuadrant.co     4124                 :        1020566 :                 break;
                               4125                 :                :             }
                               4126                 :             13 :         case REORDER_BUFFER_CHANGE_MESSAGE:
                               4127                 :                :             {
                               4128                 :                :                 char       *data;
                               4129                 :             13 :                 Size        prefix_size = strlen(change->data.msg.prefix) + 1;
                               4130                 :                : 
                               4131                 :             13 :                 sz += prefix_size + change->data.msg.message_size +
                               4132                 :                :                     sizeof(Size) + sizeof(Size);
                               4133                 :             13 :                 ReorderBufferSerializeReserve(rb, sz);
                               4134                 :                : 
                               4135                 :             13 :                 data = ((char *) rb->outbuf) + sizeof(ReorderBufferDiskChange);
                               4136                 :                : 
                               4137                 :                :                 /* might have been reallocated above */
 3265 rhaas@postgresql.org     4138                 :             13 :                 ondisk = (ReorderBufferDiskChange *) rb->outbuf;
                               4139                 :                : 
                               4140                 :                :                 /* write the prefix including the size */
 3440 simon@2ndQuadrant.co     4141                 :             13 :                 memcpy(data, &prefix_size, sizeof(Size));
                               4142                 :             13 :                 data += sizeof(Size);
                               4143                 :             13 :                 memcpy(data, change->data.msg.prefix,
                               4144                 :                :                        prefix_size);
                               4145                 :             13 :                 data += prefix_size;
                               4146                 :                : 
                               4147                 :                :                 /* write the message including the size */
                               4148                 :             13 :                 memcpy(data, &change->data.msg.message_size, sizeof(Size));
                               4149                 :             13 :                 data += sizeof(Size);
                               4150                 :             13 :                 memcpy(data, change->data.msg.message,
                               4151                 :                :                        change->data.msg.message_size);
                               4152                 :             13 :                 data += change->data.msg.message_size;
                               4153                 :                : 
 1787 akapila@postgresql.o     4154                 :             13 :                 break;
                               4155                 :                :             }
                               4156                 :            154 :         case REORDER_BUFFER_CHANGE_INVALIDATION:
                               4157                 :                :             {
                               4158                 :                :                 char       *data;
                               4159                 :            154 :                 Size        inval_size = sizeof(SharedInvalidationMessage) *
  841 tgl@sss.pgh.pa.us        4160                 :            154 :                     change->data.inval.ninvalidations;
                               4161                 :                : 
 1787 akapila@postgresql.o     4162                 :            154 :                 sz += inval_size;
                               4163                 :                : 
                               4164                 :            154 :                 ReorderBufferSerializeReserve(rb, sz);
                               4165                 :            154 :                 data = ((char *) rb->outbuf) + sizeof(ReorderBufferDiskChange);
                               4166                 :                : 
                               4167                 :                :                 /* might have been reallocated above */
                               4168                 :            154 :                 ondisk = (ReorderBufferDiskChange *) rb->outbuf;
                               4169                 :            154 :                 memcpy(data, change->data.inval.invalidations, inval_size);
                               4170                 :            154 :                 data += inval_size;
                               4171                 :                : 
 4205 rhaas@postgresql.org     4172                 :            154 :                 break;
                               4173                 :                :             }
                               4174                 :              8 :         case REORDER_BUFFER_CHANGE_INTERNAL_SNAPSHOT:
                               4175                 :                :             {
                               4176                 :                :                 Snapshot    snap;
                               4177                 :                :                 char       *data;
                               4178                 :                : 
 4201 tgl@sss.pgh.pa.us        4179                 :              8 :                 snap = change->data.snapshot;
                               4180                 :                : 
 4205 rhaas@postgresql.org     4181                 :              8 :                 sz += sizeof(SnapshotData) +
 4201 tgl@sss.pgh.pa.us        4182                 :              8 :                     sizeof(TransactionId) * snap->xcnt +
 2059 alvherre@alvh.no-ip.     4183                 :              8 :                     sizeof(TransactionId) * snap->subxcnt;
                               4184                 :                : 
                               4185                 :                :                 /* make sure we have enough space */
 4205 rhaas@postgresql.org     4186                 :              8 :                 ReorderBufferSerializeReserve(rb, sz);
                               4187                 :              8 :                 data = ((char *) rb->outbuf) + sizeof(ReorderBufferDiskChange);
                               4188                 :                :                 /* might have been reallocated above */
                               4189                 :              8 :                 ondisk = (ReorderBufferDiskChange *) rb->outbuf;
                               4190                 :                : 
 4201 tgl@sss.pgh.pa.us        4191                 :              8 :                 memcpy(data, snap, sizeof(SnapshotData));
 4205 rhaas@postgresql.org     4192                 :              8 :                 data += sizeof(SnapshotData);
                               4193                 :                : 
 4201 tgl@sss.pgh.pa.us        4194         [ +  - ]:              8 :                 if (snap->xcnt)
                               4195                 :                :                 {
                               4196                 :              8 :                     memcpy(data, snap->xip,
 4138 rhaas@postgresql.org     4197                 :              8 :                            sizeof(TransactionId) * snap->xcnt);
                               4198                 :              8 :                     data += sizeof(TransactionId) * snap->xcnt;
                               4199                 :                :                 }
                               4200                 :                : 
 4201 tgl@sss.pgh.pa.us        4201         [ -  + ]:              8 :                 if (snap->subxcnt)
                               4202                 :                :                 {
 4201 tgl@sss.pgh.pa.us        4203                 :UBC           0 :                     memcpy(data, snap->subxip,
 4138 rhaas@postgresql.org     4204                 :              0 :                            sizeof(TransactionId) * snap->subxcnt);
                               4205                 :              0 :                     data += sizeof(TransactionId) * snap->subxcnt;
                               4206                 :                :                 }
 4205 rhaas@postgresql.org     4207                 :CBC           8 :                 break;
                               4208                 :                :             }
 2709 peter_e@gmx.net          4209                 :              2 :         case REORDER_BUFFER_CHANGE_TRUNCATE:
                               4210                 :                :             {
                               4211                 :                :                 Size        size;
                               4212                 :                :                 char       *data;
                               4213                 :                : 
                               4214                 :                :                 /* account for the OIDs of truncated relations */
 2560 tomas.vondra@postgre     4215                 :              2 :                 size = sizeof(Oid) * change->data.truncate.nrelids;
                               4216                 :              2 :                 sz += size;
                               4217                 :                : 
                               4218                 :                :                 /* make sure we have enough space */
                               4219                 :              2 :                 ReorderBufferSerializeReserve(rb, sz);
                               4220                 :                : 
                               4221                 :              2 :                 data = ((char *) rb->outbuf) + sizeof(ReorderBufferDiskChange);
                               4222                 :                :                 /* might have been reallocated above */
                               4223                 :              2 :                 ondisk = (ReorderBufferDiskChange *) rb->outbuf;
                               4224                 :                : 
                               4225                 :              2 :                 memcpy(data, change->data.truncate.relids, size);
                               4226                 :              2 :                 data += size;
                               4227                 :                : 
                               4228                 :              2 :                 break;
                               4229                 :                :             }
 3774 andres@anarazel.de       4230                 :          17311 :         case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM:
                               4231                 :                :         case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_ABORT:
                               4232                 :                :         case REORDER_BUFFER_CHANGE_INTERNAL_COMMAND_ID:
                               4233                 :                :         case REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID:
                               4234                 :                :             /* ReorderBufferChange contains everything important */
 4205 rhaas@postgresql.org     4235                 :          17311 :             break;
                               4236                 :                :     }
                               4237                 :                : 
                               4238                 :        1038054 :     ondisk->size = sz;
                               4239                 :                : 
 2589 michael@paquier.xyz      4240                 :        1038054 :     errno = 0;
 3094 rhaas@postgresql.org     4241                 :        1038054 :     pgstat_report_wait_start(WAIT_EVENT_REORDER_BUFFER_WRITE);
 4205                          4242         [ -  + ]:        1038054 :     if (write(fd, rb->outbuf, ondisk->size) != ondisk->size)
                               4243                 :                :     {
 3297 tgl@sss.pgh.pa.us        4244                 :UBC           0 :         int         save_errno = errno;
                               4245                 :                : 
 4205 rhaas@postgresql.org     4246                 :              0 :         CloseTransientFile(fd);
                               4247                 :                : 
                               4248                 :                :         /* if write didn't set errno, assume problem is no disk space */
 2630 michael@paquier.xyz      4249         [ #  # ]:              0 :         errno = save_errno ? save_errno : ENOSPC;
 4205 rhaas@postgresql.org     4250         [ #  # ]:              0 :         ereport(ERROR,
                               4251                 :                :                 (errcode_for_file_access(),
                               4252                 :                :                  errmsg("could not write to data file for XID %u: %m",
                               4253                 :                :                         txn->xid)));
                               4254                 :                :     }
 3094 rhaas@postgresql.org     4255                 :CBC     1038054 :     pgstat_report_wait_end();
                               4256                 :                : 
                               4257                 :                :     /*
                               4258                 :                :      * Keep the transaction's final_lsn up to date with each change we send to
                               4259                 :                :      * disk, so that ReorderBufferRestoreCleanup works correctly.  (We used to
                               4260                 :                :      * only do this on commit and abort records, but that doesn't work if a
                               4261                 :                :      * system crash leaves a transaction without its abort record).
                               4262                 :                :      *
                               4263                 :                :      * Make sure not to move it backwards.
                               4264                 :                :      */
 2059 alvherre@alvh.no-ip.     4265         [ +  + ]:        1038054 :     if (txn->final_lsn < change->lsn)
                               4266                 :        1033571 :         txn->final_lsn = change->lsn;
                               4267                 :                : 
 4201 tgl@sss.pgh.pa.us        4268         [ -  + ]:        1038054 :     Assert(ondisk->change.action == change->action);
 4205 rhaas@postgresql.org     4269                 :        1038054 : }
                               4270                 :                : 
                               4271                 :                : /* Returns true, if the output plugin supports streaming, false, otherwise. */
                               4272                 :                : static inline bool
 1855 akapila@postgresql.o     4273                 :        1591424 : ReorderBufferCanStream(ReorderBuffer *rb)
                               4274                 :                : {
                               4275                 :        1591424 :     LogicalDecodingContext *ctx = rb->private_data;
                               4276                 :                : 
                               4277                 :        1591424 :     return ctx->streaming;
                               4278                 :                : }
                               4279                 :                : 
                               4280                 :                : /* Returns true, if the streaming can be started now, false, otherwise. */
                               4281                 :                : static inline bool
                               4282                 :         322842 : ReorderBufferCanStartStreaming(ReorderBuffer *rb)
                               4283                 :                : {
                               4284                 :         322842 :     LogicalDecodingContext *ctx = rb->private_data;
                               4285                 :         322842 :     SnapBuild  *builder = ctx->snapshot_builder;
                               4286                 :                : 
                               4287                 :                :     /* We can't start streaming unless a consistent state is reached. */
 1737                          4288         [ -  + ]:         322842 :     if (SnapBuildCurrentState(builder) < SNAPBUILD_CONSISTENT)
 1737 akapila@postgresql.o     4289                 :UBC           0 :         return false;
                               4290                 :                : 
                               4291                 :                :     /*
                               4292                 :                :      * We can't start streaming immediately even if the streaming is enabled
                               4293                 :                :      * because we previously decoded this transaction and now just are
                               4294                 :                :      * restarting.
                               4295                 :                :      */
 1855 akapila@postgresql.o     4296         [ +  + ]:CBC      322842 :     if (ReorderBufferCanStream(rb) &&
 1003                          4297         [ +  + ]:         320826 :         !SnapBuildXactNeedsSkip(builder, ctx->reader->ReadRecPtr))
 1855                          4298                 :         178550 :         return true;
                               4299                 :                : 
                               4300                 :         144292 :     return false;
                               4301                 :                : }
                               4302                 :                : 
                               4303                 :                : /*
                               4304                 :                :  * Send data of a large transaction (and its subtransactions) to the
                               4305                 :                :  * output plugin, but using the stream API.
                               4306                 :                :  */
                               4307                 :                : static void
                               4308                 :            725 : ReorderBufferStreamTXN(ReorderBuffer *rb, ReorderBufferTXN *txn)
                               4309                 :                : {
                               4310                 :                :     Snapshot    snapshot_now;
                               4311                 :                :     CommandId   command_id;
                               4312                 :                :     Size        stream_bytes;
                               4313                 :                :     bool        txn_is_streamed;
                               4314                 :                : 
                               4315                 :                :     /* We can never reach here for a subtransaction. */
  904                          4316         [ -  + ]:            725 :     Assert(rbtxn_is_toptxn(txn));
                               4317                 :                : 
                               4318                 :                :     /*
                               4319                 :                :      * We can't make any assumptions about base snapshot here, similar to what
                               4320                 :                :      * ReorderBufferCommit() does. That relies on base_snapshot getting
                               4321                 :                :      * transferred from subxact in ReorderBufferCommitChild(), but that was
                               4322                 :                :      * not yet called as the transaction is in-progress.
                               4323                 :                :      *
                               4324                 :                :      * So just walk the subxacts and use the same logic here. But we only need
                               4325                 :                :      * to do that once, when the transaction is streamed for the first time.
                               4326                 :                :      * After that we need to reuse the snapshot from the previous run.
                               4327                 :                :      *
                               4328                 :                :      * Unlike DecodeCommit which adds xids of all the subtransactions in
                               4329                 :                :      * snapshot's xip array via SnapBuildCommitTxn, we can't do that here but
                               4330                 :                :      * we do add them to subxip array instead via ReorderBufferCopySnap. This
                               4331                 :                :      * allows the catalog changes made in subtransactions decoded till now to
                               4332                 :                :      * be visible.
                               4333                 :                :      */
 1855                          4334         [ +  + ]:            725 :     if (txn->snapshot_now == NULL)
                               4335                 :                :     {
                               4336                 :                :         dlist_iter  subxact_i;
                               4337                 :                : 
                               4338                 :                :         /* make sure this transaction is streamed for the first time */
                               4339         [ -  + ]:             72 :         Assert(!rbtxn_is_streamed(txn));
                               4340                 :                : 
                               4341                 :                :         /* at the beginning we should have invalid command ID */
                               4342         [ -  + ]:             72 :         Assert(txn->command_id == InvalidCommandId);
                               4343                 :                : 
                               4344   [ +  -  +  + ]:             76 :         dlist_foreach(subxact_i, &txn->subtxns)
                               4345                 :                :         {
                               4346                 :                :             ReorderBufferTXN *subtxn;
                               4347                 :                : 
                               4348                 :              4 :             subtxn = dlist_container(ReorderBufferTXN, node, subxact_i.cur);
                               4349                 :              4 :             ReorderBufferTransferSnapToParent(txn, subtxn);
                               4350                 :                :         }
                               4351                 :                : 
                               4352                 :                :         /*
                               4353                 :                :          * If this transaction has no snapshot, it didn't make any changes to
                               4354                 :                :          * the database till now, so there's nothing to decode.
                               4355                 :                :          */
                               4356         [ -  + ]:             72 :         if (txn->base_snapshot == NULL)
                               4357                 :                :         {
 1855 akapila@postgresql.o     4358         [ #  # ]:UBC           0 :             Assert(txn->ninvalidations == 0);
                               4359                 :              0 :             return;
                               4360                 :                :         }
                               4361                 :                : 
 1855 akapila@postgresql.o     4362                 :CBC          72 :         command_id = FirstCommandId;
                               4363                 :             72 :         snapshot_now = ReorderBufferCopySnap(rb, txn->base_snapshot,
                               4364                 :                :                                              txn, command_id);
                               4365                 :                :     }
                               4366                 :                :     else
                               4367                 :                :     {
                               4368                 :                :         /* the transaction must have been already streamed */
                               4369         [ -  + ]:            653 :         Assert(rbtxn_is_streamed(txn));
                               4370                 :                : 
                               4371                 :                :         /*
                               4372                 :                :          * Nah, we already have snapshot from the previous streaming run. We
                               4373                 :                :          * assume new subxacts can't move the LSN backwards, and so can't beat
                               4374                 :                :          * the LSN condition in the previous branch (so no need to walk
                               4375                 :                :          * through subxacts again). In fact, we must not do that as we may be
                               4376                 :                :          * using snapshot half-way through the subxact.
                               4377                 :                :          */
                               4378                 :            653 :         command_id = txn->command_id;
                               4379                 :                : 
                               4380                 :                :         /*
                               4381                 :                :          * We can't use txn->snapshot_now directly because after the last
                               4382                 :                :          * streaming run, we might have got some new sub-transactions. So we
                               4383                 :                :          * need to add them to the snapshot.
                               4384                 :                :          */
                               4385                 :            653 :         snapshot_now = ReorderBufferCopySnap(rb, txn->snapshot_now,
                               4386                 :                :                                              txn, command_id);
                               4387                 :                : 
                               4388                 :                :         /* Free the previously copied snapshot. */
                               4389         [ -  + ]:            653 :         Assert(txn->snapshot_now->copied);
                               4390                 :            653 :         ReorderBufferFreeSnap(rb, txn->snapshot_now);
                               4391                 :            653 :         txn->snapshot_now = NULL;
                               4392                 :                :     }
                               4393                 :                : 
                               4394                 :                :     /*
                               4395                 :                :      * Remember this information to be used later to update stats. We can't
                               4396                 :                :      * update the stats here as an error while processing the changes would
                               4397                 :                :      * lead to the accumulation of stats even though we haven't streamed all
                               4398                 :                :      * the changes.
                               4399                 :                :      */
 1773                          4400                 :            725 :     txn_is_streamed = rbtxn_is_streamed(txn);
                               4401                 :            725 :     stream_bytes = txn->total_size;
                               4402                 :                : 
                               4403                 :                :     /* Process and send the changes to output plugin. */
 1855                          4404                 :            725 :     ReorderBufferProcessTXN(rb, txn, InvalidXLogRecPtr, snapshot_now,
                               4405                 :                :                             command_id, true);
                               4406                 :                : 
 1773                          4407                 :            725 :     rb->streamCount += 1;
                               4408                 :            725 :     rb->streamBytes += stream_bytes;
                               4409                 :                : 
                               4410                 :                :     /* Don't consider already streamed transaction. */
                               4411                 :            725 :     rb->streamTxns += (txn_is_streamed) ? 0 : 1;
                               4412                 :                : 
                               4413                 :                :     /* update the decoding stats */
 1584                          4414                 :            725 :     UpdateDecodingStats((LogicalDecodingContext *) rb->private_data);
                               4415                 :                : 
 1855                          4416         [ -  + ]:            725 :     Assert(dlist_is_empty(&txn->changes));
                               4417         [ -  + ]:            725 :     Assert(txn->nentries == 0);
                               4418         [ -  + ]:            725 :     Assert(txn->nentries_mem == 0);
                               4419                 :                : }
                               4420                 :                : 
                               4421                 :                : /*
                               4422                 :                :  * Size of a change in memory.
                               4423                 :                :  */
                               4424                 :                : static Size
 2121                          4425                 :        1869906 : ReorderBufferChangeSize(ReorderBufferChange *change)
                               4426                 :                : {
                               4427                 :        1869906 :     Size        sz = sizeof(ReorderBufferChange);
                               4428                 :                : 
                               4429   [ +  +  +  +  :        1869906 :     switch (change->action)
                                           +  +  - ]
                               4430                 :                :     {
                               4431                 :                :             /* fall through these, they're all similar enough */
                               4432                 :        1762635 :         case REORDER_BUFFER_CHANGE_INSERT:
                               4433                 :                :         case REORDER_BUFFER_CHANGE_UPDATE:
                               4434                 :                :         case REORDER_BUFFER_CHANGE_DELETE:
                               4435                 :                :         case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_INSERT:
                               4436                 :                :             {
                               4437                 :                :                 HeapTuple   oldtup,
                               4438                 :                :                             newtup;
                               4439                 :        1762635 :                 Size        oldlen = 0;
                               4440                 :        1762635 :                 Size        newlen = 0;
                               4441                 :                : 
                               4442                 :        1762635 :                 oldtup = change->data.tp.oldtuple;
                               4443                 :        1762635 :                 newtup = change->data.tp.newtuple;
                               4444                 :                : 
                               4445         [ +  + ]:        1762635 :                 if (oldtup)
                               4446                 :                :                 {
                               4447                 :         195349 :                     sz += sizeof(HeapTupleData);
  586 msawada@postgresql.o     4448                 :         195349 :                     oldlen = oldtup->t_len;
 2121 akapila@postgresql.o     4449                 :         195349 :                     sz += oldlen;
                               4450                 :                :                 }
                               4451                 :                : 
                               4452         [ +  + ]:        1762635 :                 if (newtup)
                               4453                 :                :                 {
                               4454                 :        1484300 :                     sz += sizeof(HeapTupleData);
  586 msawada@postgresql.o     4455                 :        1484300 :                     newlen = newtup->t_len;
 2121 akapila@postgresql.o     4456                 :        1484300 :                     sz += newlen;
                               4457                 :                :                 }
                               4458                 :                : 
                               4459                 :        1762635 :                 break;
                               4460                 :                :             }
                               4461                 :             67 :         case REORDER_BUFFER_CHANGE_MESSAGE:
                               4462                 :                :             {
                               4463                 :             67 :                 Size        prefix_size = strlen(change->data.msg.prefix) + 1;
                               4464                 :                : 
                               4465                 :             67 :                 sz += prefix_size + change->data.msg.message_size +
                               4466                 :                :                     sizeof(Size) + sizeof(Size);
                               4467                 :                : 
                               4468                 :             67 :                 break;
                               4469                 :                :             }
 1787                          4470                 :          10071 :         case REORDER_BUFFER_CHANGE_INVALIDATION:
                               4471                 :                :             {
                               4472                 :          10071 :                 sz += sizeof(SharedInvalidationMessage) *
                               4473                 :          10071 :                     change->data.inval.ninvalidations;
                               4474                 :          10071 :                 break;
                               4475                 :                :             }
 2121                          4476                 :           2382 :         case REORDER_BUFFER_CHANGE_INTERNAL_SNAPSHOT:
                               4477                 :                :             {
                               4478                 :                :                 Snapshot    snap;
                               4479                 :                : 
                               4480                 :           2382 :                 snap = change->data.snapshot;
                               4481                 :                : 
                               4482                 :           2382 :                 sz += sizeof(SnapshotData) +
                               4483                 :           2382 :                     sizeof(TransactionId) * snap->xcnt +
                               4484                 :           2382 :                     sizeof(TransactionId) * snap->subxcnt;
                               4485                 :                : 
                               4486                 :           2382 :                 break;
                               4487                 :                :             }
                               4488                 :             79 :         case REORDER_BUFFER_CHANGE_TRUNCATE:
                               4489                 :                :             {
                               4490                 :             79 :                 sz += sizeof(Oid) * change->data.truncate.nrelids;
                               4491                 :                : 
                               4492                 :             79 :                 break;
                               4493                 :                :             }
                               4494                 :          94672 :         case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM:
                               4495                 :                :         case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_ABORT:
                               4496                 :                :         case REORDER_BUFFER_CHANGE_INTERNAL_COMMAND_ID:
                               4497                 :                :         case REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID:
                               4498                 :                :             /* ReorderBufferChange contains everything important */
                               4499                 :          94672 :             break;
                               4500                 :                :     }
                               4501                 :                : 
                               4502                 :        1869906 :     return sz;
                               4503                 :                : }
                               4504                 :                : 
                               4505                 :                : 
                               4506                 :                : /*
                               4507                 :                :  * Restore a number of changes spilled to disk back into memory.
                               4508                 :                :  */
                               4509                 :                : static Size
 4205 rhaas@postgresql.org     4510                 :            102 : ReorderBufferRestoreChanges(ReorderBuffer *rb, ReorderBufferTXN *txn,
                               4511                 :                :                             TXNEntryFile *file, XLogSegNo *segno)
                               4512                 :                : {
                               4513                 :            102 :     Size        restored = 0;
                               4514                 :                :     XLogSegNo   last_segno;
                               4515                 :                :     dlist_mutable_iter cleanup_iter;
 2093 akapila@postgresql.o     4516                 :            102 :     File       *fd = &file->vfd;
                               4517                 :                : 
 4205 rhaas@postgresql.org     4518         [ -  + ]:            102 :     Assert(txn->first_lsn != InvalidXLogRecPtr);
                               4519         [ -  + ]:            102 :     Assert(txn->final_lsn != InvalidXLogRecPtr);
                               4520                 :                : 
                               4521                 :                :     /* free current entries, so we have memory for more */
                               4522   [ +  -  +  + ]:         169617 :     dlist_foreach_modify(cleanup_iter, &txn->changes)
                               4523                 :                :     {
                               4524                 :         169515 :         ReorderBufferChange *cleanup =
  841 tgl@sss.pgh.pa.us        4525                 :         169515 :             dlist_container(ReorderBufferChange, node, cleanup_iter.cur);
                               4526                 :                : 
 4205 rhaas@postgresql.org     4527                 :         169515 :         dlist_delete(&cleanup->node);
  178 heikki.linnakangas@i     4528                 :         169515 :         ReorderBufferFreeChange(rb, cleanup, true);
                               4529                 :                :     }
 4205 rhaas@postgresql.org     4530                 :            102 :     txn->nentries_mem = 0;
                               4531         [ -  + ]:            102 :     Assert(dlist_is_empty(&txn->changes));
                               4532                 :                : 
 2909 andres@anarazel.de       4533                 :            102 :     XLByteToSeg(txn->final_lsn, last_segno, wal_segment_size);
                               4534                 :                : 
 4205 rhaas@postgresql.org     4535   [ +  +  +  + ]:         173321 :     while (restored < max_changes_in_memory && *segno <= last_segno)
                               4536                 :                :     {
                               4537                 :                :         int         readBytes;
                               4538                 :                :         ReorderBufferDiskChange *ondisk;
                               4539                 :                : 
 1051 akapila@postgresql.o     4540         [ -  + ]:         173219 :         CHECK_FOR_INTERRUPTS();
                               4541                 :                : 
 4205 rhaas@postgresql.org     4542         [ +  + ]:         173219 :         if (*fd == -1)
                               4543                 :                :         {
                               4544                 :                :             char        path[MAXPGPATH];
                               4545                 :                : 
                               4546                 :                :             /* first time in */
                               4547         [ +  + ]:             43 :             if (*segno == 0)
 2909 andres@anarazel.de       4548                 :             39 :                 XLByteToSeg(txn->first_lsn, *segno, wal_segment_size);
                               4549                 :                : 
 4205 rhaas@postgresql.org     4550   [ -  +  -  - ]:             43 :             Assert(*segno != 0 || dlist_is_empty(&txn->changes));
                               4551                 :                : 
                               4552                 :                :             /*
                               4553                 :                :              * No need to care about TLIs here, only used during a single run,
                               4554                 :                :              * so each LSN only maps to a specific WAL record.
                               4555                 :                :              */
 2741 alvherre@alvh.no-ip.     4556                 :             43 :             ReorderBufferSerializedPath(path, MyReplicationSlot, txn->xid,
                               4557                 :                :                                         *segno);
                               4558                 :                : 
 2093 akapila@postgresql.o     4559                 :             43 :             *fd = PathNameOpenFile(path, O_RDONLY | PG_BINARY);
                               4560                 :                : 
                               4561                 :                :             /* No harm in resetting the offset even in case of failure */
                               4562                 :             43 :             file->curOffset = 0;
                               4563                 :                : 
 4205 rhaas@postgresql.org     4564   [ +  +  +  - ]:             43 :             if (*fd < 0 && errno == ENOENT)
                               4565                 :                :             {
                               4566                 :              1 :                 *fd = -1;
                               4567                 :              1 :                 (*segno)++;
                               4568                 :              1 :                 continue;
                               4569                 :                :             }
                               4570         [ -  + ]:             42 :             else if (*fd < 0)
 4205 rhaas@postgresql.org     4571         [ #  # ]:UBC           0 :                 ereport(ERROR,
                               4572                 :                :                         (errcode_for_file_access(),
                               4573                 :                :                          errmsg("could not open file \"%s\": %m",
                               4574                 :                :                                 path)));
                               4575                 :                :         }
                               4576                 :                : 
                               4577                 :                :         /*
                               4578                 :                :          * Read the statically sized part of a change which has information
                               4579                 :                :          * about the total size. If we couldn't read a record, we're at the
                               4580                 :                :          * end of this file.
                               4581                 :                :          */
 4138 rhaas@postgresql.org     4582                 :CBC      173218 :         ReorderBufferSerializeReserve(rb, sizeof(ReorderBufferDiskChange));
 2093 akapila@postgresql.o     4583                 :         173218 :         readBytes = FileRead(file->vfd, rb->outbuf,
                               4584                 :                :                              sizeof(ReorderBufferDiskChange),
                               4585                 :                :                              file->curOffset, WAIT_EVENT_REORDER_BUFFER_READ);
                               4586                 :                : 
                               4587                 :                :         /* eof */
 4205 rhaas@postgresql.org     4588         [ +  + ]:         173218 :         if (readBytes == 0)
                               4589                 :                :         {
 2093 akapila@postgresql.o     4590                 :             42 :             FileClose(*fd);
 4205 rhaas@postgresql.org     4591                 :             42 :             *fd = -1;
                               4592                 :             42 :             (*segno)++;
                               4593                 :             42 :             continue;
                               4594                 :                :         }
                               4595         [ -  + ]:         173176 :         else if (readBytes < 0)
 4205 rhaas@postgresql.org     4596         [ #  # ]:UBC           0 :             ereport(ERROR,
                               4597                 :                :                     (errcode_for_file_access(),
                               4598                 :                :                      errmsg("could not read from reorderbuffer spill file: %m")));
 4205 rhaas@postgresql.org     4599         [ -  + ]:CBC      173176 :         else if (readBytes != sizeof(ReorderBufferDiskChange))
 4205 rhaas@postgresql.org     4600         [ #  # ]:UBC           0 :             ereport(ERROR,
                               4601                 :                :                     (errcode_for_file_access(),
                               4602                 :                :                      errmsg("could not read from reorderbuffer spill file: read %d instead of %u bytes",
                               4603                 :                :                             readBytes,
                               4604                 :                :                             (uint32) sizeof(ReorderBufferDiskChange))));
                               4605                 :                : 
 2093 akapila@postgresql.o     4606                 :CBC      173176 :         file->curOffset += readBytes;
                               4607                 :                : 
 4205 rhaas@postgresql.org     4608                 :         173176 :         ondisk = (ReorderBufferDiskChange *) rb->outbuf;
                               4609                 :                : 
                               4610                 :         173176 :         ReorderBufferSerializeReserve(rb,
 2999 tgl@sss.pgh.pa.us        4611                 :         173176 :                                       sizeof(ReorderBufferDiskChange) + ondisk->size);
 4205 rhaas@postgresql.org     4612                 :         173176 :         ondisk = (ReorderBufferDiskChange *) rb->outbuf;
                               4613                 :                : 
 2093 akapila@postgresql.o     4614                 :         346352 :         readBytes = FileRead(file->vfd,
                               4615                 :         173176 :                              rb->outbuf + sizeof(ReorderBufferDiskChange),
                               4616                 :         173176 :                              ondisk->size - sizeof(ReorderBufferDiskChange),
                               4617                 :                :                              file->curOffset,
                               4618                 :                :                              WAIT_EVENT_REORDER_BUFFER_READ);
                               4619                 :                : 
 4205 rhaas@postgresql.org     4620         [ -  + ]:         173176 :         if (readBytes < 0)
 4205 rhaas@postgresql.org     4621         [ #  # ]:UBC           0 :             ereport(ERROR,
                               4622                 :                :                     (errcode_for_file_access(),
                               4623                 :                :                      errmsg("could not read from reorderbuffer spill file: %m")));
 4205 rhaas@postgresql.org     4624         [ -  + ]:CBC      173176 :         else if (readBytes != ondisk->size - sizeof(ReorderBufferDiskChange))
 4205 rhaas@postgresql.org     4625         [ #  # ]:UBC           0 :             ereport(ERROR,
                               4626                 :                :                     (errcode_for_file_access(),
                               4627                 :                :                      errmsg("could not read from reorderbuffer spill file: read %d instead of %u bytes",
                               4628                 :                :                             readBytes,
                               4629                 :                :                             (uint32) (ondisk->size - sizeof(ReorderBufferDiskChange)))));
                               4630                 :                : 
 2093 akapila@postgresql.o     4631                 :CBC      173176 :         file->curOffset += readBytes;
                               4632                 :                : 
                               4633                 :                :         /*
                               4634                 :                :          * ok, read a full change from disk, now restore it into proper
                               4635                 :                :          * in-memory format
                               4636                 :                :          */
 4205 rhaas@postgresql.org     4637                 :         173176 :         ReorderBufferRestoreChange(rb, txn, rb->outbuf);
                               4638                 :         173176 :         restored++;
                               4639                 :                :     }
                               4640                 :                : 
                               4641                 :            102 :     return restored;
                               4642                 :                : }
                               4643                 :                : 
                               4644                 :                : /*
                               4645                 :                :  * Convert change from its on-disk format to in-memory format and queue it onto
                               4646                 :                :  * the TXN's ->changes list.
                               4647                 :                :  *
                               4648                 :                :  * Note: although "data" is declared char*, at entry it points to a
                               4649                 :                :  * maxalign'd buffer, making it safe in most of this function to assume
                               4650                 :                :  * that the pointed-to data is suitably aligned for direct access.
                               4651                 :                :  */
                               4652                 :                : static void
                               4653                 :         173176 : ReorderBufferRestoreChange(ReorderBuffer *rb, ReorderBufferTXN *txn,
                               4654                 :                :                            char *data)
                               4655                 :                : {
                               4656                 :                :     ReorderBufferDiskChange *ondisk;
                               4657                 :                :     ReorderBufferChange *change;
                               4658                 :                : 
                               4659                 :         173176 :     ondisk = (ReorderBufferDiskChange *) data;
                               4660                 :                : 
  178 heikki.linnakangas@i     4661                 :         173176 :     change = ReorderBufferAllocChange(rb);
                               4662                 :                : 
                               4663                 :                :     /* copy static part */
 4205 rhaas@postgresql.org     4664                 :         173176 :     memcpy(change, &ondisk->change, sizeof(ReorderBufferChange));
                               4665                 :                : 
                               4666                 :         173176 :     data += sizeof(ReorderBufferDiskChange);
                               4667                 :                : 
                               4668                 :                :     /* restore individual stuff */
 4201 tgl@sss.pgh.pa.us        4669   [ +  +  +  +  :         173176 :     switch (change->action)
                                           -  +  - ]
                               4670                 :                :     {
                               4671                 :                :             /* fall through these, they're all similar enough */
                               4672                 :         171247 :         case REORDER_BUFFER_CHANGE_INSERT:
                               4673                 :                :         case REORDER_BUFFER_CHANGE_UPDATE:
                               4674                 :                :         case REORDER_BUFFER_CHANGE_DELETE:
                               4675                 :                :         case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_INSERT:
 3472 andres@anarazel.de       4676         [ +  + ]:         171247 :             if (change->data.tp.oldtuple)
                               4677                 :                :             {
 3432 tgl@sss.pgh.pa.us        4678                 :           5006 :                 uint32      tuplelen = ((HeapTuple) data)->t_len;
                               4679                 :                : 
 3472 andres@anarazel.de       4680                 :           5006 :                 change->data.tp.oldtuple =
  178 heikki.linnakangas@i     4681                 :           5006 :                     ReorderBufferAllocTupleBuf(rb, tuplelen - SizeofHeapTupleHeader);
                               4682                 :                : 
                               4683                 :                :                 /* restore ->tuple */
  586 msawada@postgresql.o     4684                 :           5006 :                 memcpy(change->data.tp.oldtuple, data,
                               4685                 :                :                        sizeof(HeapTupleData));
 3472 andres@anarazel.de       4686                 :           5006 :                 data += sizeof(HeapTupleData);
                               4687                 :                : 
                               4688                 :                :                 /* reset t_data pointer into the new tuplebuf */
  586 msawada@postgresql.o     4689                 :           5006 :                 change->data.tp.oldtuple->t_data =
                               4690                 :           5006 :                     (HeapTupleHeader) ((char *) change->data.tp.oldtuple + HEAPTUPLESIZE);
                               4691                 :                : 
                               4692                 :                :                 /* restore tuple data itself */
                               4693                 :           5006 :                 memcpy(change->data.tp.oldtuple->t_data, data, tuplelen);
 3472 andres@anarazel.de       4694                 :           5006 :                 data += tuplelen;
                               4695                 :                :             }
                               4696                 :                : 
                               4697         [ +  + ]:         171247 :             if (change->data.tp.newtuple)
                               4698                 :                :             {
                               4699                 :                :                 /* here, data might not be suitably aligned! */
                               4700                 :                :                 uint32      tuplelen;
                               4701                 :                : 
 3432 tgl@sss.pgh.pa.us        4702                 :         161026 :                 memcpy(&tuplelen, data + offsetof(HeapTupleData, t_len),
                               4703                 :                :                        sizeof(uint32));
                               4704                 :                : 
 3472 andres@anarazel.de       4705                 :         161026 :                 change->data.tp.newtuple =
  178 heikki.linnakangas@i     4706                 :         161026 :                     ReorderBufferAllocTupleBuf(rb, tuplelen - SizeofHeapTupleHeader);
                               4707                 :                : 
                               4708                 :                :                 /* restore ->tuple */
  586 msawada@postgresql.o     4709                 :         161026 :                 memcpy(change->data.tp.newtuple, data,
                               4710                 :                :                        sizeof(HeapTupleData));
 3472 andres@anarazel.de       4711                 :         161026 :                 data += sizeof(HeapTupleData);
                               4712                 :                : 
                               4713                 :                :                 /* reset t_data pointer into the new tuplebuf */
  586 msawada@postgresql.o     4714                 :         161026 :                 change->data.tp.newtuple->t_data =
                               4715                 :         161026 :                     (HeapTupleHeader) ((char *) change->data.tp.newtuple + HEAPTUPLESIZE);
                               4716                 :                : 
                               4717                 :                :                 /* restore tuple data itself */
                               4718                 :         161026 :                 memcpy(change->data.tp.newtuple->t_data, data, tuplelen);
 3472 andres@anarazel.de       4719                 :         161026 :                 data += tuplelen;
                               4720                 :                :             }
                               4721                 :                : 
 4205 rhaas@postgresql.org     4722                 :         171247 :             break;
 3440 simon@2ndQuadrant.co     4723                 :              1 :         case REORDER_BUFFER_CHANGE_MESSAGE:
                               4724                 :                :             {
                               4725                 :                :                 Size        prefix_size;
                               4726                 :                : 
                               4727                 :                :                 /* read prefix */
                               4728                 :              1 :                 memcpy(&prefix_size, data, sizeof(Size));
                               4729                 :              1 :                 data += sizeof(Size);
                               4730                 :              1 :                 change->data.msg.prefix = MemoryContextAlloc(rb->context,
                               4731                 :                :                                                              prefix_size);
                               4732                 :              1 :                 memcpy(change->data.msg.prefix, data, prefix_size);
 3376 rhaas@postgresql.org     4733         [ -  + ]:              1 :                 Assert(change->data.msg.prefix[prefix_size - 1] == '\0');
 3440 simon@2ndQuadrant.co     4734                 :              1 :                 data += prefix_size;
                               4735                 :                : 
                               4736                 :                :                 /* read the message */
                               4737                 :              1 :                 memcpy(&change->data.msg.message_size, data, sizeof(Size));
                               4738                 :              1 :                 data += sizeof(Size);
                               4739                 :              1 :                 change->data.msg.message = MemoryContextAlloc(rb->context,
                               4740                 :                :                                                               change->data.msg.message_size);
                               4741                 :              1 :                 memcpy(change->data.msg.message, data,
                               4742                 :                :                        change->data.msg.message_size);
                               4743                 :              1 :                 data += change->data.msg.message_size;
                               4744                 :                : 
 1787 akapila@postgresql.o     4745                 :              1 :                 break;
                               4746                 :                :             }
                               4747                 :             23 :         case REORDER_BUFFER_CHANGE_INVALIDATION:
                               4748                 :                :             {
                               4749                 :             23 :                 Size        inval_size = sizeof(SharedInvalidationMessage) *
  841 tgl@sss.pgh.pa.us        4750                 :             23 :                     change->data.inval.ninvalidations;
                               4751                 :                : 
 1787 akapila@postgresql.o     4752                 :             23 :                 change->data.inval.invalidations =
                               4753                 :             23 :                     MemoryContextAlloc(rb->context, inval_size);
                               4754                 :                : 
                               4755                 :                :                 /* read the message */
                               4756                 :             23 :                 memcpy(change->data.inval.invalidations, data, inval_size);
                               4757                 :                : 
 3440 simon@2ndQuadrant.co     4758                 :             23 :                 break;
                               4759                 :                :             }
 4205 rhaas@postgresql.org     4760                 :              2 :         case REORDER_BUFFER_CHANGE_INTERNAL_SNAPSHOT:
                               4761                 :                :             {
                               4762                 :                :                 Snapshot    oldsnap;
                               4763                 :                :                 Snapshot    newsnap;
                               4764                 :                :                 Size        size;
                               4765                 :                : 
 4201 tgl@sss.pgh.pa.us        4766                 :              2 :                 oldsnap = (Snapshot) data;
                               4767                 :                : 
                               4768                 :              2 :                 size = sizeof(SnapshotData) +
                               4769                 :              2 :                     sizeof(TransactionId) * oldsnap->xcnt +
                               4770                 :              2 :                     sizeof(TransactionId) * (oldsnap->subxcnt + 0);
                               4771                 :                : 
                               4772                 :              2 :                 change->data.snapshot = MemoryContextAllocZero(rb->context, size);
                               4773                 :                : 
                               4774                 :              2 :                 newsnap = change->data.snapshot;
                               4775                 :                : 
                               4776                 :              2 :                 memcpy(newsnap, data, size);
                               4777                 :              2 :                 newsnap->xip = (TransactionId *)
                               4778                 :                :                     (((char *) newsnap) + sizeof(SnapshotData));
                               4779                 :              2 :                 newsnap->subxip = newsnap->xip + newsnap->xcnt;
                               4780                 :              2 :                 newsnap->copied = true;
 4205 rhaas@postgresql.org     4781                 :              2 :                 break;
                               4782                 :                :             }
                               4783                 :                :             /* the base struct contains all the data, easy peasy */
 2709 peter_e@gmx.net          4784                 :UBC           0 :         case REORDER_BUFFER_CHANGE_TRUNCATE:
                               4785                 :                :             {
                               4786                 :                :                 Oid        *relids;
                               4787                 :                : 
  178 heikki.linnakangas@i     4788                 :              0 :                 relids = ReorderBufferAllocRelids(rb, change->data.truncate.nrelids);
 2560 tomas.vondra@postgre     4789                 :              0 :                 memcpy(relids, data, change->data.truncate.nrelids * sizeof(Oid));
                               4790                 :              0 :                 change->data.truncate.relids = relids;
                               4791                 :                : 
                               4792                 :              0 :                 break;
                               4793                 :                :             }
 3774 andres@anarazel.de       4794                 :CBC        1903 :         case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM:
                               4795                 :                :         case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_ABORT:
                               4796                 :                :         case REORDER_BUFFER_CHANGE_INTERNAL_COMMAND_ID:
                               4797                 :                :         case REORDER_BUFFER_CHANGE_INTERNAL_TUPLECID:
 4205 rhaas@postgresql.org     4798                 :           1903 :             break;
                               4799                 :                :     }
                               4800                 :                : 
                               4801                 :         173176 :     dlist_push_tail(&txn->changes, &change->node);
                               4802                 :         173176 :     txn->nentries_mem++;
                               4803                 :                : 
                               4804                 :                :     /*
                               4805                 :                :      * Update memory accounting for the restored change.  We need to do this
                               4806                 :                :      * although we don't check the memory limit when restoring the changes in
                               4807                 :                :      * this branch (we only do that when initially queueing the changes after
                               4808                 :                :      * decoding), because we will release the changes later, and that will
                               4809                 :                :      * update the accounting too (subtracting the size from the counters). And
                               4810                 :                :      * we don't want to underflow there.
                               4811                 :                :      */
  521 msawada@postgresql.o     4812                 :         173176 :     ReorderBufferChangeMemoryUpdate(rb, change, NULL, true,
                               4813                 :                :                                     ReorderBufferChangeSize(change));
 4205 rhaas@postgresql.org     4814                 :         173176 : }
                               4815                 :                : 
                               4816                 :                : /*
                               4817                 :                :  * Remove all on-disk stored for the passed in transaction.
                               4818                 :                :  */
                               4819                 :                : static void
                               4820                 :            232 : ReorderBufferRestoreCleanup(ReorderBuffer *rb, ReorderBufferTXN *txn)
                               4821                 :                : {
                               4822                 :                :     XLogSegNo   first;
                               4823                 :                :     XLogSegNo   cur;
                               4824                 :                :     XLogSegNo   last;
                               4825                 :                : 
                               4826         [ -  + ]:            232 :     Assert(txn->first_lsn != InvalidXLogRecPtr);
                               4827         [ -  + ]:            232 :     Assert(txn->final_lsn != InvalidXLogRecPtr);
                               4828                 :                : 
 2909 andres@anarazel.de       4829                 :            232 :     XLByteToSeg(txn->first_lsn, first, wal_segment_size);
                               4830                 :            232 :     XLByteToSeg(txn->final_lsn, last, wal_segment_size);
                               4831                 :                : 
                               4832                 :                :     /* iterate over all possible filenames, and delete them */
 4205 rhaas@postgresql.org     4833         [ +  + ]:            483 :     for (cur = first; cur <= last; cur++)
                               4834                 :                :     {
                               4835                 :                :         char        path[MAXPGPATH];
                               4836                 :                : 
 2741 alvherre@alvh.no-ip.     4837                 :            251 :         ReorderBufferSerializedPath(path, MyReplicationSlot, txn->xid, cur);
 4205 rhaas@postgresql.org     4838   [ +  +  -  + ]:            251 :         if (unlink(path) != 0 && errno != ENOENT)
 4205 rhaas@postgresql.org     4839         [ #  # ]:UBC           0 :             ereport(ERROR,
                               4840                 :                :                     (errcode_for_file_access(),
                               4841                 :                :                      errmsg("could not remove file \"%s\": %m", path)));
                               4842                 :                :     }
 4205 rhaas@postgresql.org     4843                 :CBC         232 : }
                               4844                 :                : 
                               4845                 :                : /*
                               4846                 :                :  * Remove any leftover serialized reorder buffers from a slot directory after a
                               4847                 :                :  * prior crash or decoding session exit.
                               4848                 :                :  */
                               4849                 :                : static void
 2741 alvherre@alvh.no-ip.     4850                 :           1986 : ReorderBufferCleanupSerializedTXNs(const char *slotname)
                               4851                 :                : {
                               4852                 :                :     DIR        *spill_dir;
                               4853                 :                :     struct dirent *spill_de;
                               4854                 :                :     struct stat statbuf;
                               4855                 :                :     char        path[MAXPGPATH * 2 + sizeof(PG_REPLSLOT_DIR)];
                               4856                 :                : 
  372 michael@paquier.xyz      4857                 :           1986 :     sprintf(path, "%s/%s", PG_REPLSLOT_DIR, slotname);
                               4858                 :                : 
                               4859                 :                :     /* we're only handling directories here, skip if it's not ours */
 2741 alvherre@alvh.no-ip.     4860   [ +  -  -  + ]:           1986 :     if (lstat(path, &statbuf) == 0 && !S_ISDIR(statbuf.st_mode))
 2741 alvherre@alvh.no-ip.     4861                 :UBC           0 :         return;
                               4862                 :                : 
 2741 alvherre@alvh.no-ip.     4863                 :CBC        1986 :     spill_dir = AllocateDir(path);
                               4864         [ +  + ]:           9930 :     while ((spill_de = ReadDirExtended(spill_dir, path, INFO)) != NULL)
                               4865                 :                :     {
                               4866                 :                :         /* only look at names that can be ours */
                               4867         [ -  + ]:           5958 :         if (strncmp(spill_de->d_name, "xid", 3) == 0)
                               4868                 :                :         {
 2741 alvherre@alvh.no-ip.     4869                 :UBC           0 :             snprintf(path, sizeof(path),
                               4870                 :                :                      "%s/%s/%s", PG_REPLSLOT_DIR, slotname,
                               4871                 :              0 :                      spill_de->d_name);
                               4872                 :                : 
                               4873         [ #  # ]:              0 :             if (unlink(path) != 0)
                               4874         [ #  # ]:              0 :                 ereport(ERROR,
                               4875                 :                :                         (errcode_for_file_access(),
                               4876                 :                :                          errmsg("could not remove file \"%s\" during removal of %s/%s/xid*: %m",
                               4877                 :                :                                 path, PG_REPLSLOT_DIR, slotname)));
                               4878                 :                :         }
                               4879                 :                :     }
 2741 alvherre@alvh.no-ip.     4880                 :CBC        1986 :     FreeDir(spill_dir);
                               4881                 :                : }
                               4882                 :                : 
                               4883                 :                : /*
                               4884                 :                :  * Given a replication slot, transaction ID and segment number, fill in the
                               4885                 :                :  * corresponding spill file into 'path', which is a caller-owned buffer of size
                               4886                 :                :  * at least MAXPGPATH.
                               4887                 :                :  */
                               4888                 :                : static void
                               4889                 :           3430 : ReorderBufferSerializedPath(char *path, ReplicationSlot *slot, TransactionId xid,
                               4890                 :                :                             XLogSegNo segno)
                               4891                 :                : {
                               4892                 :                :     XLogRecPtr  recptr;
                               4893                 :                : 
 2616                          4894                 :           3430 :     XLogSegNoOffsetToRecPtr(segno, 0, wal_segment_size, recptr);
                               4895                 :                : 
  372 michael@paquier.xyz      4896                 :           3430 :     snprintf(path, MAXPGPATH, "%s/%s/xid-%u-lsn-%X-%X.spill",
                               4897                 :                :              PG_REPLSLOT_DIR,
 2690 tgl@sss.pgh.pa.us        4898                 :           3430 :              NameStr(MyReplicationSlot->data.name),
 1656 peter@eisentraut.org     4899                 :           3430 :              xid, LSN_FORMAT_ARGS(recptr));
 2741 alvherre@alvh.no-ip.     4900                 :           3430 : }
                               4901                 :                : 
                               4902                 :                : /*
                               4903                 :                :  * Delete all data spilled to disk after we've restarted/crashed. It will be
                               4904                 :                :  * recreated when the respective slots are reused.
                               4905                 :                :  */
                               4906                 :                : void
 4205 rhaas@postgresql.org     4907                 :            887 : StartupReorderBuffer(void)
                               4908                 :                : {
                               4909                 :                :     DIR        *logical_dir;
                               4910                 :                :     struct dirent *logical_de;
                               4911                 :                : 
  372 michael@paquier.xyz      4912                 :            887 :     logical_dir = AllocateDir(PG_REPLSLOT_DIR);
                               4913         [ +  + ]:           2757 :     while ((logical_de = ReadDir(logical_dir, PG_REPLSLOT_DIR)) != NULL)
                               4914                 :                :     {
 4205 rhaas@postgresql.org     4915         [ +  + ]:           1870 :         if (strcmp(logical_de->d_name, ".") == 0 ||
                               4916         [ +  + ]:            983 :             strcmp(logical_de->d_name, "..") == 0)
                               4917                 :           1774 :             continue;
                               4918                 :                : 
                               4919                 :                :         /* if it cannot be a slot, skip the directory */
   45 akapila@postgresql.o     4920         [ -  + ]:GNC          96 :         if (!ReplicationSlotValidateName(logical_de->d_name, true, DEBUG2))
 4205 rhaas@postgresql.org     4921                 :UBC           0 :             continue;
                               4922                 :                : 
                               4923                 :                :         /*
                               4924                 :                :          * ok, has to be a surviving logical slot, iterate and delete
                               4925                 :                :          * everything starting with xid-*
                               4926                 :                :          */
 2741 alvherre@alvh.no-ip.     4927                 :CBC          96 :         ReorderBufferCleanupSerializedTXNs(logical_de->d_name);
                               4928                 :                :     }
 4205 rhaas@postgresql.org     4929                 :            887 :     FreeDir(logical_dir);
                               4930                 :            887 : }
                               4931                 :                : 
                               4932                 :                : /* ---------------------------------------
                               4933                 :                :  * toast reassembly support
                               4934                 :                :  * ---------------------------------------
                               4935                 :                :  */
                               4936                 :                : 
                               4937                 :                : /*
                               4938                 :                :  * Initialize per tuple toast reconstruction support.
                               4939                 :                :  */
                               4940                 :                : static void
                               4941                 :             35 : ReorderBufferToastInitHash(ReorderBuffer *rb, ReorderBufferTXN *txn)
                               4942                 :                : {
                               4943                 :                :     HASHCTL     hash_ctl;
                               4944                 :                : 
                               4945         [ -  + ]:             35 :     Assert(txn->toast_hash == NULL);
                               4946                 :                : 
                               4947                 :             35 :     hash_ctl.keysize = sizeof(Oid);
                               4948                 :             35 :     hash_ctl.entrysize = sizeof(ReorderBufferToastEnt);
                               4949                 :             35 :     hash_ctl.hcxt = rb->context;
                               4950                 :             35 :     txn->toast_hash = hash_create("ReorderBufferToastHash", 5, &hash_ctl,
                               4951                 :                :                                   HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
                               4952                 :             35 : }
                               4953                 :                : 
                               4954                 :                : /*
                               4955                 :                :  * Per toast-chunk handling for toast reconstruction
                               4956                 :                :  *
                               4957                 :                :  * Appends a toast chunk so we can reconstruct it when the tuple "owning" the
                               4958                 :                :  * toasted Datum comes along.
                               4959                 :                :  */
                               4960                 :                : static void
                               4961                 :           1830 : ReorderBufferToastAppendChunk(ReorderBuffer *rb, ReorderBufferTXN *txn,
                               4962                 :                :                               Relation relation, ReorderBufferChange *change)
                               4963                 :                : {
                               4964                 :                :     ReorderBufferToastEnt *ent;
                               4965                 :                :     HeapTuple   newtup;
                               4966                 :                :     bool        found;
                               4967                 :                :     int32       chunksize;
                               4968                 :                :     bool        isnull;
                               4969                 :                :     Pointer     chunk;
                               4970                 :           1830 :     TupleDesc   desc = RelationGetDescr(relation);
                               4971                 :                :     Oid         chunk_id;
                               4972                 :                :     int32       chunk_seq;
                               4973                 :                : 
                               4974         [ +  + ]:           1830 :     if (txn->toast_hash == NULL)
                               4975                 :             35 :         ReorderBufferToastInitHash(rb, txn);
                               4976                 :                : 
                               4977         [ -  + ]:           1830 :     Assert(IsToastRelation(relation));
                               4978                 :                : 
 4201 tgl@sss.pgh.pa.us        4979                 :           1830 :     newtup = change->data.tp.newtuple;
  586 msawada@postgresql.o     4980                 :           1830 :     chunk_id = DatumGetObjectId(fastgetattr(newtup, 1, desc, &isnull));
 4205 rhaas@postgresql.org     4981         [ -  + ]:           1830 :     Assert(!isnull);
  586 msawada@postgresql.o     4982                 :           1830 :     chunk_seq = DatumGetInt32(fastgetattr(newtup, 2, desc, &isnull));
 4205 rhaas@postgresql.org     4983         [ -  + ]:           1830 :     Assert(!isnull);
                               4984                 :                : 
                               4985                 :                :     ent = (ReorderBufferToastEnt *)
  943 peter@eisentraut.org     4986                 :           1830 :         hash_search(txn->toast_hash, &chunk_id, HASH_ENTER, &found);
                               4987                 :                : 
 4205 rhaas@postgresql.org     4988         [ +  + ]:           1830 :     if (!found)
                               4989                 :                :     {
                               4990         [ -  + ]:             49 :         Assert(ent->chunk_id == chunk_id);
                               4991                 :             49 :         ent->num_chunks = 0;
                               4992                 :             49 :         ent->last_chunk_seq = 0;
                               4993                 :             49 :         ent->size = 0;
                               4994                 :             49 :         ent->reconstructed = NULL;
                               4995                 :             49 :         dlist_init(&ent->chunks);
                               4996                 :                : 
                               4997         [ -  + ]:             49 :         if (chunk_seq != 0)
 4205 rhaas@postgresql.org     4998         [ #  # ]:UBC           0 :             elog(ERROR, "got sequence entry %d for toast chunk %u instead of seq 0",
                               4999                 :                :                  chunk_seq, chunk_id);
                               5000                 :                :     }
 4205 rhaas@postgresql.org     5001   [ +  -  -  + ]:CBC        1781 :     else if (found && chunk_seq != ent->last_chunk_seq + 1)
 4205 rhaas@postgresql.org     5002         [ #  # ]:UBC           0 :         elog(ERROR, "got sequence entry %d for toast chunk %u instead of seq %d",
                               5003                 :                :              chunk_seq, chunk_id, ent->last_chunk_seq + 1);
                               5004                 :                : 
  586 msawada@postgresql.o     5005                 :CBC        1830 :     chunk = DatumGetPointer(fastgetattr(newtup, 3, desc, &isnull));
 4205 rhaas@postgresql.org     5006         [ -  + ]:           1830 :     Assert(!isnull);
                               5007                 :                : 
                               5008                 :                :     /* calculate size so we can allocate the right size at once later */
                               5009         [ +  - ]:           1830 :     if (!VARATT_IS_EXTENDED(chunk))
                               5010                 :           1830 :         chunksize = VARSIZE(chunk) - VARHDRSZ;
 4205 rhaas@postgresql.org     5011         [ #  # ]:UBC           0 :     else if (VARATT_IS_SHORT(chunk))
                               5012                 :                :         /* could happen due to heap_form_tuple doing its thing */
                               5013                 :              0 :         chunksize = VARSIZE_SHORT(chunk) - VARHDRSZ_SHORT;
                               5014                 :                :     else
                               5015         [ #  # ]:              0 :         elog(ERROR, "unexpected type of toast chunk");
                               5016                 :                : 
 4205 rhaas@postgresql.org     5017                 :CBC        1830 :     ent->size += chunksize;
                               5018                 :           1830 :     ent->last_chunk_seq = chunk_seq;
                               5019                 :           1830 :     ent->num_chunks++;
                               5020                 :           1830 :     dlist_push_tail(&ent->chunks, &change->node);
                               5021                 :           1830 : }
                               5022                 :                : 
                               5023                 :                : /*
                               5024                 :                :  * Rejigger change->newtuple to point to in-memory toast tuples instead of
                               5025                 :                :  * on-disk toast tuples that may no longer exist (think DROP TABLE or VACUUM).
                               5026                 :                :  *
                               5027                 :                :  * We cannot replace unchanged toast tuples though, so those will still point
                               5028                 :                :  * to on-disk toast data.
                               5029                 :                :  *
                               5030                 :                :  * While updating the existing change with detoasted tuple data, we need to
                               5031                 :                :  * update the memory accounting info, because the change size will differ.
                               5032                 :                :  * Otherwise the accounting may get out of sync, triggering serialization
                               5033                 :                :  * at unexpected times.
                               5034                 :                :  *
                               5035                 :                :  * We simply subtract size of the change before rejiggering the tuple, and
                               5036                 :                :  * then add the new size. This makes it look like the change was removed
                               5037                 :                :  * and then added back, except it only tweaks the accounting info.
                               5038                 :                :  *
                               5039                 :                :  * In particular it can't trigger serialization, which would be pointless
                               5040                 :                :  * anyway as it happens during commit processing right before handing
                               5041                 :                :  * the change to the output plugin.
                               5042                 :                :  */
                               5043                 :                : static void
                               5044                 :         337003 : ReorderBufferToastReplace(ReorderBuffer *rb, ReorderBufferTXN *txn,
                               5045                 :                :                           Relation relation, ReorderBufferChange *change)
                               5046                 :                : {
                               5047                 :                :     TupleDesc   desc;
                               5048                 :                :     int         natt;
                               5049                 :                :     Datum      *attrs;
                               5050                 :                :     bool       *isnull;
                               5051                 :                :     bool       *free;
                               5052                 :                :     HeapTuple   tmphtup;
                               5053                 :                :     Relation    toast_rel;
                               5054                 :                :     TupleDesc   toast_desc;
                               5055                 :                :     MemoryContext oldcontext;
                               5056                 :                :     HeapTuple   newtup;
                               5057                 :                :     Size        old_size;
                               5058                 :                : 
                               5059                 :                :     /* no toast tuples changed */
                               5060         [ +  + ]:         337003 :     if (txn->toast_hash == NULL)
                               5061                 :         336757 :         return;
                               5062                 :                : 
                               5063                 :                :     /*
                               5064                 :                :      * We're going to modify the size of the change. So, to make sure the
                               5065                 :                :      * accounting is correct we record the current change size and then after
                               5066                 :                :      * re-computing the change we'll subtract the recorded size and then
                               5067                 :                :      * re-add the new change size at the end. We don't immediately subtract
                               5068                 :                :      * the old size because if there is any error before we add the new size,
                               5069                 :                :      * we will release the changes and that will update the accounting info
                               5070                 :                :      * (subtracting the size from the counters). And we don't want to
                               5071                 :                :      * underflow there.
                               5072                 :                :      */
 1454 akapila@postgresql.o     5073                 :            246 :     old_size = ReorderBufferChangeSize(change);
                               5074                 :                : 
 4205 rhaas@postgresql.org     5075                 :            246 :     oldcontext = MemoryContextSwitchTo(rb->context);
                               5076                 :                : 
                               5077                 :                :     /* we should only have toast tuples in an INSERT or UPDATE */
 4201 tgl@sss.pgh.pa.us        5078         [ -  + ]:            246 :     Assert(change->data.tp.newtuple);
                               5079                 :                : 
 4205 rhaas@postgresql.org     5080                 :            246 :     desc = RelationGetDescr(relation);
                               5081                 :                : 
                               5082                 :            246 :     toast_rel = RelationIdGetRelation(relation->rd_rel->reltoastrelid);
 2190 tgl@sss.pgh.pa.us        5083         [ -  + ]:            246 :     if (!RelationIsValid(toast_rel))
 1445 akapila@postgresql.o     5084         [ #  # ]:UBC           0 :         elog(ERROR, "could not open toast relation with OID %u (base relation \"%s\")",
                               5085                 :                :              relation->rd_rel->reltoastrelid, RelationGetRelationName(relation));
                               5086                 :                : 
 4205 rhaas@postgresql.org     5087                 :CBC         246 :     toast_desc = RelationGetDescr(toast_rel);
                               5088                 :                : 
                               5089                 :                :     /* should we allocate from stack instead? */
                               5090                 :            246 :     attrs = palloc0(sizeof(Datum) * desc->natts);
                               5091                 :            246 :     isnull = palloc0(sizeof(bool) * desc->natts);
                               5092                 :            246 :     free = palloc0(sizeof(bool) * desc->natts);
                               5093                 :                : 
 4201 tgl@sss.pgh.pa.us        5094                 :            246 :     newtup = change->data.tp.newtuple;
                               5095                 :                : 
  586 msawada@postgresql.o     5096                 :            246 :     heap_deform_tuple(newtup, desc, attrs, isnull);
                               5097                 :                : 
 4205 rhaas@postgresql.org     5098         [ +  + ]:            757 :     for (natt = 0; natt < desc->natts; natt++)
                               5099                 :                :     {
 2939 andres@anarazel.de       5100                 :            511 :         Form_pg_attribute attr = TupleDescAttr(desc, natt);
                               5101                 :                :         ReorderBufferToastEnt *ent;
                               5102                 :                :         struct varlena *varlena;
                               5103                 :                : 
                               5104                 :                :         /* va_rawsize is the size of the original datum -- including header */
                               5105                 :                :         struct varatt_external toast_pointer;
                               5106                 :                :         struct varatt_indirect redirect_pointer;
 4205 rhaas@postgresql.org     5107                 :            511 :         struct varlena *new_datum = NULL;
                               5108                 :                :         struct varlena *reconstructed;
                               5109                 :                :         dlist_iter  it;
                               5110                 :            511 :         Size        data_done = 0;
                               5111                 :                : 
                               5112                 :                :         /* system columns aren't toasted */
                               5113         [ -  + ]:            511 :         if (attr->attnum < 0)
                               5114                 :            463 :             continue;
                               5115                 :                : 
                               5116         [ -  + ]:            511 :         if (attr->attisdropped)
 4205 rhaas@postgresql.org     5117                 :UBC           0 :             continue;
                               5118                 :                : 
                               5119                 :                :         /* not a varlena datatype */
 4205 rhaas@postgresql.org     5120         [ +  + ]:CBC         511 :         if (attr->attlen != -1)
                               5121                 :            241 :             continue;
                               5122                 :                : 
                               5123                 :                :         /* no data */
                               5124         [ +  + ]:            270 :         if (isnull[natt])
                               5125                 :             12 :             continue;
                               5126                 :                : 
                               5127                 :                :         /* ok, we know we have a toast datum */
                               5128                 :            258 :         varlena = (struct varlena *) DatumGetPointer(attrs[natt]);
                               5129                 :                : 
                               5130                 :                :         /* no need to do anything if the tuple isn't external */
                               5131         [ +  + ]:            258 :         if (!VARATT_IS_EXTERNAL(varlena))
                               5132                 :            202 :             continue;
                               5133                 :                : 
                               5134   [ -  +  -  +  :             56 :         VARATT_EXTERNAL_GET_POINTER(toast_pointer, varlena);
                                     +  -  -  +  -  
                                                 + ]
                               5135                 :                : 
                               5136                 :                :         /*
                               5137                 :                :          * Check whether the toast tuple changed, replace if so.
                               5138                 :                :          */
                               5139                 :                :         ent = (ReorderBufferToastEnt *)
                               5140                 :             56 :             hash_search(txn->toast_hash,
                               5141                 :                :                         &toast_pointer.va_valueid,
                               5142                 :                :                         HASH_FIND,
                               5143                 :                :                         NULL);
                               5144         [ +  + ]:             56 :         if (ent == NULL)
                               5145                 :              8 :             continue;
                               5146                 :                : 
                               5147                 :                :         new_datum =
                               5148                 :             48 :             (struct varlena *) palloc0(INDIRECT_POINTER_SIZE);
                               5149                 :                : 
                               5150                 :             48 :         free[natt] = true;
                               5151                 :                : 
                               5152                 :             48 :         reconstructed = palloc0(toast_pointer.va_rawsize);
                               5153                 :                : 
                               5154                 :             48 :         ent->reconstructed = reconstructed;
                               5155                 :                : 
                               5156                 :                :         /* stitch toast tuple back together from its parts */
                               5157   [ +  -  +  + ]:           1827 :         dlist_foreach(it, &ent->chunks)
                               5158                 :                :         {
                               5159                 :                :             bool        cisnull;
                               5160                 :                :             ReorderBufferChange *cchange;
                               5161                 :                :             HeapTuple   ctup;
                               5162                 :                :             Pointer     chunk;
                               5163                 :                : 
 4201 tgl@sss.pgh.pa.us        5164                 :           1779 :             cchange = dlist_container(ReorderBufferChange, node, it.cur);
                               5165                 :           1779 :             ctup = cchange->data.tp.newtuple;
  586 msawada@postgresql.o     5166                 :           1779 :             chunk = DatumGetPointer(fastgetattr(ctup, 3, toast_desc, &cisnull));
                               5167                 :                : 
  737 michael@paquier.xyz      5168         [ -  + ]:           1779 :             Assert(!cisnull);
 4205 rhaas@postgresql.org     5169         [ -  + ]:           1779 :             Assert(!VARATT_IS_EXTERNAL(chunk));
                               5170         [ -  + ]:           1779 :             Assert(!VARATT_IS_SHORT(chunk));
                               5171                 :                : 
                               5172                 :           1779 :             memcpy(VARDATA(reconstructed) + data_done,
                               5173                 :           1779 :                    VARDATA(chunk),
                               5174                 :           1779 :                    VARSIZE(chunk) - VARHDRSZ);
                               5175                 :           1779 :             data_done += VARSIZE(chunk) - VARHDRSZ;
                               5176                 :                :         }
 1632                          5177         [ -  + ]:             48 :         Assert(data_done == VARATT_EXTERNAL_GET_EXTSIZE(toast_pointer));
                               5178                 :                : 
                               5179                 :                :         /* make sure its marked as compressed or not */
 4205                          5180         [ +  + ]:             48 :         if (VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer))
                               5181                 :              5 :             SET_VARSIZE_COMPRESSED(reconstructed, data_done + VARHDRSZ);
                               5182                 :                :         else
                               5183                 :             43 :             SET_VARSIZE(reconstructed, data_done + VARHDRSZ);
                               5184                 :                : 
                               5185                 :             48 :         memset(&redirect_pointer, 0, sizeof(redirect_pointer));
                               5186                 :             48 :         redirect_pointer.pointer = reconstructed;
                               5187                 :                : 
                               5188                 :             48 :         SET_VARTAG_EXTERNAL(new_datum, VARTAG_INDIRECT);
                               5189                 :             48 :         memcpy(VARDATA_EXTERNAL(new_datum), &redirect_pointer,
                               5190                 :                :                sizeof(redirect_pointer));
                               5191                 :                : 
                               5192                 :             48 :         attrs[natt] = PointerGetDatum(new_datum);
                               5193                 :                :     }
                               5194                 :                : 
                               5195                 :                :     /*
                               5196                 :                :      * Build tuple in separate memory & copy tuple back into the tuplebuf
                               5197                 :                :      * passed to the output plugin. We can't directly heap_fill_tuple() into
                               5198                 :                :      * the tuplebuf because attrs[] will point back into the current content.
                               5199                 :                :      */
 4201 tgl@sss.pgh.pa.us        5200                 :            246 :     tmphtup = heap_form_tuple(desc, attrs, isnull);
  586 msawada@postgresql.o     5201         [ -  + ]:            246 :     Assert(newtup->t_len <= MaxHeapTupleSize);
                               5202         [ -  + ]:            246 :     Assert(newtup->t_data == (HeapTupleHeader) ((char *) newtup + HEAPTUPLESIZE));
                               5203                 :                : 
                               5204                 :            246 :     memcpy(newtup->t_data, tmphtup->t_data, tmphtup->t_len);
                               5205                 :            246 :     newtup->t_len = tmphtup->t_len;
                               5206                 :                : 
                               5207                 :                :     /*
                               5208                 :                :      * free resources we won't further need, more persistent stuff will be
                               5209                 :                :      * free'd in ReorderBufferToastReset().
                               5210                 :                :      */
 4205 rhaas@postgresql.org     5211                 :            246 :     RelationClose(toast_rel);
 4201 tgl@sss.pgh.pa.us        5212                 :            246 :     pfree(tmphtup);
 4205 rhaas@postgresql.org     5213         [ +  + ]:            757 :     for (natt = 0; natt < desc->natts; natt++)
                               5214                 :                :     {
                               5215         [ +  + ]:            511 :         if (free[natt])
                               5216                 :             48 :             pfree(DatumGetPointer(attrs[natt]));
                               5217                 :                :     }
                               5218                 :            246 :     pfree(attrs);
                               5219                 :            246 :     pfree(free);
                               5220                 :            246 :     pfree(isnull);
                               5221                 :                : 
                               5222                 :            246 :     MemoryContextSwitchTo(oldcontext);
                               5223                 :                : 
                               5224                 :                :     /* subtract the old change size */
  521 msawada@postgresql.o     5225                 :            246 :     ReorderBufferChangeMemoryUpdate(rb, change, NULL, false, old_size);
                               5226                 :                :     /* now add the change back, with the correct size */
                               5227                 :            246 :     ReorderBufferChangeMemoryUpdate(rb, change, NULL, true,
                               5228                 :                :                                     ReorderBufferChangeSize(change));
                               5229                 :                : }
                               5230                 :                : 
                               5231                 :                : /*
                               5232                 :                :  * Free all resources allocated for toast reconstruction.
                               5233                 :                :  */
                               5234                 :                : static void
 4205 rhaas@postgresql.org     5235                 :         340552 : ReorderBufferToastReset(ReorderBuffer *rb, ReorderBufferTXN *txn)
                               5236                 :                : {
                               5237                 :                :     HASH_SEQ_STATUS hstat;
                               5238                 :                :     ReorderBufferToastEnt *ent;
                               5239                 :                : 
                               5240         [ +  + ]:         340552 :     if (txn->toast_hash == NULL)
                               5241                 :         340517 :         return;
                               5242                 :                : 
                               5243                 :                :     /* sequentially walk over the hash and free everything */
                               5244                 :             35 :     hash_seq_init(&hstat, txn->toast_hash);
                               5245         [ +  + ]:             84 :     while ((ent = (ReorderBufferToastEnt *) hash_seq_search(&hstat)) != NULL)
                               5246                 :                :     {
                               5247                 :                :         dlist_mutable_iter it;
                               5248                 :                : 
                               5249         [ +  + ]:             49 :         if (ent->reconstructed != NULL)
                               5250                 :             48 :             pfree(ent->reconstructed);
                               5251                 :                : 
                               5252   [ +  -  +  + ]:           1879 :         dlist_foreach_modify(it, &ent->chunks)
                               5253                 :                :         {
                               5254                 :           1830 :             ReorderBufferChange *change =
  841 tgl@sss.pgh.pa.us        5255                 :           1830 :                 dlist_container(ReorderBufferChange, node, it.cur);
                               5256                 :                : 
 4205 rhaas@postgresql.org     5257                 :           1830 :             dlist_delete(&change->node);
  178 heikki.linnakangas@i     5258                 :           1830 :             ReorderBufferFreeChange(rb, change, true);
                               5259                 :                :         }
                               5260                 :                :     }
                               5261                 :                : 
 4205 rhaas@postgresql.org     5262                 :             35 :     hash_destroy(txn->toast_hash);
                               5263                 :             35 :     txn->toast_hash = NULL;
                               5264                 :                : }
                               5265                 :                : 
                               5266                 :                : 
                               5267                 :                : /* ---------------------------------------
                               5268                 :                :  * Visibility support for logical decoding
                               5269                 :                :  *
                               5270                 :                :  *
                               5271                 :                :  * Lookup actual cmin/cmax values when using decoding snapshot. We can't
                               5272                 :                :  * always rely on stored cmin/cmax values because of two scenarios:
                               5273                 :                :  *
                               5274                 :                :  * * A tuple got changed multiple times during a single transaction and thus
                               5275                 :                :  *   has got a combo CID. Combo CIDs are only valid for the duration of a
                               5276                 :                :  *   single transaction.
                               5277                 :                :  * * A tuple with a cmin but no cmax (and thus no combo CID) got
                               5278                 :                :  *   deleted/updated in another transaction than the one which created it
                               5279                 :                :  *   which we are looking at right now. As only one of cmin, cmax or combo CID
                               5280                 :                :  *   is actually stored in the heap we don't have access to the value we
                               5281                 :                :  *   need anymore.
                               5282                 :                :  *
                               5283                 :                :  * To resolve those problems we have a per-transaction hash of (cmin,
                               5284                 :                :  * cmax) tuples keyed by (relfilelocator, ctid) which contains the actual
                               5285                 :                :  * (cmin, cmax) values. That also takes care of combo CIDs by simply
                               5286                 :                :  * not caring about them at all. As we have the real cmin/cmax values
                               5287                 :                :  * combo CIDs aren't interesting.
                               5288                 :                :  *
                               5289                 :                :  * As we only care about catalog tuples here the overhead of this
                               5290                 :                :  * hashtable should be acceptable.
                               5291                 :                :  *
                               5292                 :                :  * Heap rewrites complicate this a bit, check rewriteheap.c for
                               5293                 :                :  * details.
                               5294                 :                :  * -------------------------------------------------------------------------
                               5295                 :                :  */
                               5296                 :                : 
                               5297                 :                : /* struct for sorting mapping files by LSN efficiently */
                               5298                 :                : typedef struct RewriteMappingFile
                               5299                 :                : {
                               5300                 :                :     XLogRecPtr  lsn;
                               5301                 :                :     char        fname[MAXPGPATH];
                               5302                 :                : } RewriteMappingFile;
                               5303                 :                : 
                               5304                 :                : #ifdef NOT_USED
                               5305                 :                : static void
                               5306                 :                : DisplayMapping(HTAB *tuplecid_data)
                               5307                 :                : {
                               5308                 :                :     HASH_SEQ_STATUS hstat;
                               5309                 :                :     ReorderBufferTupleCidEnt *ent;
                               5310                 :                : 
                               5311                 :                :     hash_seq_init(&hstat, tuplecid_data);
                               5312                 :                :     while ((ent = (ReorderBufferTupleCidEnt *) hash_seq_search(&hstat)) != NULL)
                               5313                 :                :     {
                               5314                 :                :         elog(DEBUG3, "mapping: node: %u/%u/%u tid: %u/%u cmin: %u, cmax: %u",
                               5315                 :                :              ent->key.rlocator.dbOid,
                               5316                 :                :              ent->key.rlocator.spcOid,
                               5317                 :                :              ent->key.rlocator.relNumber,
                               5318                 :                :              ItemPointerGetBlockNumber(&ent->key.tid),
                               5319                 :                :              ItemPointerGetOffsetNumber(&ent->key.tid),
                               5320                 :                :              ent->cmin,
                               5321                 :                :              ent->cmax
                               5322                 :                :             );
                               5323                 :                :     }
                               5324                 :                : }
                               5325                 :                : #endif
                               5326                 :                : 
                               5327                 :                : /*
                               5328                 :                :  * Apply a single mapping file to tuplecid_data.
                               5329                 :                :  *
                               5330                 :                :  * The mapping file has to have been verified to be a) committed b) for our
                               5331                 :                :  * transaction c) applied in LSN order.
                               5332                 :                :  */
                               5333                 :                : static void
                               5334                 :             27 : ApplyLogicalMappingFile(HTAB *tuplecid_data, Oid relid, const char *fname)
                               5335                 :                : {
                               5336                 :                :     char        path[MAXPGPATH];
                               5337                 :                :     int         fd;
                               5338                 :                :     int         readBytes;
                               5339                 :                :     LogicalRewriteMappingData map;
                               5340                 :                : 
  372 michael@paquier.xyz      5341                 :             27 :     sprintf(path, "%s/%s", PG_LOGICAL_MAPPINGS_DIR, fname);
 2905 peter_e@gmx.net          5342                 :             27 :     fd = OpenTransientFile(path, O_RDONLY | PG_BINARY);
 4205 rhaas@postgresql.org     5343         [ +  - ]:             27 :     if (fd < 0)
 4205 rhaas@postgresql.org     5344         [ #  # ]:UBC           0 :         ereport(ERROR,
                               5345                 :                :                 (errcode_for_file_access(),
                               5346                 :                :                  errmsg("could not open file \"%s\": %m", path)));
                               5347                 :                : 
                               5348                 :                :     while (true)
 4205 rhaas@postgresql.org     5349                 :CBC         209 :     {
                               5350                 :                :         ReorderBufferTupleCidKey key;
                               5351                 :                :         ReorderBufferTupleCidEnt *ent;
                               5352                 :                :         ReorderBufferTupleCidEnt *new_ent;
                               5353                 :                :         bool        found;
                               5354                 :                : 
                               5355                 :                :         /* be careful about padding */
                               5356                 :            236 :         memset(&key, 0, sizeof(ReorderBufferTupleCidKey));
                               5357                 :                : 
                               5358                 :                :         /* read all mappings till the end of the file */
 3094                          5359                 :            236 :         pgstat_report_wait_start(WAIT_EVENT_REORDER_LOGICAL_MAPPING_READ);
 4205                          5360                 :            236 :         readBytes = read(fd, &map, sizeof(LogicalRewriteMappingData));
 3094                          5361                 :            236 :         pgstat_report_wait_end();
                               5362                 :                : 
 4205                          5363         [ -  + ]:            236 :         if (readBytes < 0)
 4205 rhaas@postgresql.org     5364         [ #  # ]:UBC           0 :             ereport(ERROR,
                               5365                 :                :                     (errcode_for_file_access(),
                               5366                 :                :                      errmsg("could not read file \"%s\": %m",
                               5367                 :                :                             path)));
 4141 bruce@momjian.us         5368         [ +  + ]:CBC         236 :         else if (readBytes == 0)    /* EOF */
 4205 rhaas@postgresql.org     5369                 :             27 :             break;
                               5370         [ -  + ]:            209 :         else if (readBytes != sizeof(LogicalRewriteMappingData))
 4205 rhaas@postgresql.org     5371         [ #  # ]:UBC           0 :             ereport(ERROR,
                               5372                 :                :                     (errcode_for_file_access(),
                               5373                 :                :                      errmsg("could not read from file \"%s\": read %d instead of %d bytes",
                               5374                 :                :                             path, readBytes,
                               5375                 :                :                             (int32) sizeof(LogicalRewriteMappingData))));
                               5376                 :                : 
 1158 rhaas@postgresql.org     5377                 :CBC         209 :         key.rlocator = map.old_locator;
 4205                          5378                 :            209 :         ItemPointerCopy(&map.old_tid,
                               5379                 :                :                         &key.tid);
                               5380                 :                : 
                               5381                 :                : 
                               5382                 :                :         ent = (ReorderBufferTupleCidEnt *)
  943 peter@eisentraut.org     5383                 :            209 :             hash_search(tuplecid_data, &key, HASH_FIND, NULL);
                               5384                 :                : 
                               5385                 :                :         /* no existing mapping, no need to update */
 4205 rhaas@postgresql.org     5386         [ -  + ]:            209 :         if (!ent)
 4205 rhaas@postgresql.org     5387                 :UBC           0 :             continue;
                               5388                 :                : 
 1158 rhaas@postgresql.org     5389                 :CBC         209 :         key.rlocator = map.new_locator;
 4205                          5390                 :            209 :         ItemPointerCopy(&map.new_tid,
                               5391                 :                :                         &key.tid);
                               5392                 :                : 
                               5393                 :                :         new_ent = (ReorderBufferTupleCidEnt *)
  943 peter@eisentraut.org     5394                 :            209 :             hash_search(tuplecid_data, &key, HASH_ENTER, &found);
                               5395                 :                : 
 4205 rhaas@postgresql.org     5396         [ +  + ]:            209 :         if (found)
                               5397                 :                :         {
                               5398                 :                :             /*
                               5399                 :                :              * Make sure the existing mapping makes sense. We sometime update
                               5400                 :                :              * old records that did not yet have a cmax (e.g. pg_class' own
                               5401                 :                :              * entry while rewriting it) during rewrites, so allow that.
                               5402                 :                :              */
                               5403   [ +  -  -  + ]:              6 :             Assert(ent->cmin == InvalidCommandId || ent->cmin == new_ent->cmin);
                               5404   [ -  +  -  - ]:              6 :             Assert(ent->cmax == InvalidCommandId || ent->cmax == new_ent->cmax);
                               5405                 :                :         }
                               5406                 :                :         else
                               5407                 :                :         {
                               5408                 :                :             /* update mapping */
                               5409                 :            203 :             new_ent->cmin = ent->cmin;
                               5410                 :            203 :             new_ent->cmax = ent->cmax;
                               5411                 :            203 :             new_ent->combocid = ent->combocid;
                               5412                 :                :         }
                               5413                 :                :     }
                               5414                 :                : 
 2254 peter@eisentraut.org     5415         [ -  + ]:             27 :     if (CloseTransientFile(fd) != 0)
 2373 michael@paquier.xyz      5416         [ #  # ]:UBC           0 :         ereport(ERROR,
                               5417                 :                :                 (errcode_for_file_access(),
                               5418                 :                :                  errmsg("could not close file \"%s\": %m", path)));
 4205 rhaas@postgresql.org     5419                 :CBC          27 : }
                               5420                 :                : 
                               5421                 :                : 
                               5422                 :                : /*
                               5423                 :                :  * Check whether the TransactionId 'xid' is in the pre-sorted array 'xip'.
                               5424                 :                :  */
                               5425                 :                : static bool
                               5426                 :            348 : TransactionIdInArray(TransactionId xid, TransactionId *xip, Size num)
                               5427                 :                : {
                               5428                 :            348 :     return bsearch(&xid, xip, num,
                               5429                 :            348 :                    sizeof(TransactionId), xidComparator) != NULL;
                               5430                 :                : }
                               5431                 :                : 
                               5432                 :                : /*
                               5433                 :                :  * list_sort() comparator for sorting RewriteMappingFiles in LSN order.
                               5434                 :                :  */
                               5435                 :                : static int
 2244 tgl@sss.pgh.pa.us        5436                 :             36 : file_sort_by_lsn(const ListCell *a_p, const ListCell *b_p)
                               5437                 :                : {
                               5438                 :             36 :     RewriteMappingFile *a = (RewriteMappingFile *) lfirst(a_p);
                               5439                 :             36 :     RewriteMappingFile *b = (RewriteMappingFile *) lfirst(b_p);
                               5440                 :                : 
  568 nathan@postgresql.or     5441                 :             36 :     return pg_cmp_u64(a->lsn, b->lsn);
                               5442                 :                : }
                               5443                 :                : 
                               5444                 :                : /*
                               5445                 :                :  * Apply any existing logical remapping files if there are any targeted at our
                               5446                 :                :  * transaction for relid.
                               5447                 :                :  */
                               5448                 :                : static void
 4205 rhaas@postgresql.org     5449                 :             11 : UpdateLogicalMappings(HTAB *tuplecid_data, Oid relid, Snapshot snapshot)
                               5450                 :                : {
                               5451                 :                :     DIR        *mapping_dir;
                               5452                 :                :     struct dirent *mapping_de;
                               5453                 :             11 :     List       *files = NIL;
                               5454                 :                :     ListCell   *file;
                               5455         [ +  - ]:             11 :     Oid         dboid = IsSharedRelation(relid) ? InvalidOid : MyDatabaseId;
                               5456                 :                : 
  372 michael@paquier.xyz      5457                 :             11 :     mapping_dir = AllocateDir(PG_LOGICAL_MAPPINGS_DIR);
                               5458         [ +  + ]:            573 :     while ((mapping_de = ReadDir(mapping_dir, PG_LOGICAL_MAPPINGS_DIR)) != NULL)
                               5459                 :                :     {
                               5460                 :                :         Oid         f_dboid;
                               5461                 :                :         Oid         f_relid;
                               5462                 :                :         TransactionId f_mapped_xid;
                               5463                 :                :         TransactionId f_create_xid;
                               5464                 :                :         XLogRecPtr  f_lsn;
                               5465                 :                :         uint32      f_hi,
                               5466                 :                :                     f_lo;
                               5467                 :                :         RewriteMappingFile *f;
                               5468                 :                : 
 4205 rhaas@postgresql.org     5469         [ +  + ]:            562 :         if (strcmp(mapping_de->d_name, ".") == 0 ||
                               5470         [ +  + ]:            551 :             strcmp(mapping_de->d_name, "..") == 0)
                               5471                 :            535 :             continue;
                               5472                 :                : 
                               5473                 :                :         /* Ignore files that aren't ours */
                               5474         [ -  + ]:            540 :         if (strncmp(mapping_de->d_name, "map-", 4) != 0)
 4205 rhaas@postgresql.org     5475                 :UBC           0 :             continue;
                               5476                 :                : 
 4205 rhaas@postgresql.org     5477         [ -  + ]:CBC         540 :         if (sscanf(mapping_de->d_name, LOGICAL_REWRITE_FORMAT,
                               5478                 :                :                    &f_dboid, &f_relid, &f_hi, &f_lo,
                               5479                 :                :                    &f_mapped_xid, &f_create_xid) != 6)
 4147 tgl@sss.pgh.pa.us        5480         [ #  # ]:UBC           0 :             elog(ERROR, "could not parse filename \"%s\"", mapping_de->d_name);
                               5481                 :                : 
 4205 rhaas@postgresql.org     5482                 :CBC         540 :         f_lsn = ((uint64) f_hi) << 32 | f_lo;
                               5483                 :                : 
                               5484                 :                :         /* mapping for another database */
                               5485         [ -  + ]:            540 :         if (f_dboid != dboid)
 4205 rhaas@postgresql.org     5486                 :UBC           0 :             continue;
                               5487                 :                : 
                               5488                 :                :         /* mapping for another relation */
 4205 rhaas@postgresql.org     5489         [ +  + ]:CBC         540 :         if (f_relid != relid)
                               5490                 :             60 :             continue;
                               5491                 :                : 
                               5492                 :                :         /* did the creating transaction abort? */
                               5493         [ +  + ]:            480 :         if (!TransactionIdDidCommit(f_create_xid))
                               5494                 :            132 :             continue;
                               5495                 :                : 
                               5496                 :                :         /* not for our transaction */
                               5497         [ +  + ]:            348 :         if (!TransactionIdInArray(f_mapped_xid, snapshot->subxip, snapshot->subxcnt))
                               5498                 :            321 :             continue;
                               5499                 :                : 
                               5500                 :                :         /* ok, relevant, queue for apply */
                               5501                 :             27 :         f = palloc(sizeof(RewriteMappingFile));
                               5502                 :             27 :         f->lsn = f_lsn;
                               5503                 :             27 :         strcpy(f->fname, mapping_de->d_name);
                               5504                 :             27 :         files = lappend(files, f);
                               5505                 :                :     }
                               5506                 :             11 :     FreeDir(mapping_dir);
                               5507                 :                : 
                               5508                 :                :     /* sort files so we apply them in LSN order */
 2244 tgl@sss.pgh.pa.us        5509                 :             11 :     list_sort(files, file_sort_by_lsn);
                               5510                 :                : 
                               5511   [ +  +  +  +  :             38 :     foreach(file, files)
                                              +  + ]
                               5512                 :                :     {
                               5513                 :             27 :         RewriteMappingFile *f = (RewriteMappingFile *) lfirst(file);
                               5514                 :                : 
 4147                          5515         [ -  + ]:             27 :         elog(DEBUG1, "applying mapping: \"%s\" in %u", f->fname,
                               5516                 :                :              snapshot->subxip[0]);
 4205 rhaas@postgresql.org     5517                 :             27 :         ApplyLogicalMappingFile(tuplecid_data, relid, f->fname);
                               5518                 :             27 :         pfree(f);
                               5519                 :                :     }
                               5520                 :             11 : }
                               5521                 :                : 
                               5522                 :                : /*
                               5523                 :                :  * Lookup cmin/cmax of a tuple, during logical decoding where we can't rely on
                               5524                 :                :  * combo CIDs.
                               5525                 :                :  */
                               5526                 :                : bool
                               5527                 :            768 : ResolveCminCmaxDuringDecoding(HTAB *tuplecid_data,
                               5528                 :                :                               Snapshot snapshot,
                               5529                 :                :                               HeapTuple htup, Buffer buffer,
                               5530                 :                :                               CommandId *cmin, CommandId *cmax)
                               5531                 :                : {
                               5532                 :                :     ReorderBufferTupleCidKey key;
                               5533                 :                :     ReorderBufferTupleCidEnt *ent;
                               5534                 :                :     ForkNumber  forkno;
                               5535                 :                :     BlockNumber blockno;
 4141 bruce@momjian.us         5536                 :            768 :     bool        updated_mapping = false;
                               5537                 :                : 
                               5538                 :                :     /*
                               5539                 :                :      * Return unresolved if tuplecid_data is not valid.  That's because when
                               5540                 :                :      * streaming in-progress transactions we may run into tuples with the CID
                               5541                 :                :      * before actually decoding them.  Think e.g. about INSERT followed by
                               5542                 :                :      * TRUNCATE, where the TRUNCATE may not be decoded yet when applying the
                               5543                 :                :      * INSERT.  So in such cases, we assume the CID is from the future
                               5544                 :                :      * command.
                               5545                 :                :      */
 1855 akapila@postgresql.o     5546         [ +  + ]:            768 :     if (tuplecid_data == NULL)
                               5547                 :             11 :         return false;
                               5548                 :                : 
                               5549                 :                :     /* be careful about padding */
 4205 rhaas@postgresql.org     5550                 :            757 :     memset(&key, 0, sizeof(key));
                               5551                 :                : 
                               5552         [ -  + ]:            757 :     Assert(!BufferIsLocal(buffer));
                               5553                 :                : 
                               5554                 :                :     /*
                               5555                 :                :      * get relfilelocator from the buffer, no convenient way to access it
                               5556                 :                :      * other than that.
                               5557                 :                :      */
 1158                          5558                 :            757 :     BufferGetTag(buffer, &key.rlocator, &forkno, &blockno);
                               5559                 :                : 
                               5560                 :                :     /* tuples can only be in the main fork */
 4205                          5561         [ -  + ]:            757 :     Assert(forkno == MAIN_FORKNUM);
                               5562         [ -  + ]:            757 :     Assert(blockno == ItemPointerGetBlockNumber(&htup->t_self));
                               5563                 :                : 
                               5564                 :            757 :     ItemPointerCopy(&htup->t_self,
                               5565                 :                :                     &key.tid);
                               5566                 :                : 
                               5567                 :            768 : restart:
                               5568                 :                :     ent = (ReorderBufferTupleCidEnt *)
  943 peter@eisentraut.org     5569                 :            768 :         hash_search(tuplecid_data, &key, HASH_FIND, NULL);
                               5570                 :                : 
                               5571                 :                :     /*
                               5572                 :                :      * failed to find a mapping, check whether the table was rewritten and
                               5573                 :                :      * apply mapping if so, but only do that once - there can be no new
                               5574                 :                :      * mappings while we are in here since we have to hold a lock on the
                               5575                 :                :      * relation.
                               5576                 :                :      */
 4205 rhaas@postgresql.org     5577   [ +  +  +  + ]:            768 :     if (ent == NULL && !updated_mapping)
                               5578                 :                :     {
                               5579                 :             11 :         UpdateLogicalMappings(tuplecid_data, htup->t_tableOid, snapshot);
                               5580                 :                :         /* now check but don't update for a mapping again */
                               5581                 :             11 :         updated_mapping = true;
                               5582                 :             11 :         goto restart;
                               5583                 :                :     }
                               5584         [ +  + ]:            757 :     else if (ent == NULL)
                               5585                 :              5 :         return false;
                               5586                 :                : 
                               5587         [ +  - ]:            752 :     if (cmin)
                               5588                 :            752 :         *cmin = ent->cmin;
                               5589         [ +  - ]:            752 :     if (cmax)
                               5590                 :            752 :         *cmax = ent->cmax;
                               5591                 :            752 :     return true;
                               5592                 :                : }
                               5593                 :                : 
                               5594                 :                : /*
                               5595                 :                :  * Count invalidation messages of specified transaction.
                               5596                 :                :  *
                               5597                 :                :  * Returns number of messages, and msgs is set to the pointer of the linked
                               5598                 :                :  * list for the messages.
                               5599                 :                :  */
                               5600                 :                : uint32
  149 akapila@postgresql.o     5601                 :             34 : ReorderBufferGetInvalidations(ReorderBuffer *rb, TransactionId xid,
                               5602                 :                :                               SharedInvalidationMessage **msgs)
                               5603                 :                : {
                               5604                 :                :     ReorderBufferTXN *txn;
                               5605                 :                : 
                               5606                 :             34 :     txn = ReorderBufferTXNByXid(rb, xid, false, NULL, InvalidXLogRecPtr,
                               5607                 :                :                                 false);
                               5608                 :                : 
                               5609         [ -  + ]:             34 :     if (txn == NULL)
  149 akapila@postgresql.o     5610                 :UBC           0 :         return 0;
                               5611                 :                : 
  149 akapila@postgresql.o     5612                 :CBC          34 :     *msgs = txn->invalidations;
                               5613                 :                : 
                               5614                 :             34 :     return txn->ninvalidations;
                               5615                 :                : }
        

Generated by: LCOV version 2.4-beta