LCOV - code coverage report
Current view: top level - src/backend/replication/logical - logical.c (source / functions) Hit Total Coverage
Test: PostgreSQL 14devel Lines: 591 638 92.6 %
Date: 2020-10-28 11:24:57 Functions: 36 37 97.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  * logical.c
       3             :  *     PostgreSQL logical decoding coordination
       4             :  *
       5             :  * Copyright (c) 2012-2020, PostgreSQL Global Development Group
       6             :  *
       7             :  * IDENTIFICATION
       8             :  *    src/backend/replication/logical/logical.c
       9             :  *
      10             :  * NOTES
      11             :  *    This file coordinates interaction between the various modules that
      12             :  *    together provide logical decoding, primarily by providing so
      13             :  *    called LogicalDecodingContexts. The goal is to encapsulate most of the
      14             :  *    internal complexity for consumers of logical decoding, so they can
      15             :  *    create and consume a changestream with a low amount of code. Builtin
      16             :  *    consumers are the walsender and SQL SRF interface, but it's possible to
      17             :  *    add further ones without changing core code, e.g. to consume changes in
      18             :  *    a bgworker.
      19             :  *
      20             :  *    The idea is that a consumer provides three callbacks, one to read WAL,
      21             :  *    one to prepare a data write, and a final one for actually writing since
      22             :  *    their implementation depends on the type of consumer.  Check
      23             :  *    logicalfuncs.c for an example implementation of a fairly simple consumer
      24             :  *    and an implementation of a WAL reading callback that's suitable for
      25             :  *    simple consumers.
      26             :  *-------------------------------------------------------------------------
      27             :  */
      28             : 
      29             : #include "postgres.h"
      30             : 
      31             : #include "access/xact.h"
      32             : #include "access/xlog_internal.h"
      33             : #include "fmgr.h"
      34             : #include "miscadmin.h"
      35             : #include "pgstat.h"
      36             : #include "replication/decode.h"
      37             : #include "replication/logical.h"
      38             : #include "replication/origin.h"
      39             : #include "replication/reorderbuffer.h"
      40             : #include "replication/snapbuild.h"
      41             : #include "storage/proc.h"
      42             : #include "storage/procarray.h"
      43             : #include "utils/builtins.h"
      44             : #include "utils/memutils.h"
      45             : 
      46             : /* data for errcontext callback */
      47             : typedef struct LogicalErrorCallbackState
      48             : {
      49             :     LogicalDecodingContext *ctx;
      50             :     const char *callback_name;
      51             :     XLogRecPtr  report_location;
      52             : } LogicalErrorCallbackState;
      53             : 
      54             : /* wrappers around output plugin callbacks */
      55             : static void output_plugin_error_callback(void *arg);
      56             : static void startup_cb_wrapper(LogicalDecodingContext *ctx, OutputPluginOptions *opt,
      57             :                                bool is_init);
      58             : static void shutdown_cb_wrapper(LogicalDecodingContext *ctx);
      59             : static void begin_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn);
      60             : static void commit_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
      61             :                               XLogRecPtr commit_lsn);
      62             : static bool filter_prepare_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
      63             :                                       TransactionId xid, const char *gid);
      64             : static void prepare_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
      65             :                                XLogRecPtr prepare_lsn);
      66             : static void commit_prepared_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
      67             :                                        XLogRecPtr commit_lsn);
      68             : static void rollback_prepared_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
      69             :                                       XLogRecPtr abort_lsn);
      70             : static void change_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
      71             :                               Relation relation, ReorderBufferChange *change);
      72             : static void truncate_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
      73             :                                 int nrelations, Relation relations[], ReorderBufferChange *change);
      74             : static void message_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
      75             :                                XLogRecPtr message_lsn, bool transactional,
      76             :                                const char *prefix, Size message_size, const char *message);
      77             : 
      78             : /* streaming callbacks */
      79             : static void stream_start_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
      80             :                                     XLogRecPtr first_lsn);
      81             : static void stream_stop_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
      82             :                                    XLogRecPtr last_lsn);
      83             : static void stream_abort_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
      84             :                                     XLogRecPtr abort_lsn);
      85             : static void stream_prepare_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
      86             :                                       XLogRecPtr prepare_lsn);
      87             : static void stream_commit_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
      88             :                                      XLogRecPtr commit_lsn);
      89             : static void stream_change_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
      90             :                                      Relation relation, ReorderBufferChange *change);
      91             : static void stream_message_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
      92             :                                       XLogRecPtr message_lsn, bool transactional,
      93             :                                       const char *prefix, Size message_size, const char *message);
      94             : static void stream_truncate_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
      95             :                                        int nrelations, Relation relations[], ReorderBufferChange *change);
      96             : 
      97             : static void LoadOutputPlugin(OutputPluginCallbacks *callbacks, const char *plugin);
      98             : 
      99             : /*
     100             :  * Make sure the current settings & environment are capable of doing logical
     101             :  * decoding.
     102             :  */
     103             : void
     104         834 : CheckLogicalDecodingRequirements(void)
     105             : {
     106         834 :     CheckSlotRequirements();
     107             : 
     108             :     /*
     109             :      * NB: Adding a new requirement likely means that RestoreSlotFromDisk()
     110             :      * needs the same check.
     111             :      */
     112             : 
     113         834 :     if (wal_level < WAL_LEVEL_LOGICAL)
     114           0 :         ereport(ERROR,
     115             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     116             :                  errmsg("logical decoding requires wal_level >= logical")));
     117             : 
     118         834 :     if (MyDatabaseId == InvalidOid)
     119           0 :         ereport(ERROR,
     120             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     121             :                  errmsg("logical decoding requires a database connection")));
     122             : 
     123             :     /* ----
     124             :      * TODO: We got to change that someday soon...
     125             :      *
     126             :      * There's basically three things missing to allow this:
     127             :      * 1) We need to be able to correctly and quickly identify the timeline a
     128             :      *    LSN belongs to
     129             :      * 2) We need to force hot_standby_feedback to be enabled at all times so
     130             :      *    the primary cannot remove rows we need.
     131             :      * 3) support dropping replication slots referring to a database, in
     132             :      *    dbase_redo. There can't be any active ones due to HS recovery
     133             :      *    conflicts, so that should be relatively easy.
     134             :      * ----
     135             :      */
     136         834 :     if (RecoveryInProgress())
     137           0 :         ereport(ERROR,
     138             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     139             :                  errmsg("logical decoding cannot be used while in recovery")));
     140         834 : }
     141             : 
     142             : /*
     143             :  * Helper function for CreateInitDecodingContext() and
     144             :  * CreateDecodingContext() performing common tasks.
     145             :  */
     146             : static LogicalDecodingContext *
     147         822 : StartupDecodingContext(List *output_plugin_options,
     148             :                        XLogRecPtr start_lsn,
     149             :                        TransactionId xmin_horizon,
     150             :                        bool need_full_snapshot,
     151             :                        bool fast_forward,
     152             :                        XLogReaderRoutine *xl_routine,
     153             :                        LogicalOutputPluginWriterPrepareWrite prepare_write,
     154             :                        LogicalOutputPluginWriterWrite do_write,
     155             :                        LogicalOutputPluginWriterUpdateProgress update_progress)
     156             : {
     157             :     ReplicationSlot *slot;
     158             :     MemoryContext context,
     159             :                 old_context;
     160             :     LogicalDecodingContext *ctx;
     161             : 
     162             :     /* shorter lines... */
     163         822 :     slot = MyReplicationSlot;
     164             : 
     165         822 :     context = AllocSetContextCreate(CurrentMemoryContext,
     166             :                                     "Logical decoding context",
     167             :                                     ALLOCSET_DEFAULT_SIZES);
     168         822 :     old_context = MemoryContextSwitchTo(context);
     169         822 :     ctx = palloc0(sizeof(LogicalDecodingContext));
     170             : 
     171         822 :     ctx->context = context;
     172             : 
     173             :     /*
     174             :      * (re-)load output plugins, so we detect a bad (removed) output plugin
     175             :      * now.
     176             :      */
     177         822 :     if (!fast_forward)
     178         818 :         LoadOutputPlugin(&ctx->callbacks, NameStr(slot->data.plugin));
     179             : 
     180             :     /*
     181             :      * Now that the slot's xmin has been set, we can announce ourselves as a
     182             :      * logical decoding backend which doesn't need to be checked individually
     183             :      * when computing the xmin horizon because the xmin is enforced via
     184             :      * replication slots.
     185             :      *
     186             :      * We can only do so if we're outside of a transaction (i.e. the case when
     187             :      * streaming changes via walsender), otherwise an already setup
     188             :      * snapshot/xid would end up being ignored. That's not a particularly
     189             :      * bothersome restriction since the SQL interface can't be used for
     190             :      * streaming anyway.
     191             :      */
     192         820 :     if (!IsTransactionOrTransactionBlock())
     193             :     {
     194         252 :         LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
     195         252 :         MyProc->vacuumFlags |= PROC_IN_LOGICAL_DECODING;
     196         252 :         ProcGlobal->vacuumFlags[MyProc->pgxactoff] = MyProc->vacuumFlags;
     197         252 :         LWLockRelease(ProcArrayLock);
     198             :     }
     199             : 
     200         820 :     ctx->slot = slot;
     201             : 
     202         820 :     ctx->reader = XLogReaderAllocate(wal_segment_size, NULL, xl_routine, ctx);
     203         820 :     if (!ctx->reader)
     204           0 :         ereport(ERROR,
     205             :                 (errcode(ERRCODE_OUT_OF_MEMORY),
     206             :                  errmsg("out of memory")));
     207             : 
     208         820 :     ctx->reorder = ReorderBufferAllocate();
     209         820 :     ctx->snapshot_builder =
     210         820 :         AllocateSnapshotBuilder(ctx->reorder, xmin_horizon, start_lsn,
     211             :                                 need_full_snapshot);
     212             : 
     213         820 :     ctx->reorder->private_data = ctx;
     214             : 
     215             :     /* wrap output plugin callbacks, so we can add error context information */
     216         820 :     ctx->reorder->begin = begin_cb_wrapper;
     217         820 :     ctx->reorder->apply_change = change_cb_wrapper;
     218         820 :     ctx->reorder->apply_truncate = truncate_cb_wrapper;
     219         820 :     ctx->reorder->commit = commit_cb_wrapper;
     220         820 :     ctx->reorder->filter_prepare = filter_prepare_cb_wrapper;
     221         820 :     ctx->reorder->prepare = prepare_cb_wrapper;
     222         820 :     ctx->reorder->commit_prepared = commit_prepared_cb_wrapper;
     223         820 :     ctx->reorder->rollback_prepared = rollback_prepared_cb_wrapper;
     224         820 :     ctx->reorder->message = message_cb_wrapper;
     225             : 
     226             :     /*
     227             :      * To support streaming, we require start/stop/abort/commit/change
     228             :      * callbacks. The message and truncate callbacks are optional, similar to
     229             :      * regular output plugins. We however enable streaming when at least one
     230             :      * of the methods is enabled so that we can easily identify missing
     231             :      * methods.
     232             :      *
     233             :      * We decide it here, but only check it later in the wrappers.
     234             :      */
     235        1644 :     ctx->streaming = (ctx->callbacks.stream_start_cb != NULL) ||
     236           8 :         (ctx->callbacks.stream_stop_cb != NULL) ||
     237           8 :         (ctx->callbacks.stream_abort_cb != NULL) ||
     238           8 :         (ctx->callbacks.stream_prepare_cb != NULL) ||
     239           8 :         (ctx->callbacks.stream_commit_cb != NULL) ||
     240           8 :         (ctx->callbacks.stream_change_cb != NULL) ||
     241         828 :         (ctx->callbacks.stream_message_cb != NULL) ||
     242           4 :         (ctx->callbacks.stream_truncate_cb != NULL);
     243             : 
     244             :     /*
     245             :      * To support two-phase logical decoding, we require prepare/commit-prepare/abort-prepare
     246             :      * callbacks. The filter-prepare callback is optional. We however enable two-phase logical
     247             :      * decoding when at least one of the methods is enabled so that we can easily identify
     248             :      * missing methods.
     249             :      *
     250             :      * We decide it here, but only check it later in the wrappers.
     251             :      */
     252        1644 :     ctx->twophase = (ctx->callbacks.prepare_cb != NULL) ||
     253           8 :         (ctx->callbacks.commit_prepared_cb != NULL) ||
     254         828 :         (ctx->callbacks.rollback_prepared_cb != NULL) ||
     255           4 :         (ctx->callbacks.filter_prepare_cb != NULL);
     256             : 
     257             :     /*
     258             :      * streaming callbacks
     259             :      *
     260             :      * stream_message and stream_truncate callbacks are optional, so we do not
     261             :      * fail with ERROR when missing, but the wrappers simply do nothing. We
     262             :      * must set the ReorderBuffer callbacks to something, otherwise the calls
     263             :      * from there will crash (we don't want to move the checks there).
     264             :      */
     265         820 :     ctx->reorder->stream_start = stream_start_cb_wrapper;
     266         820 :     ctx->reorder->stream_stop = stream_stop_cb_wrapper;
     267         820 :     ctx->reorder->stream_abort = stream_abort_cb_wrapper;
     268         820 :     ctx->reorder->stream_prepare = stream_prepare_cb_wrapper;
     269         820 :     ctx->reorder->stream_commit = stream_commit_cb_wrapper;
     270         820 :     ctx->reorder->stream_change = stream_change_cb_wrapper;
     271         820 :     ctx->reorder->stream_message = stream_message_cb_wrapper;
     272         820 :     ctx->reorder->stream_truncate = stream_truncate_cb_wrapper;
     273             : 
     274         820 :     ctx->out = makeStringInfo();
     275         820 :     ctx->prepare_write = prepare_write;
     276         820 :     ctx->write = do_write;
     277         820 :     ctx->update_progress = update_progress;
     278             : 
     279         820 :     ctx->output_plugin_options = output_plugin_options;
     280             : 
     281         820 :     ctx->fast_forward = fast_forward;
     282             : 
     283         820 :     MemoryContextSwitchTo(old_context);
     284             : 
     285         820 :     return ctx;
     286             : }
     287             : 
     288             : /*
     289             :  * Create a new decoding context, for a new logical slot.
     290             :  *
     291             :  * plugin -- contains the name of the output plugin
     292             :  * output_plugin_options -- contains options passed to the output plugin
     293             :  * need_full_snapshot -- if true, must obtain a snapshot able to read all
     294             :  *      tables; if false, one that can read only catalogs is acceptable.
     295             :  * restart_lsn -- if given as invalid, it's this routine's responsibility to
     296             :  *      mark WAL as reserved by setting a convenient restart_lsn for the slot.
     297             :  *      Otherwise, we set for decoding to start from the given LSN without
     298             :  *      marking WAL reserved beforehand.  In that scenario, it's up to the
     299             :  *      caller to guarantee that WAL remains available.
     300             :  * xl_routine -- XLogReaderRoutine for underlying XLogReader
     301             :  * prepare_write, do_write, update_progress --
     302             :  *      callbacks that perform the use-case dependent, actual, work.
     303             :  *
     304             :  * Needs to be called while in a memory context that's at least as long lived
     305             :  * as the decoding context because further memory contexts will be created
     306             :  * inside it.
     307             :  *
     308             :  * Returns an initialized decoding context after calling the output plugin's
     309             :  * startup function.
     310             :  */
     311             : LogicalDecodingContext *
     312         334 : CreateInitDecodingContext(const char *plugin,
     313             :                           List *output_plugin_options,
     314             :                           bool need_full_snapshot,
     315             :                           XLogRecPtr restart_lsn,
     316             :                           XLogReaderRoutine *xl_routine,
     317             :                           LogicalOutputPluginWriterPrepareWrite prepare_write,
     318             :                           LogicalOutputPluginWriterWrite do_write,
     319             :                           LogicalOutputPluginWriterUpdateProgress update_progress)
     320             : {
     321         334 :     TransactionId xmin_horizon = InvalidTransactionId;
     322             :     ReplicationSlot *slot;
     323             :     NameData    plugin_name;
     324             :     LogicalDecodingContext *ctx;
     325             :     MemoryContext old_context;
     326             : 
     327             :     /* shorter lines... */
     328         334 :     slot = MyReplicationSlot;
     329             : 
     330             :     /* first some sanity checks that are unlikely to be violated */
     331         334 :     if (slot == NULL)
     332           0 :         elog(ERROR, "cannot perform logical decoding without an acquired slot");
     333             : 
     334         334 :     if (plugin == NULL)
     335           0 :         elog(ERROR, "cannot initialize logical decoding without a specified plugin");
     336             : 
     337             :     /* Make sure the passed slot is suitable. These are user facing errors. */
     338         334 :     if (SlotIsPhysical(slot))
     339           0 :         ereport(ERROR,
     340             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     341             :                  errmsg("cannot use physical replication slot for logical decoding")));
     342             : 
     343         334 :     if (slot->data.database != MyDatabaseId)
     344           0 :         ereport(ERROR,
     345             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     346             :                  errmsg("replication slot \"%s\" was not created in this database",
     347             :                         NameStr(slot->data.name))));
     348             : 
     349         612 :     if (IsTransactionState() &&
     350         278 :         GetTopTransactionIdIfAny() != InvalidTransactionId)
     351           4 :         ereport(ERROR,
     352             :                 (errcode(ERRCODE_ACTIVE_SQL_TRANSACTION),
     353             :                  errmsg("cannot create logical replication slot in transaction that has performed writes")));
     354             : 
     355             :     /*
     356             :      * Register output plugin name with slot.  We need the mutex to avoid
     357             :      * concurrent reading of a partially copied string.  But we don't want any
     358             :      * complicated code while holding a spinlock, so do namestrcpy() outside.
     359             :      */
     360         330 :     namestrcpy(&plugin_name, plugin);
     361         330 :     SpinLockAcquire(&slot->mutex);
     362         330 :     slot->data.plugin = plugin_name;
     363         330 :     SpinLockRelease(&slot->mutex);
     364             : 
     365         330 :     if (XLogRecPtrIsInvalid(restart_lsn))
     366         318 :         ReplicationSlotReserveWal();
     367             :     else
     368             :     {
     369          12 :         SpinLockAcquire(&slot->mutex);
     370          12 :         slot->data.restart_lsn = restart_lsn;
     371          12 :         SpinLockRelease(&slot->mutex);
     372             :     }
     373             : 
     374             :     /* ----
     375             :      * This is a bit tricky: We need to determine a safe xmin horizon to start
     376             :      * decoding from, to avoid starting from a running xacts record referring
     377             :      * to xids whose rows have been vacuumed or pruned
     378             :      * already. GetOldestSafeDecodingTransactionId() returns such a value, but
     379             :      * without further interlock its return value might immediately be out of
     380             :      * date.
     381             :      *
     382             :      * So we have to acquire the ProcArrayLock to prevent computation of new
     383             :      * xmin horizons by other backends, get the safe decoding xid, and inform
     384             :      * the slot machinery about the new limit. Once that's done the
     385             :      * ProcArrayLock can be released as the slot machinery now is
     386             :      * protecting against vacuum.
     387             :      *
     388             :      * Note that, temporarily, the data, not just the catalog, xmin has to be
     389             :      * reserved if a data snapshot is to be exported.  Otherwise the initial
     390             :      * data snapshot created here is not guaranteed to be valid. After that
     391             :      * the data xmin doesn't need to be managed anymore and the global xmin
     392             :      * should be recomputed. As we are fine with losing the pegged data xmin
     393             :      * after crash - no chance a snapshot would get exported anymore - we can
     394             :      * get away with just setting the slot's
     395             :      * effective_xmin. ReplicationSlotRelease will reset it again.
     396             :      *
     397             :      * ----
     398             :      */
     399         330 :     LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
     400             : 
     401         330 :     xmin_horizon = GetOldestSafeDecodingTransactionId(!need_full_snapshot);
     402             : 
     403         330 :     SpinLockAcquire(&slot->mutex);
     404         330 :     slot->effective_catalog_xmin = xmin_horizon;
     405         330 :     slot->data.catalog_xmin = xmin_horizon;
     406         330 :     if (need_full_snapshot)
     407         126 :         slot->effective_xmin = xmin_horizon;
     408         330 :     SpinLockRelease(&slot->mutex);
     409             : 
     410         330 :     ReplicationSlotsComputeRequiredXmin(true);
     411             : 
     412         330 :     LWLockRelease(ProcArrayLock);
     413             : 
     414         330 :     ReplicationSlotMarkDirty();
     415         330 :     ReplicationSlotSave();
     416             : 
     417         330 :     ctx = StartupDecodingContext(NIL, restart_lsn, xmin_horizon,
     418             :                                  need_full_snapshot, false,
     419             :                                  xl_routine, prepare_write, do_write,
     420             :                                  update_progress);
     421             : 
     422             :     /* call output plugin initialization callback */
     423         328 :     old_context = MemoryContextSwitchTo(ctx->context);
     424         328 :     if (ctx->callbacks.startup_cb != NULL)
     425         328 :         startup_cb_wrapper(ctx, &ctx->options, true);
     426         328 :     MemoryContextSwitchTo(old_context);
     427             : 
     428         328 :     ctx->reorder->output_rewrites = ctx->options.receive_rewrites;
     429             : 
     430         328 :     return ctx;
     431             : }
     432             : 
     433             : /*
     434             :  * Create a new decoding context, for a logical slot that has previously been
     435             :  * used already.
     436             :  *
     437             :  * start_lsn
     438             :  *      The LSN at which to start decoding.  If InvalidXLogRecPtr, restart
     439             :  *      from the slot's confirmed_flush; otherwise, start from the specified
     440             :  *      location (but move it forwards to confirmed_flush if it's older than
     441             :  *      that, see below).
     442             :  *
     443             :  * output_plugin_options
     444             :  *      options passed to the output plugin.
     445             :  *
     446             :  * fast_forward
     447             :  *      bypass the generation of logical changes.
     448             :  *
     449             :  * xl_routine
     450             :  *      XLogReaderRoutine used by underlying xlogreader
     451             :  *
     452             :  * prepare_write, do_write, update_progress
     453             :  *      callbacks that have to be filled to perform the use-case dependent,
     454             :  *      actual work.
     455             :  *
     456             :  * Needs to be called while in a memory context that's at least as long lived
     457             :  * as the decoding context because further memory contexts will be created
     458             :  * inside it.
     459             :  *
     460             :  * Returns an initialized decoding context after calling the output plugin's
     461             :  * startup function.
     462             :  */
     463             : LogicalDecodingContext *
     464         494 : CreateDecodingContext(XLogRecPtr start_lsn,
     465             :                       List *output_plugin_options,
     466             :                       bool fast_forward,
     467             :                       XLogReaderRoutine *xl_routine,
     468             :                       LogicalOutputPluginWriterPrepareWrite prepare_write,
     469             :                       LogicalOutputPluginWriterWrite do_write,
     470             :                       LogicalOutputPluginWriterUpdateProgress update_progress)
     471             : {
     472             :     LogicalDecodingContext *ctx;
     473             :     ReplicationSlot *slot;
     474             :     MemoryContext old_context;
     475             : 
     476             :     /* shorter lines... */
     477         494 :     slot = MyReplicationSlot;
     478             : 
     479             :     /* first some sanity checks that are unlikely to be violated */
     480         494 :     if (slot == NULL)
     481           0 :         elog(ERROR, "cannot perform logical decoding without an acquired slot");
     482             : 
     483             :     /* make sure the passed slot is suitable, these are user facing errors */
     484         494 :     if (SlotIsPhysical(slot))
     485           2 :         ereport(ERROR,
     486             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     487             :                  errmsg("cannot use physical replication slot for logical decoding")));
     488             : 
     489         492 :     if (slot->data.database != MyDatabaseId)
     490           0 :         ereport(ERROR,
     491             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     492             :                  errmsg("replication slot \"%s\" was not created in this database",
     493             :                         NameStr(slot->data.name))));
     494             : 
     495         492 :     if (start_lsn == InvalidXLogRecPtr)
     496             :     {
     497             :         /* continue from last position */
     498         344 :         start_lsn = slot->data.confirmed_flush;
     499             :     }
     500         148 :     else if (start_lsn < slot->data.confirmed_flush)
     501             :     {
     502             :         /*
     503             :          * It might seem like we should error out in this case, but it's
     504             :          * pretty common for a client to acknowledge a LSN it doesn't have to
     505             :          * do anything for, and thus didn't store persistently, because the
     506             :          * xlog records didn't result in anything relevant for logical
     507             :          * decoding. Clients have to be able to do that to support synchronous
     508             :          * replication.
     509             :          */
     510           0 :         elog(DEBUG1, "cannot stream from %X/%X, minimum is %X/%X, forwarding",
     511             :              (uint32) (start_lsn >> 32), (uint32) start_lsn,
     512             :              (uint32) (slot->data.confirmed_flush >> 32),
     513             :              (uint32) slot->data.confirmed_flush);
     514             : 
     515           0 :         start_lsn = slot->data.confirmed_flush;
     516             :     }
     517             : 
     518         492 :     ctx = StartupDecodingContext(output_plugin_options,
     519             :                                  start_lsn, InvalidTransactionId, false,
     520             :                                  fast_forward, xl_routine, prepare_write,
     521             :                                  do_write, update_progress);
     522             : 
     523             :     /* call output plugin initialization callback */
     524         492 :     old_context = MemoryContextSwitchTo(ctx->context);
     525         492 :     if (ctx->callbacks.startup_cb != NULL)
     526         488 :         startup_cb_wrapper(ctx, &ctx->options, false);
     527         486 :     MemoryContextSwitchTo(old_context);
     528             : 
     529         486 :     ctx->reorder->output_rewrites = ctx->options.receive_rewrites;
     530             : 
     531         486 :     ereport(LOG,
     532             :             (errmsg("starting logical decoding for slot \"%s\"",
     533             :                     NameStr(slot->data.name)),
     534             :              errdetail("Streaming transactions committing after %X/%X, reading WAL from %X/%X.",
     535             :                        (uint32) (slot->data.confirmed_flush >> 32),
     536             :                        (uint32) slot->data.confirmed_flush,
     537             :                        (uint32) (slot->data.restart_lsn >> 32),
     538             :                        (uint32) slot->data.restart_lsn)));
     539             : 
     540         486 :     return ctx;
     541             : }
     542             : 
     543             : /*
     544             :  * Returns true if a consistent initial decoding snapshot has been built.
     545             :  */
     546             : bool
     547       10452 : DecodingContextReady(LogicalDecodingContext *ctx)
     548             : {
     549       10452 :     return SnapBuildCurrentState(ctx->snapshot_builder) == SNAPBUILD_CONSISTENT;
     550             : }
     551             : 
     552             : /*
     553             :  * Read from the decoding slot, until it is ready to start extracting changes.
     554             :  */
     555             : void
     556         316 : DecodingContextFindStartpoint(LogicalDecodingContext *ctx)
     557             : {
     558         316 :     ReplicationSlot *slot = ctx->slot;
     559             : 
     560             :     /* Initialize from where to start reading WAL. */
     561         316 :     XLogBeginRead(ctx->reader, slot->data.restart_lsn);
     562             : 
     563         316 :     elog(DEBUG1, "searching for logical decoding starting point, starting at %X/%X",
     564             :          (uint32) (slot->data.restart_lsn >> 32),
     565             :          (uint32) slot->data.restart_lsn);
     566             : 
     567             :     /* Wait for a consistent starting point */
     568             :     for (;;)
     569             :     {
     570             :         XLogRecord *record;
     571       10452 :         char       *err = NULL;
     572             : 
     573             :         /* the read_page callback waits for new WAL */
     574       10452 :         record = XLogReadRecord(ctx->reader, &err);
     575       10452 :         if (err)
     576           0 :             elog(ERROR, "%s", err);
     577       10452 :         if (!record)
     578           0 :             elog(ERROR, "no record found"); /* shouldn't happen */
     579             : 
     580       10452 :         LogicalDecodingProcessRecord(ctx, ctx->reader);
     581             : 
     582             :         /* only continue till we found a consistent spot */
     583       10452 :         if (DecodingContextReady(ctx))
     584         316 :             break;
     585             : 
     586       10136 :         CHECK_FOR_INTERRUPTS();
     587       10136 :     }
     588             : 
     589         316 :     SpinLockAcquire(&slot->mutex);
     590         316 :     slot->data.confirmed_flush = ctx->reader->EndRecPtr;
     591         316 :     SpinLockRelease(&slot->mutex);
     592         316 : }
     593             : 
     594             : /*
     595             :  * Free a previously allocated decoding context, invoking the shutdown
     596             :  * callback if necessary.
     597             :  */
     598             : void
     599         738 : FreeDecodingContext(LogicalDecodingContext *ctx)
     600             : {
     601         738 :     if (ctx->callbacks.shutdown_cb != NULL)
     602         734 :         shutdown_cb_wrapper(ctx);
     603             : 
     604         738 :     ReorderBufferFree(ctx->reorder);
     605         738 :     FreeSnapshotBuilder(ctx->snapshot_builder);
     606         738 :     XLogReaderFree(ctx->reader);
     607         738 :     MemoryContextDelete(ctx->context);
     608         738 : }
     609             : 
     610             : /*
     611             :  * Prepare a write using the context's output routine.
     612             :  */
     613             : void
     614      634048 : OutputPluginPrepareWrite(struct LogicalDecodingContext *ctx, bool last_write)
     615             : {
     616      634048 :     if (!ctx->accept_writes)
     617           0 :         elog(ERROR, "writes are only accepted in commit, begin and change callbacks");
     618             : 
     619      634048 :     ctx->prepare_write(ctx, ctx->write_location, ctx->write_xid, last_write);
     620      634048 :     ctx->prepared_write = true;
     621      634048 : }
     622             : 
     623             : /*
     624             :  * Perform a write using the context's output routine.
     625             :  */
     626             : void
     627      634046 : OutputPluginWrite(struct LogicalDecodingContext *ctx, bool last_write)
     628             : {
     629      634046 :     if (!ctx->prepared_write)
     630           0 :         elog(ERROR, "OutputPluginPrepareWrite needs to be called before OutputPluginWrite");
     631             : 
     632      634046 :     ctx->write(ctx, ctx->write_location, ctx->write_xid, last_write);
     633      634040 :     ctx->prepared_write = false;
     634      634040 : }
     635             : 
     636             : /*
     637             :  * Update progress tracking (if supported).
     638             :  */
     639             : void
     640         434 : OutputPluginUpdateProgress(struct LogicalDecodingContext *ctx)
     641             : {
     642         434 :     if (!ctx->update_progress)
     643         434 :         return;
     644             : 
     645         434 :     ctx->update_progress(ctx, ctx->write_location, ctx->write_xid);
     646             : }
     647             : 
     648             : /*
     649             :  * Load the output plugin, lookup its output plugin init function, and check
     650             :  * that it provides the required callbacks.
     651             :  */
     652             : static void
     653         818 : LoadOutputPlugin(OutputPluginCallbacks *callbacks, const char *plugin)
     654             : {
     655             :     LogicalOutputPluginInit plugin_init;
     656             : 
     657         816 :     plugin_init = (LogicalOutputPluginInit)
     658         818 :         load_external_function(plugin, "_PG_output_plugin_init", false, NULL);
     659             : 
     660         816 :     if (plugin_init == NULL)
     661           0 :         elog(ERROR, "output plugins have to declare the _PG_output_plugin_init symbol");
     662             : 
     663             :     /* ask the output plugin to fill the callback struct */
     664         816 :     plugin_init(callbacks);
     665             : 
     666         816 :     if (callbacks->begin_cb == NULL)
     667           0 :         elog(ERROR, "output plugins have to register a begin callback");
     668         816 :     if (callbacks->change_cb == NULL)
     669           0 :         elog(ERROR, "output plugins have to register a change callback");
     670         816 :     if (callbacks->commit_cb == NULL)
     671           0 :         elog(ERROR, "output plugins have to register a commit callback");
     672         816 : }
     673             : 
     674             : static void
     675          18 : output_plugin_error_callback(void *arg)
     676             : {
     677          18 :     LogicalErrorCallbackState *state = (LogicalErrorCallbackState *) arg;
     678             : 
     679             :     /* not all callbacks have an associated LSN  */
     680          18 :     if (state->report_location != InvalidXLogRecPtr)
     681          48 :         errcontext("slot \"%s\", output plugin \"%s\", in the %s callback, associated LSN %X/%X",
     682          12 :                    NameStr(state->ctx->slot->data.name),
     683          12 :                    NameStr(state->ctx->slot->data.plugin),
     684             :                    state->callback_name,
     685          12 :                    (uint32) (state->report_location >> 32),
     686          12 :                    (uint32) state->report_location);
     687             :     else
     688          12 :         errcontext("slot \"%s\", output plugin \"%s\", in the %s callback",
     689           6 :                    NameStr(state->ctx->slot->data.name),
     690           6 :                    NameStr(state->ctx->slot->data.plugin),
     691             :                    state->callback_name);
     692          18 : }
     693             : 
     694             : static void
     695         816 : startup_cb_wrapper(LogicalDecodingContext *ctx, OutputPluginOptions *opt, bool is_init)
     696             : {
     697             :     LogicalErrorCallbackState state;
     698             :     ErrorContextCallback errcallback;
     699             : 
     700         816 :     Assert(!ctx->fast_forward);
     701             : 
     702             :     /* Push callback + info on the error context stack */
     703         816 :     state.ctx = ctx;
     704         816 :     state.callback_name = "startup";
     705         816 :     state.report_location = InvalidXLogRecPtr;
     706         816 :     errcallback.callback = output_plugin_error_callback;
     707         816 :     errcallback.arg = (void *) &state;
     708         816 :     errcallback.previous = error_context_stack;
     709         816 :     error_context_stack = &errcallback;
     710             : 
     711             :     /* set output state */
     712         816 :     ctx->accept_writes = false;
     713             : 
     714             :     /* do the actual work: call callback */
     715         816 :     ctx->callbacks.startup_cb(ctx, opt, is_init);
     716             : 
     717             :     /* Pop the error context stack */
     718         810 :     error_context_stack = errcallback.previous;
     719         810 : }
     720             : 
     721             : static void
     722         734 : shutdown_cb_wrapper(LogicalDecodingContext *ctx)
     723             : {
     724             :     LogicalErrorCallbackState state;
     725             :     ErrorContextCallback errcallback;
     726             : 
     727         734 :     Assert(!ctx->fast_forward);
     728             : 
     729             :     /* Push callback + info on the error context stack */
     730         734 :     state.ctx = ctx;
     731         734 :     state.callback_name = "shutdown";
     732         734 :     state.report_location = InvalidXLogRecPtr;
     733         734 :     errcallback.callback = output_plugin_error_callback;
     734         734 :     errcallback.arg = (void *) &state;
     735         734 :     errcallback.previous = error_context_stack;
     736         734 :     error_context_stack = &errcallback;
     737             : 
     738             :     /* set output state */
     739         734 :     ctx->accept_writes = false;
     740             : 
     741             :     /* do the actual work: call callback */
     742         734 :     ctx->callbacks.shutdown_cb(ctx);
     743             : 
     744             :     /* Pop the error context stack */
     745         734 :     error_context_stack = errcallback.previous;
     746         734 : }
     747             : 
     748             : 
     749             : /*
     750             :  * Callbacks for ReorderBuffer which add in some more information and then call
     751             :  * output_plugin.h plugins.
     752             :  */
     753             : static void
     754        1078 : begin_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn)
     755             : {
     756        1078 :     LogicalDecodingContext *ctx = cache->private_data;
     757             :     LogicalErrorCallbackState state;
     758             :     ErrorContextCallback errcallback;
     759             : 
     760        1078 :     Assert(!ctx->fast_forward);
     761             : 
     762             :     /* Push callback + info on the error context stack */
     763        1078 :     state.ctx = ctx;
     764        1078 :     state.callback_name = "begin";
     765        1078 :     state.report_location = txn->first_lsn;
     766        1078 :     errcallback.callback = output_plugin_error_callback;
     767        1078 :     errcallback.arg = (void *) &state;
     768        1078 :     errcallback.previous = error_context_stack;
     769        1078 :     error_context_stack = &errcallback;
     770             : 
     771             :     /* set output state */
     772        1078 :     ctx->accept_writes = true;
     773        1078 :     ctx->write_xid = txn->xid;
     774        1078 :     ctx->write_location = txn->first_lsn;
     775             : 
     776             :     /* do the actual work: call callback */
     777        1078 :     ctx->callbacks.begin_cb(ctx, txn);
     778             : 
     779             :     /* Pop the error context stack */
     780        1078 :     error_context_stack = errcallback.previous;
     781        1078 : }
     782             : 
     783             : static void
     784        1048 : commit_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
     785             :                   XLogRecPtr commit_lsn)
     786             : {
     787        1048 :     LogicalDecodingContext *ctx = cache->private_data;
     788             :     LogicalErrorCallbackState state;
     789             :     ErrorContextCallback errcallback;
     790             : 
     791        1048 :     Assert(!ctx->fast_forward);
     792             : 
     793             :     /* Push callback + info on the error context stack */
     794        1048 :     state.ctx = ctx;
     795        1048 :     state.callback_name = "commit";
     796        1048 :     state.report_location = txn->final_lsn; /* beginning of commit record */
     797        1048 :     errcallback.callback = output_plugin_error_callback;
     798        1048 :     errcallback.arg = (void *) &state;
     799        1048 :     errcallback.previous = error_context_stack;
     800        1048 :     error_context_stack = &errcallback;
     801             : 
     802             :     /* set output state */
     803        1048 :     ctx->accept_writes = true;
     804        1048 :     ctx->write_xid = txn->xid;
     805        1048 :     ctx->write_location = txn->end_lsn; /* points to the end of the record */
     806             : 
     807             :     /* do the actual work: call callback */
     808        1048 :     ctx->callbacks.commit_cb(ctx, txn, commit_lsn);
     809             : 
     810             :     /* Pop the error context stack */
     811        1048 :     error_context_stack = errcallback.previous;
     812        1048 : }
     813             : 
     814             : static void
     815          26 : prepare_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
     816             :                    XLogRecPtr prepare_lsn)
     817             : {
     818          26 :     LogicalDecodingContext *ctx = cache->private_data;
     819             :     LogicalErrorCallbackState state;
     820             :     ErrorContextCallback errcallback;
     821             : 
     822             :     /* We're only supposed to call this when two-phase commits are supported */
     823          26 :     Assert(ctx->twophase);
     824             : 
     825             :     /* Push callback + info on the error context stack */
     826          26 :     state.ctx = ctx;
     827          26 :     state.callback_name = "prepare";
     828          26 :     state.report_location = txn->final_lsn; /* beginning of commit record */
     829          26 :     errcallback.callback = output_plugin_error_callback;
     830          26 :     errcallback.arg = (void *) &state;
     831          26 :     errcallback.previous = error_context_stack;
     832          26 :     error_context_stack = &errcallback;
     833             : 
     834             :     /* set output state */
     835          26 :     ctx->accept_writes = true;
     836          26 :     ctx->write_xid = txn->xid;
     837          26 :     ctx->write_location = txn->end_lsn; /* points to the end of the record */
     838             : 
     839             :     /* If the plugin supports two-phase commits then prepare callback is mandatory */
     840          26 :     if (ctx->callbacks.prepare_cb == NULL)
     841           0 :         ereport(ERROR,
     842             :             (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     843             :                 errmsg("Output plugin did not register prepare_cb callback")));
     844             : 
     845             :     /* do the actual work: call callback */
     846          26 :     ctx->callbacks.prepare_cb(ctx, txn, prepare_lsn);
     847             : 
     848             :     /* Pop the error context stack */
     849          26 :     error_context_stack = errcallback.previous;
     850          26 : }
     851             : 
     852             : static void
     853          30 : commit_prepared_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
     854             :                            XLogRecPtr commit_lsn)
     855             : {
     856          30 :     LogicalDecodingContext *ctx = cache->private_data;
     857             :     LogicalErrorCallbackState state;
     858             :     ErrorContextCallback errcallback;
     859             : 
     860             :     /* We're only supposed to call this when two-phase commits are supported */
     861          30 :     Assert(ctx->twophase);
     862             : 
     863             :     /* Push callback + info on the error context stack */
     864          30 :     state.ctx = ctx;
     865          30 :     state.callback_name = "commit_prepared";
     866          30 :     state.report_location = txn->final_lsn; /* beginning of commit record */
     867          30 :     errcallback.callback = output_plugin_error_callback;
     868          30 :     errcallback.arg = (void *) &state;
     869          30 :     errcallback.previous = error_context_stack;
     870          30 :     error_context_stack = &errcallback;
     871             : 
     872             :     /* set output state */
     873          30 :     ctx->accept_writes = true;
     874          30 :     ctx->write_xid = txn->xid;
     875          30 :     ctx->write_location = txn->end_lsn; /* points to the end of the record */
     876             : 
     877             :     /* If the plugin support two-phase commits then commit prepared callback is mandatory */
     878          30 :     if (ctx->callbacks.commit_prepared_cb == NULL)
     879           0 :         ereport(ERROR,
     880             :             (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     881             :                 errmsg("Output plugin did not register commit_prepared_cb callback")));
     882             : 
     883             :     /* do the actual work: call callback */
     884          30 :     ctx->callbacks.commit_prepared_cb(ctx, txn, commit_lsn);
     885             : 
     886             :     /* Pop the error context stack */
     887          30 :     error_context_stack = errcallback.previous;
     888          30 : }
     889             : 
     890             : static void
     891          22 : rollback_prepared_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
     892             :                              XLogRecPtr abort_lsn)
     893             : {
     894          22 :     LogicalDecodingContext *ctx = cache->private_data;
     895             :     LogicalErrorCallbackState state;
     896             :     ErrorContextCallback errcallback;
     897             : 
     898             :     /* We're only supposed to call this when two-phase commits are supported */
     899          22 :     Assert(ctx->twophase);
     900             : 
     901             :     /* Push callback + info on the error context stack */
     902          22 :     state.ctx = ctx;
     903          22 :     state.callback_name = "rollback_prepared";
     904          22 :     state.report_location = txn->final_lsn; /* beginning of commit record */
     905          22 :     errcallback.callback = output_plugin_error_callback;
     906          22 :     errcallback.arg = (void *) &state;
     907          22 :     errcallback.previous = error_context_stack;
     908          22 :     error_context_stack = &errcallback;
     909             : 
     910             :     /* set output state */
     911          22 :     ctx->accept_writes = true;
     912          22 :     ctx->write_xid = txn->xid;
     913          22 :     ctx->write_location = txn->end_lsn; /* points to the end of the record */
     914             : 
     915             :     /* If the plugin support two-phase commits then abort prepared callback is mandatory */
     916          22 :     if (ctx->callbacks.rollback_prepared_cb == NULL)
     917           0 :         ereport(ERROR,
     918             :             (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     919             :                 errmsg("Output plugin did not register rollback_prepared_cb callback")));
     920             : 
     921             :     /* do the actual work: call callback */
     922          22 :     ctx->callbacks.rollback_prepared_cb(ctx, txn, abort_lsn);
     923             : 
     924             :     /* Pop the error context stack */
     925          22 :     error_context_stack = errcallback.previous;
     926          22 : }
     927             : 
     928             : static void
     929      314374 : change_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
     930             :                   Relation relation, ReorderBufferChange *change)
     931             : {
     932      314374 :     LogicalDecodingContext *ctx = cache->private_data;
     933             :     LogicalErrorCallbackState state;
     934             :     ErrorContextCallback errcallback;
     935             : 
     936      314374 :     Assert(!ctx->fast_forward);
     937             : 
     938             :     /* Push callback + info on the error context stack */
     939      314374 :     state.ctx = ctx;
     940      314374 :     state.callback_name = "change";
     941      314374 :     state.report_location = change->lsn;
     942      314374 :     errcallback.callback = output_plugin_error_callback;
     943      314374 :     errcallback.arg = (void *) &state;
     944      314374 :     errcallback.previous = error_context_stack;
     945      314374 :     error_context_stack = &errcallback;
     946             : 
     947             :     /* set output state */
     948      314374 :     ctx->accept_writes = true;
     949      314374 :     ctx->write_xid = txn->xid;
     950             : 
     951             :     /*
     952             :      * report this change's lsn so replies from clients can give an up2date
     953             :      * answer. This won't ever be enough (and shouldn't be!) to confirm
     954             :      * receipt of this transaction, but it might allow another transaction's
     955             :      * commit to be confirmed with one message.
     956             :      */
     957      314374 :     ctx->write_location = change->lsn;
     958             : 
     959      314374 :     ctx->callbacks.change_cb(ctx, txn, relation, change);
     960             : 
     961             :     /* Pop the error context stack */
     962      314372 :     error_context_stack = errcallback.previous;
     963      314372 : }
     964             : 
     965             : static void
     966          20 : truncate_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
     967             :                     int nrelations, Relation relations[], ReorderBufferChange *change)
     968             : {
     969          20 :     LogicalDecodingContext *ctx = cache->private_data;
     970             :     LogicalErrorCallbackState state;
     971             :     ErrorContextCallback errcallback;
     972             : 
     973          20 :     Assert(!ctx->fast_forward);
     974             : 
     975          20 :     if (!ctx->callbacks.truncate_cb)
     976          20 :         return;
     977             : 
     978             :     /* Push callback + info on the error context stack */
     979          20 :     state.ctx = ctx;
     980          20 :     state.callback_name = "truncate";
     981          20 :     state.report_location = change->lsn;
     982          20 :     errcallback.callback = output_plugin_error_callback;
     983          20 :     errcallback.arg = (void *) &state;
     984          20 :     errcallback.previous = error_context_stack;
     985          20 :     error_context_stack = &errcallback;
     986             : 
     987             :     /* set output state */
     988          20 :     ctx->accept_writes = true;
     989          20 :     ctx->write_xid = txn->xid;
     990             : 
     991             :     /*
     992             :      * report this change's lsn so replies from clients can give an up2date
     993             :      * answer. This won't ever be enough (and shouldn't be!) to confirm
     994             :      * receipt of this transaction, but it might allow another transaction's
     995             :      * commit to be confirmed with one message.
     996             :      */
     997          20 :     ctx->write_location = change->lsn;
     998             : 
     999          20 :     ctx->callbacks.truncate_cb(ctx, txn, nrelations, relations, change);
    1000             : 
    1001             :     /* Pop the error context stack */
    1002          20 :     error_context_stack = errcallback.previous;
    1003             : }
    1004             : 
    1005             : static bool
    1006         156 : filter_prepare_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
    1007             :                           TransactionId xid, const char *gid)
    1008             : {
    1009         156 :     LogicalDecodingContext *ctx = cache->private_data;
    1010             :     LogicalErrorCallbackState state;
    1011             :     ErrorContextCallback errcallback;
    1012             :     bool        ret;
    1013             : 
    1014             :     /*
    1015             :      * Skip if decoding of two-phase transactions at PREPARE time is not enabled. In that
    1016             :      * case all two-phase transactions are considered filtered out and will be
    1017             :      * applied as regular transactions at COMMIT PREPARED.
    1018             :      */
    1019         156 :     if (!ctx->twophase)
    1020           6 :         return true;
    1021             : 
    1022             :     /*
    1023             :      * The filter_prepare callback is optional. When not supplied, all
    1024             :      * prepared transactions should go through.
    1025             :      */
    1026         150 :     if (!ctx->callbacks.filter_prepare_cb)
    1027          16 :         return false;
    1028             : 
    1029             :     /* Push callback + info on the error context stack */
    1030         134 :     state.ctx = ctx;
    1031         134 :     state.callback_name = "filter_prepare";
    1032         134 :     state.report_location = InvalidXLogRecPtr;
    1033         134 :     errcallback.callback = output_plugin_error_callback;
    1034         134 :     errcallback.arg = (void *) &state;
    1035         134 :     errcallback.previous = error_context_stack;
    1036         134 :     error_context_stack = &errcallback;
    1037             : 
    1038             :     /* set output state */
    1039         134 :     ctx->accept_writes = false;
    1040             : 
    1041             :     /* do the actual work: call callback */
    1042         134 :     ret = ctx->callbacks.filter_prepare_cb(ctx, txn, xid, gid);
    1043             : 
    1044             :     /* Pop the error context stack */
    1045         134 :     error_context_stack = errcallback.previous;
    1046             : 
    1047         134 :     return ret;
    1048             : }
    1049             : 
    1050             : bool
    1051     2978528 : filter_by_origin_cb_wrapper(LogicalDecodingContext *ctx, RepOriginId origin_id)
    1052             : {
    1053             :     LogicalErrorCallbackState state;
    1054             :     ErrorContextCallback errcallback;
    1055             :     bool        ret;
    1056             : 
    1057     2978528 :     Assert(!ctx->fast_forward);
    1058             : 
    1059             :     /* Push callback + info on the error context stack */
    1060     2978528 :     state.ctx = ctx;
    1061     2978528 :     state.callback_name = "filter_by_origin";
    1062     2978528 :     state.report_location = InvalidXLogRecPtr;
    1063     2978528 :     errcallback.callback = output_plugin_error_callback;
    1064     2978528 :     errcallback.arg = (void *) &state;
    1065     2978528 :     errcallback.previous = error_context_stack;
    1066     2978528 :     error_context_stack = &errcallback;
    1067             : 
    1068             :     /* set output state */
    1069     2978528 :     ctx->accept_writes = false;
    1070             : 
    1071             :     /* do the actual work: call callback */
    1072     2978528 :     ret = ctx->callbacks.filter_by_origin_cb(ctx, origin_id);
    1073             : 
    1074             :     /* Pop the error context stack */
    1075     2978528 :     error_context_stack = errcallback.previous;
    1076             : 
    1077     2978528 :     return ret;
    1078             : }
    1079             : 
    1080             : static void
    1081          16 : message_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
    1082             :                    XLogRecPtr message_lsn, bool transactional,
    1083             :                    const char *prefix, Size message_size, const char *message)
    1084             : {
    1085          16 :     LogicalDecodingContext *ctx = cache->private_data;
    1086             :     LogicalErrorCallbackState state;
    1087             :     ErrorContextCallback errcallback;
    1088             : 
    1089          16 :     Assert(!ctx->fast_forward);
    1090             : 
    1091          16 :     if (ctx->callbacks.message_cb == NULL)
    1092          16 :         return;
    1093             : 
    1094             :     /* Push callback + info on the error context stack */
    1095          16 :     state.ctx = ctx;
    1096          16 :     state.callback_name = "message";
    1097          16 :     state.report_location = message_lsn;
    1098          16 :     errcallback.callback = output_plugin_error_callback;
    1099          16 :     errcallback.arg = (void *) &state;
    1100          16 :     errcallback.previous = error_context_stack;
    1101          16 :     error_context_stack = &errcallback;
    1102             : 
    1103             :     /* set output state */
    1104          16 :     ctx->accept_writes = true;
    1105          16 :     ctx->write_xid = txn != NULL ? txn->xid : InvalidTransactionId;
    1106          16 :     ctx->write_location = message_lsn;
    1107             : 
    1108             :     /* do the actual work: call callback */
    1109          16 :     ctx->callbacks.message_cb(ctx, txn, message_lsn, transactional, prefix,
    1110             :                               message_size, message);
    1111             : 
    1112             :     /* Pop the error context stack */
    1113          16 :     error_context_stack = errcallback.previous;
    1114             : }
    1115             : 
    1116             : static void
    1117         812 : stream_start_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
    1118             :                         XLogRecPtr first_lsn)
    1119             : {
    1120         812 :     LogicalDecodingContext *ctx = cache->private_data;
    1121             :     LogicalErrorCallbackState state;
    1122             :     ErrorContextCallback errcallback;
    1123             : 
    1124         812 :     Assert(!ctx->fast_forward);
    1125             : 
    1126             :     /* We're only supposed to call this when streaming is supported. */
    1127         812 :     Assert(ctx->streaming);
    1128             : 
    1129             :     /* Push callback + info on the error context stack */
    1130         812 :     state.ctx = ctx;
    1131         812 :     state.callback_name = "stream_start";
    1132         812 :     state.report_location = first_lsn;
    1133         812 :     errcallback.callback = output_plugin_error_callback;
    1134         812 :     errcallback.arg = (void *) &state;
    1135         812 :     errcallback.previous = error_context_stack;
    1136         812 :     error_context_stack = &errcallback;
    1137             : 
    1138             :     /* set output state */
    1139         812 :     ctx->accept_writes = true;
    1140         812 :     ctx->write_xid = txn->xid;
    1141             : 
    1142             :     /*
    1143             :      * report this message's lsn so replies from clients can give an up2date
    1144             :      * answer. This won't ever be enough (and shouldn't be!) to confirm
    1145             :      * receipt of this transaction, but it might allow another transaction's
    1146             :      * commit to be confirmed with one message.
    1147             :      */
    1148         812 :     ctx->write_location = first_lsn;
    1149             : 
    1150             :     /* in streaming mode, stream_start_cb is required */
    1151         812 :     if (ctx->callbacks.stream_start_cb == NULL)
    1152           0 :         ereport(ERROR,
    1153             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    1154             :                  errmsg("logical streaming requires a stream_start_cb callback")));
    1155             : 
    1156         812 :     ctx->callbacks.stream_start_cb(ctx, txn);
    1157             : 
    1158             :     /* Pop the error context stack */
    1159         812 :     error_context_stack = errcallback.previous;
    1160         812 : }
    1161             : 
    1162             : static void
    1163         806 : stream_stop_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
    1164             :                        XLogRecPtr last_lsn)
    1165             : {
    1166         806 :     LogicalDecodingContext *ctx = cache->private_data;
    1167             :     LogicalErrorCallbackState state;
    1168             :     ErrorContextCallback errcallback;
    1169             : 
    1170         806 :     Assert(!ctx->fast_forward);
    1171             : 
    1172             :     /* We're only supposed to call this when streaming is supported. */
    1173         806 :     Assert(ctx->streaming);
    1174             : 
    1175             :     /* Push callback + info on the error context stack */
    1176         806 :     state.ctx = ctx;
    1177         806 :     state.callback_name = "stream_stop";
    1178         806 :     state.report_location = last_lsn;
    1179         806 :     errcallback.callback = output_plugin_error_callback;
    1180         806 :     errcallback.arg = (void *) &state;
    1181         806 :     errcallback.previous = error_context_stack;
    1182         806 :     error_context_stack = &errcallback;
    1183             : 
    1184             :     /* set output state */
    1185         806 :     ctx->accept_writes = true;
    1186         806 :     ctx->write_xid = txn->xid;
    1187             : 
    1188             :     /*
    1189             :      * report this message's lsn so replies from clients can give an up2date
    1190             :      * answer. This won't ever be enough (and shouldn't be!) to confirm
    1191             :      * receipt of this transaction, but it might allow another transaction's
    1192             :      * commit to be confirmed with one message.
    1193             :      */
    1194         806 :     ctx->write_location = last_lsn;
    1195             : 
    1196             :     /* in streaming mode, stream_stop_cb is required */
    1197         806 :     if (ctx->callbacks.stream_stop_cb == NULL)
    1198           0 :         ereport(ERROR,
    1199             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    1200             :                  errmsg("logical streaming requires a stream_stop_cb callback")));
    1201             : 
    1202         806 :     ctx->callbacks.stream_stop_cb(ctx, txn);
    1203             : 
    1204             :     /* Pop the error context stack */
    1205         806 :     error_context_stack = errcallback.previous;
    1206         806 : }
    1207             : 
    1208             : static void
    1209          28 : stream_abort_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
    1210             :                         XLogRecPtr abort_lsn)
    1211             : {
    1212          28 :     LogicalDecodingContext *ctx = cache->private_data;
    1213             :     LogicalErrorCallbackState state;
    1214             :     ErrorContextCallback errcallback;
    1215             : 
    1216          28 :     Assert(!ctx->fast_forward);
    1217             : 
    1218             :     /* We're only supposed to call this when streaming is supported. */
    1219          28 :     Assert(ctx->streaming);
    1220             : 
    1221             :     /* Push callback + info on the error context stack */
    1222          28 :     state.ctx = ctx;
    1223          28 :     state.callback_name = "stream_abort";
    1224          28 :     state.report_location = abort_lsn;
    1225          28 :     errcallback.callback = output_plugin_error_callback;
    1226          28 :     errcallback.arg = (void *) &state;
    1227          28 :     errcallback.previous = error_context_stack;
    1228          28 :     error_context_stack = &errcallback;
    1229             : 
    1230             :     /* set output state */
    1231          28 :     ctx->accept_writes = true;
    1232          28 :     ctx->write_xid = txn->xid;
    1233          28 :     ctx->write_location = abort_lsn;
    1234             : 
    1235             :     /* in streaming mode, stream_abort_cb is required */
    1236          28 :     if (ctx->callbacks.stream_abort_cb == NULL)
    1237           0 :         ereport(ERROR,
    1238             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    1239             :                  errmsg("logical streaming requires a stream_abort_cb callback")));
    1240             : 
    1241          28 :     ctx->callbacks.stream_abort_cb(ctx, txn, abort_lsn);
    1242             : 
    1243             :     /* Pop the error context stack */
    1244          28 :     error_context_stack = errcallback.previous;
    1245          28 : }
    1246             : 
    1247             : static void
    1248          18 : stream_prepare_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
    1249             :                           XLogRecPtr prepare_lsn)
    1250             : {
    1251          18 :     LogicalDecodingContext *ctx = cache->private_data;
    1252             :     LogicalErrorCallbackState state;
    1253             :     ErrorContextCallback errcallback;
    1254             : 
    1255          18 :     Assert(!ctx->fast_forward);
    1256             : 
    1257             :     /* We're only supposed to call this when streaming and two-phase commits are supported. */
    1258          18 :     Assert(ctx->streaming);
    1259          18 :     Assert(ctx->twophase);
    1260             : 
    1261             :     /* Push callback + info on the error context stack */
    1262          18 :     state.ctx = ctx;
    1263          18 :     state.callback_name = "stream_prepare";
    1264          18 :     state.report_location = txn->final_lsn;
    1265          18 :     errcallback.callback = output_plugin_error_callback;
    1266          18 :     errcallback.arg = (void *) &state;
    1267          18 :     errcallback.previous = error_context_stack;
    1268          18 :     error_context_stack = &errcallback;
    1269             : 
    1270             :     /* set output state */
    1271          18 :     ctx->accept_writes = true;
    1272          18 :     ctx->write_xid = txn->xid;
    1273          18 :     ctx->write_location = txn->end_lsn;
    1274             : 
    1275             :     /* in streaming mode with two-phase commits, stream_prepare_cb is required */
    1276          18 :     if (ctx->callbacks.stream_prepare_cb == NULL)
    1277           0 :         ereport(ERROR,
    1278             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    1279             :                  errmsg("logical streaming commits requires a stream_prepare_cb callback")));
    1280             : 
    1281          18 :     ctx->callbacks.stream_prepare_cb(ctx, txn, prepare_lsn);
    1282             : 
    1283             :     /* Pop the error context stack */
    1284          18 :     error_context_stack = errcallback.previous;
    1285          18 : }
    1286             : 
    1287             : static void
    1288          34 : stream_commit_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
    1289             :                          XLogRecPtr commit_lsn)
    1290             : {
    1291          34 :     LogicalDecodingContext *ctx = cache->private_data;
    1292             :     LogicalErrorCallbackState state;
    1293             :     ErrorContextCallback errcallback;
    1294             : 
    1295          34 :     Assert(!ctx->fast_forward);
    1296             : 
    1297             :     /* We're only supposed to call this when streaming is supported. */
    1298          34 :     Assert(ctx->streaming);
    1299             : 
    1300             :     /* Push callback + info on the error context stack */
    1301          34 :     state.ctx = ctx;
    1302          34 :     state.callback_name = "stream_commit";
    1303          34 :     state.report_location = txn->final_lsn;
    1304          34 :     errcallback.callback = output_plugin_error_callback;
    1305          34 :     errcallback.arg = (void *) &state;
    1306          34 :     errcallback.previous = error_context_stack;
    1307          34 :     error_context_stack = &errcallback;
    1308             : 
    1309             :     /* set output state */
    1310          34 :     ctx->accept_writes = true;
    1311          34 :     ctx->write_xid = txn->xid;
    1312          34 :     ctx->write_location = txn->end_lsn;
    1313             : 
    1314             :     /* in streaming mode, stream_abort_cb is required */
    1315          34 :     if (ctx->callbacks.stream_commit_cb == NULL)
    1316           0 :         ereport(ERROR,
    1317             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    1318             :                  errmsg("logical streaming requires a stream_commit_cb callback")));
    1319             : 
    1320          34 :     ctx->callbacks.stream_commit_cb(ctx, txn, commit_lsn);
    1321             : 
    1322             :     /* Pop the error context stack */
    1323          34 :     error_context_stack = errcallback.previous;
    1324          34 : }
    1325             : 
    1326             : static void
    1327      318344 : stream_change_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
    1328             :                          Relation relation, ReorderBufferChange *change)
    1329             : {
    1330      318344 :     LogicalDecodingContext *ctx = cache->private_data;
    1331             :     LogicalErrorCallbackState state;
    1332             :     ErrorContextCallback errcallback;
    1333             : 
    1334      318344 :     Assert(!ctx->fast_forward);
    1335             : 
    1336             :     /* We're only supposed to call this when streaming is supported. */
    1337      318344 :     Assert(ctx->streaming);
    1338             : 
    1339             :     /* Push callback + info on the error context stack */
    1340      318344 :     state.ctx = ctx;
    1341      318344 :     state.callback_name = "stream_change";
    1342      318344 :     state.report_location = change->lsn;
    1343      318344 :     errcallback.callback = output_plugin_error_callback;
    1344      318344 :     errcallback.arg = (void *) &state;
    1345      318344 :     errcallback.previous = error_context_stack;
    1346      318344 :     error_context_stack = &errcallback;
    1347             : 
    1348             :     /* set output state */
    1349      318344 :     ctx->accept_writes = true;
    1350      318344 :     ctx->write_xid = txn->xid;
    1351             : 
    1352             :     /*
    1353             :      * report this change's lsn so replies from clients can give an up2date
    1354             :      * answer. This won't ever be enough (and shouldn't be!) to confirm
    1355             :      * receipt of this transaction, but it might allow another transaction's
    1356             :      * commit to be confirmed with one message.
    1357             :      */
    1358      318344 :     ctx->write_location = change->lsn;
    1359             : 
    1360             :     /* in streaming mode, stream_change_cb is required */
    1361      318344 :     if (ctx->callbacks.stream_change_cb == NULL)
    1362           0 :         ereport(ERROR,
    1363             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    1364             :                  errmsg("logical streaming requires a stream_change_cb callback")));
    1365             : 
    1366      318344 :     ctx->callbacks.stream_change_cb(ctx, txn, relation, change);
    1367             : 
    1368             :     /* Pop the error context stack */
    1369      318338 :     error_context_stack = errcallback.previous;
    1370      318338 : }
    1371             : 
    1372             : static void
    1373           2 : stream_message_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
    1374             :                           XLogRecPtr message_lsn, bool transactional,
    1375             :                           const char *prefix, Size message_size, const char *message)
    1376             : {
    1377           2 :     LogicalDecodingContext *ctx = cache->private_data;
    1378             :     LogicalErrorCallbackState state;
    1379             :     ErrorContextCallback errcallback;
    1380             : 
    1381           2 :     Assert(!ctx->fast_forward);
    1382             : 
    1383             :     /* We're only supposed to call this when streaming is supported. */
    1384           2 :     Assert(ctx->streaming);
    1385             : 
    1386             :     /* this callback is optional */
    1387           2 :     if (ctx->callbacks.stream_message_cb == NULL)
    1388           2 :         return;
    1389             : 
    1390             :     /* Push callback + info on the error context stack */
    1391           2 :     state.ctx = ctx;
    1392           2 :     state.callback_name = "stream_message";
    1393           2 :     state.report_location = message_lsn;
    1394           2 :     errcallback.callback = output_plugin_error_callback;
    1395           2 :     errcallback.arg = (void *) &state;
    1396           2 :     errcallback.previous = error_context_stack;
    1397           2 :     error_context_stack = &errcallback;
    1398             : 
    1399             :     /* set output state */
    1400           2 :     ctx->accept_writes = true;
    1401           2 :     ctx->write_xid = txn != NULL ? txn->xid : InvalidTransactionId;
    1402           2 :     ctx->write_location = message_lsn;
    1403             : 
    1404             :     /* do the actual work: call callback */
    1405           2 :     ctx->callbacks.stream_message_cb(ctx, txn, message_lsn, transactional, prefix,
    1406             :                                      message_size, message);
    1407             : 
    1408             :     /* Pop the error context stack */
    1409           2 :     error_context_stack = errcallback.previous;
    1410             : }
    1411             : 
    1412             : static void
    1413           0 : stream_truncate_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
    1414             :                            int nrelations, Relation relations[],
    1415             :                            ReorderBufferChange *change)
    1416             : {
    1417           0 :     LogicalDecodingContext *ctx = cache->private_data;
    1418             :     LogicalErrorCallbackState state;
    1419             :     ErrorContextCallback errcallback;
    1420             : 
    1421           0 :     Assert(!ctx->fast_forward);
    1422             : 
    1423             :     /* We're only supposed to call this when streaming is supported. */
    1424           0 :     Assert(ctx->streaming);
    1425             : 
    1426             :     /* this callback is optional */
    1427           0 :     if (!ctx->callbacks.stream_truncate_cb)
    1428           0 :         return;
    1429             : 
    1430             :     /* Push callback + info on the error context stack */
    1431           0 :     state.ctx = ctx;
    1432           0 :     state.callback_name = "stream_truncate";
    1433           0 :     state.report_location = change->lsn;
    1434           0 :     errcallback.callback = output_plugin_error_callback;
    1435           0 :     errcallback.arg = (void *) &state;
    1436           0 :     errcallback.previous = error_context_stack;
    1437           0 :     error_context_stack = &errcallback;
    1438             : 
    1439             :     /* set output state */
    1440           0 :     ctx->accept_writes = true;
    1441           0 :     ctx->write_xid = txn->xid;
    1442             : 
    1443             :     /*
    1444             :      * report this change's lsn so replies from clients can give an up2date
    1445             :      * answer. This won't ever be enough (and shouldn't be!) to confirm
    1446             :      * receipt of this transaction, but it might allow another transaction's
    1447             :      * commit to be confirmed with one message.
    1448             :      */
    1449           0 :     ctx->write_location = change->lsn;
    1450             : 
    1451           0 :     ctx->callbacks.stream_truncate_cb(ctx, txn, nrelations, relations, change);
    1452             : 
    1453             :     /* Pop the error context stack */
    1454           0 :     error_context_stack = errcallback.previous;
    1455             : }
    1456             : 
    1457             : /*
    1458             :  * Set the required catalog xmin horizon for historic snapshots in the current
    1459             :  * replication slot.
    1460             :  *
    1461             :  * Note that in the most cases, we won't be able to immediately use the xmin
    1462             :  * to increase the xmin horizon: we need to wait till the client has confirmed
    1463             :  * receiving current_lsn with LogicalConfirmReceivedLocation().
    1464             :  */
    1465             : void
    1466         186 : LogicalIncreaseXminForSlot(XLogRecPtr current_lsn, TransactionId xmin)
    1467             : {
    1468         186 :     bool        updated_xmin = false;
    1469             :     ReplicationSlot *slot;
    1470             : 
    1471         186 :     slot = MyReplicationSlot;
    1472             : 
    1473         186 :     Assert(slot != NULL);
    1474             : 
    1475         186 :     SpinLockAcquire(&slot->mutex);
    1476             : 
    1477             :     /*
    1478             :      * don't overwrite if we already have a newer xmin. This can happen if we
    1479             :      * restart decoding in a slot.
    1480             :      */
    1481         186 :     if (TransactionIdPrecedesOrEquals(xmin, slot->data.catalog_xmin))
    1482             :     {
    1483             :     }
    1484             : 
    1485             :     /*
    1486             :      * If the client has already confirmed up to this lsn, we directly can
    1487             :      * mark this as accepted. This can happen if we restart decoding in a
    1488             :      * slot.
    1489             :      */
    1490          28 :     else if (current_lsn <= slot->data.confirmed_flush)
    1491             :     {
    1492          16 :         slot->candidate_catalog_xmin = xmin;
    1493          16 :         slot->candidate_xmin_lsn = current_lsn;
    1494             : 
    1495             :         /* our candidate can directly be used */
    1496          16 :         updated_xmin = true;
    1497             :     }
    1498             : 
    1499             :     /*
    1500             :      * Only increase if the previous values have been applied, otherwise we
    1501             :      * might never end up updating if the receiver acks too slowly.
    1502             :      */
    1503          12 :     else if (slot->candidate_xmin_lsn == InvalidXLogRecPtr)
    1504             :     {
    1505          10 :         slot->candidate_catalog_xmin = xmin;
    1506          10 :         slot->candidate_xmin_lsn = current_lsn;
    1507             :     }
    1508         186 :     SpinLockRelease(&slot->mutex);
    1509             : 
    1510             :     /* candidate already valid with the current flush position, apply */
    1511         186 :     if (updated_xmin)
    1512          16 :         LogicalConfirmReceivedLocation(slot->data.confirmed_flush);
    1513         186 : }
    1514             : 
    1515             : /*
    1516             :  * Mark the minimal LSN (restart_lsn) we need to read to replay all
    1517             :  * transactions that have not yet committed at current_lsn.
    1518             :  *
    1519             :  * Just like LogicalIncreaseXminForSlot this only takes effect when the
    1520             :  * client has confirmed to have received current_lsn.
    1521             :  */
    1522             : void
    1523         154 : LogicalIncreaseRestartDecodingForSlot(XLogRecPtr current_lsn, XLogRecPtr restart_lsn)
    1524             : {
    1525         154 :     bool        updated_lsn = false;
    1526             :     ReplicationSlot *slot;
    1527             : 
    1528         154 :     slot = MyReplicationSlot;
    1529             : 
    1530         154 :     Assert(slot != NULL);
    1531         154 :     Assert(restart_lsn != InvalidXLogRecPtr);
    1532         154 :     Assert(current_lsn != InvalidXLogRecPtr);
    1533             : 
    1534         154 :     SpinLockAcquire(&slot->mutex);
    1535             : 
    1536             :     /* don't overwrite if have a newer restart lsn */
    1537         154 :     if (restart_lsn <= slot->data.restart_lsn)
    1538             :     {
    1539             :     }
    1540             : 
    1541             :     /*
    1542             :      * We might have already flushed far enough to directly accept this lsn,
    1543             :      * in this case there is no need to check for existing candidate LSNs
    1544             :      */
    1545         148 :     else if (current_lsn <= slot->data.confirmed_flush)
    1546             :     {
    1547         134 :         slot->candidate_restart_valid = current_lsn;
    1548         134 :         slot->candidate_restart_lsn = restart_lsn;
    1549             : 
    1550             :         /* our candidate can directly be used */
    1551         134 :         updated_lsn = true;
    1552             :     }
    1553             : 
    1554             :     /*
    1555             :      * Only increase if the previous values have been applied, otherwise we
    1556             :      * might never end up updating if the receiver acks too slowly. A missed
    1557             :      * value here will just cause some extra effort after reconnecting.
    1558             :      */
    1559         154 :     if (slot->candidate_restart_valid == InvalidXLogRecPtr)
    1560             :     {
    1561          20 :         slot->candidate_restart_valid = current_lsn;
    1562          20 :         slot->candidate_restart_lsn = restart_lsn;
    1563          20 :         SpinLockRelease(&slot->mutex);
    1564             : 
    1565          20 :         elog(DEBUG1, "got new restart lsn %X/%X at %X/%X",
    1566             :              (uint32) (restart_lsn >> 32), (uint32) restart_lsn,
    1567             :              (uint32) (current_lsn >> 32), (uint32) current_lsn);
    1568             :     }
    1569             :     else
    1570             :     {
    1571             :         XLogRecPtr  candidate_restart_lsn;
    1572             :         XLogRecPtr  candidate_restart_valid;
    1573             :         XLogRecPtr  confirmed_flush;
    1574             : 
    1575         134 :         candidate_restart_lsn = slot->candidate_restart_lsn;
    1576         134 :         candidate_restart_valid = slot->candidate_restart_valid;
    1577         134 :         confirmed_flush = slot->data.confirmed_flush;
    1578         134 :         SpinLockRelease(&slot->mutex);
    1579             : 
    1580         134 :         elog(DEBUG1, "failed to increase restart lsn: proposed %X/%X, after %X/%X, current candidate %X/%X, current after %X/%X, flushed up to %X/%X",
    1581             :              (uint32) (restart_lsn >> 32), (uint32) restart_lsn,
    1582             :              (uint32) (current_lsn >> 32), (uint32) current_lsn,
    1583             :              (uint32) (candidate_restart_lsn >> 32),
    1584             :              (uint32) candidate_restart_lsn,
    1585             :              (uint32) (candidate_restart_valid >> 32),
    1586             :              (uint32) candidate_restart_valid,
    1587             :              (uint32) (confirmed_flush >> 32),
    1588             :              (uint32) confirmed_flush);
    1589             :     }
    1590             : 
    1591             :     /* candidates are already valid with the current flush position, apply */
    1592         154 :     if (updated_lsn)
    1593         134 :         LogicalConfirmReceivedLocation(slot->data.confirmed_flush);
    1594         154 : }
    1595             : 
    1596             : /*
    1597             :  * Handle a consumer's confirmation having received all changes up to lsn.
    1598             :  */
    1599             : void
    1600       50558 : LogicalConfirmReceivedLocation(XLogRecPtr lsn)
    1601             : {
    1602       50558 :     Assert(lsn != InvalidXLogRecPtr);
    1603             : 
    1604             :     /* Do an unlocked check for candidate_lsn first. */
    1605      101092 :     if (MyReplicationSlot->candidate_xmin_lsn != InvalidXLogRecPtr ||
    1606       50534 :         MyReplicationSlot->candidate_restart_valid != InvalidXLogRecPtr)
    1607         174 :     {
    1608         174 :         bool        updated_xmin = false;
    1609         174 :         bool        updated_restart = false;
    1610             : 
    1611         174 :         SpinLockAcquire(&MyReplicationSlot->mutex);
    1612             : 
    1613         174 :         MyReplicationSlot->data.confirmed_flush = lsn;
    1614             : 
    1615             :         /* if we're past the location required for bumping xmin, do so */
    1616         198 :         if (MyReplicationSlot->candidate_xmin_lsn != InvalidXLogRecPtr &&
    1617          24 :             MyReplicationSlot->candidate_xmin_lsn <= lsn)
    1618             :         {
    1619             :             /*
    1620             :              * We have to write the changed xmin to disk *before* we change
    1621             :              * the in-memory value, otherwise after a crash we wouldn't know
    1622             :              * that some catalog tuples might have been removed already.
    1623             :              *
    1624             :              * Ensure that by first writing to ->xmin and only update
    1625             :              * ->effective_xmin once the new state is synced to disk. After a
    1626             :              * crash ->effective_xmin is set to ->xmin.
    1627             :              */
    1628          48 :             if (TransactionIdIsValid(MyReplicationSlot->candidate_catalog_xmin) &&
    1629          24 :                 MyReplicationSlot->data.catalog_xmin != MyReplicationSlot->candidate_catalog_xmin)
    1630             :             {
    1631          24 :                 MyReplicationSlot->data.catalog_xmin = MyReplicationSlot->candidate_catalog_xmin;
    1632          24 :                 MyReplicationSlot->candidate_catalog_xmin = InvalidTransactionId;
    1633          24 :                 MyReplicationSlot->candidate_xmin_lsn = InvalidXLogRecPtr;
    1634          24 :                 updated_xmin = true;
    1635             :             }
    1636             :         }
    1637             : 
    1638         330 :         if (MyReplicationSlot->candidate_restart_valid != InvalidXLogRecPtr &&
    1639         156 :             MyReplicationSlot->candidate_restart_valid <= lsn)
    1640             :         {
    1641         154 :             Assert(MyReplicationSlot->candidate_restart_lsn != InvalidXLogRecPtr);
    1642             : 
    1643         154 :             MyReplicationSlot->data.restart_lsn = MyReplicationSlot->candidate_restart_lsn;
    1644         154 :             MyReplicationSlot->candidate_restart_lsn = InvalidXLogRecPtr;
    1645         154 :             MyReplicationSlot->candidate_restart_valid = InvalidXLogRecPtr;
    1646         154 :             updated_restart = true;
    1647             :         }
    1648             : 
    1649         174 :         SpinLockRelease(&MyReplicationSlot->mutex);
    1650             : 
    1651             :         /* first write new xmin to disk, so we know what's up after a crash */
    1652         174 :         if (updated_xmin || updated_restart)
    1653             :         {
    1654         172 :             ReplicationSlotMarkDirty();
    1655         172 :             ReplicationSlotSave();
    1656         172 :             elog(DEBUG1, "updated xmin: %u restart: %u", updated_xmin, updated_restart);
    1657             :         }
    1658             : 
    1659             :         /*
    1660             :          * Now the new xmin is safely on disk, we can let the global value
    1661             :          * advance. We do not take ProcArrayLock or similar since we only
    1662             :          * advance xmin here and there's not much harm done by a concurrent
    1663             :          * computation missing that.
    1664             :          */
    1665         174 :         if (updated_xmin)
    1666             :         {
    1667          24 :             SpinLockAcquire(&MyReplicationSlot->mutex);
    1668          24 :             MyReplicationSlot->effective_catalog_xmin = MyReplicationSlot->data.catalog_xmin;
    1669          24 :             SpinLockRelease(&MyReplicationSlot->mutex);
    1670             : 
    1671          24 :             ReplicationSlotsComputeRequiredXmin(false);
    1672          24 :             ReplicationSlotsComputeRequiredLSN();
    1673             :         }
    1674             :     }
    1675             :     else
    1676             :     {
    1677       50384 :         SpinLockAcquire(&MyReplicationSlot->mutex);
    1678       50384 :         MyReplicationSlot->data.confirmed_flush = lsn;
    1679       50384 :         SpinLockRelease(&MyReplicationSlot->mutex);
    1680             :     }
    1681       50558 : }
    1682             : 
    1683             : /*
    1684             :  * Clear logical streaming state during (sub)transaction abort.
    1685             :  */
    1686             : void
    1687        2748 : ResetLogicalStreamingState(void)
    1688             : {
    1689        2748 :     CheckXidAlive = InvalidTransactionId;
    1690        2748 :     bsysscan = false;
    1691        2748 : }
    1692             : 
    1693             : /*
    1694             :  * Report stats for a slot.
    1695             :  */
    1696             : void
    1697        1164 : UpdateDecodingStats(LogicalDecodingContext *ctx)
    1698             : {
    1699        1164 :     ReorderBuffer *rb = ctx->reorder;
    1700             : 
    1701             :     /*
    1702             :      * Nothing to do if we haven't spilled anything since the last time the
    1703             :      * stats has been sent.
    1704             :      */
    1705        1164 :     if (rb->spillBytes <= 0)
    1706        2246 :         return;
    1707             : 
    1708          82 :     elog(DEBUG2, "UpdateDecodingStats: updating stats %p %lld %lld %lld",
    1709             :          rb,
    1710             :          (long long) rb->spillTxns,
    1711             :          (long long) rb->spillCount,
    1712             :          (long long) rb->spillBytes);
    1713             : 
    1714         246 :     pgstat_report_replslot(NameStr(ctx->slot->data.name),
    1715         246 :                            rb->spillTxns, rb->spillCount, rb->spillBytes);
    1716          82 :     rb->spillTxns = 0;
    1717          82 :     rb->spillCount = 0;
    1718          82 :     rb->spillBytes = 0;
    1719             : }

Generated by: LCOV version 1.14