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

Generated by: LCOV version 1.14