LCOV - code coverage report
Current view: top level - src/backend/replication/logical - decode.c (source / functions) Hit Total Coverage
Test: PostgreSQL 14devel Lines: 375 403 93.1 %
Date: 2020-11-10 11:26:58 Functions: 18 18 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -------------------------------------------------------------------------
       2             :  *
       3             :  * decode.c
       4             :  *      This module decodes WAL records read using xlogreader.h's APIs for the
       5             :  *      purpose of logical decoding by passing information to the
       6             :  *      reorderbuffer module (containing the actual changes) and to the
       7             :  *      snapbuild module to build a fitting catalog snapshot (to be able to
       8             :  *      properly decode the changes in the reorderbuffer).
       9             :  *
      10             :  * NOTE:
      11             :  *      This basically tries to handle all low level xlog stuff for
      12             :  *      reorderbuffer.c and snapbuild.c. There's some minor leakage where a
      13             :  *      specific record's struct is used to pass data along, but those just
      14             :  *      happen to contain the right amount of data in a convenient
      15             :  *      format. There isn't and shouldn't be much intelligence about the
      16             :  *      contents of records in here except turning them into a more usable
      17             :  *      format.
      18             :  *
      19             :  * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
      20             :  * Portions Copyright (c) 1994, Regents of the University of California
      21             :  *
      22             :  * IDENTIFICATION
      23             :  *    src/backend/replication/logical/decode.c
      24             :  *
      25             :  * -------------------------------------------------------------------------
      26             :  */
      27             : #include "postgres.h"
      28             : 
      29             : #include "access/heapam.h"
      30             : #include "access/heapam_xlog.h"
      31             : #include "access/transam.h"
      32             : #include "access/xact.h"
      33             : #include "access/xlog_internal.h"
      34             : #include "access/xlogreader.h"
      35             : #include "access/xlogrecord.h"
      36             : #include "access/xlogutils.h"
      37             : #include "catalog/pg_control.h"
      38             : #include "replication/decode.h"
      39             : #include "replication/logical.h"
      40             : #include "replication/message.h"
      41             : #include "replication/origin.h"
      42             : #include "replication/reorderbuffer.h"
      43             : #include "replication/snapbuild.h"
      44             : #include "storage/standby.h"
      45             : 
      46             : typedef struct XLogRecordBuffer
      47             : {
      48             :     XLogRecPtr  origptr;
      49             :     XLogRecPtr  endptr;
      50             :     XLogReaderState *record;
      51             : } XLogRecordBuffer;
      52             : 
      53             : /* RMGR Handlers */
      54             : static void DecodeXLogOp(LogicalDecodingContext *ctx, XLogRecordBuffer *buf);
      55             : static void DecodeHeapOp(LogicalDecodingContext *ctx, XLogRecordBuffer *buf);
      56             : static void DecodeHeap2Op(LogicalDecodingContext *ctx, XLogRecordBuffer *buf);
      57             : static void DecodeXactOp(LogicalDecodingContext *ctx, XLogRecordBuffer *buf);
      58             : static void DecodeStandbyOp(LogicalDecodingContext *ctx, XLogRecordBuffer *buf);
      59             : static void DecodeLogicalMsgOp(LogicalDecodingContext *ctx, XLogRecordBuffer *buf);
      60             : 
      61             : /* individual record(group)'s handlers */
      62             : static void DecodeInsert(LogicalDecodingContext *ctx, XLogRecordBuffer *buf);
      63             : static void DecodeUpdate(LogicalDecodingContext *ctx, XLogRecordBuffer *buf);
      64             : static void DecodeDelete(LogicalDecodingContext *ctx, XLogRecordBuffer *buf);
      65             : static void DecodeTruncate(LogicalDecodingContext *ctx, XLogRecordBuffer *buf);
      66             : static void DecodeMultiInsert(LogicalDecodingContext *ctx, XLogRecordBuffer *buf);
      67             : static void DecodeSpecConfirm(LogicalDecodingContext *ctx, XLogRecordBuffer *buf);
      68             : 
      69             : static void DecodeCommit(LogicalDecodingContext *ctx, XLogRecordBuffer *buf,
      70             :                          xl_xact_parsed_commit *parsed, TransactionId xid, bool prepared);
      71             : static void DecodeAbort(LogicalDecodingContext *ctx, XLogRecordBuffer *buf,
      72             :                         xl_xact_parsed_abort *parsed, TransactionId xid, bool prepared);
      73             : static void DecodePrepare(LogicalDecodingContext *ctx, XLogRecordBuffer *buf,
      74             :                           xl_xact_parsed_prepare *parsed);
      75             : 
      76             : 
      77             : /* common function to decode tuples */
      78             : static void DecodeXLogTuple(char *data, Size len, ReorderBufferTupleBuf *tup);
      79             : 
      80             : /*
      81             :  * Take every XLogReadRecord()ed record and perform the actions required to
      82             :  * decode it using the output plugin already setup in the logical decoding
      83             :  * context.
      84             :  *
      85             :  * NB: Note that every record's xid needs to be processed by reorderbuffer
      86             :  * (xids contained in the content of records are not relevant for this rule).
      87             :  * That means that for records which'd otherwise not go through the
      88             :  * reorderbuffer ReorderBufferProcessXid() has to be called. We don't want to
      89             :  * call ReorderBufferProcessXid for each record type by default, because
      90             :  * e.g. empty xacts can be handled more efficiently if there's no previous
      91             :  * state for them.
      92             :  *
      93             :  * We also support the ability to fast forward thru records, skipping some
      94             :  * record types completely - see individual record types for details.
      95             :  */
      96             : void
      97     4375226 : LogicalDecodingProcessRecord(LogicalDecodingContext *ctx, XLogReaderState *record)
      98             : {
      99             :     XLogRecordBuffer buf;
     100             :     TransactionId txid;
     101             : 
     102     4375226 :     buf.origptr = ctx->reader->ReadRecPtr;
     103     4375226 :     buf.endptr = ctx->reader->EndRecPtr;
     104     4375226 :     buf.record = record;
     105             : 
     106     4375226 :     txid = XLogRecGetTopXid(record);
     107             : 
     108             :     /*
     109             :      * If the top-level xid is valid, we need to assign the subxact to the
     110             :      * top-level xact. We need to do this for all records, hence we do it
     111             :      * before the switch.
     112             :      */
     113     4375226 :     if (TransactionIdIsValid(txid))
     114             :     {
     115        2588 :         ReorderBufferAssignChild(ctx->reorder,
     116             :                                  txid,
     117        1294 :                                  record->decoded_record->xl_xid,
     118             :                                  buf.origptr);
     119             :     }
     120             : 
     121             :     /* cast so we get a warning when new rmgrs are added */
     122     4375226 :     switch ((RmgrId) XLogRecGetRmid(record))
     123             :     {
     124             :             /*
     125             :              * Rmgrs we care about for logical decoding. Add new rmgrs in
     126             :              * rmgrlist.h's order.
     127             :              */
     128             :         case RM_XLOG_ID:
     129        4686 :             DecodeXLogOp(ctx, &buf);
     130        4686 :             break;
     131             : 
     132             :         case RM_XACT_ID:
     133        9768 :             DecodeXactOp(ctx, &buf);
     134        9768 :             break;
     135             : 
     136             :         case RM_STANDBY_ID:
     137        3834 :             DecodeStandbyOp(ctx, &buf);
     138        3834 :             break;
     139             : 
     140             :         case RM_HEAP2_ID:
     141       42666 :             DecodeHeap2Op(ctx, &buf);
     142       42666 :             break;
     143             : 
     144             :         case RM_HEAP_ID:
     145     3329214 :             DecodeHeapOp(ctx, &buf);
     146     3329210 :             break;
     147             : 
     148             :         case RM_LOGICALMSG_ID:
     149          94 :             DecodeLogicalMsgOp(ctx, &buf);
     150          94 :             break;
     151             : 
     152             :             /*
     153             :              * Rmgrs irrelevant for logical decoding; they describe stuff not
     154             :              * represented in logical decoding. Add new rmgrs in rmgrlist.h's
     155             :              * order.
     156             :              */
     157             :         case RM_SMGR_ID:
     158             :         case RM_CLOG_ID:
     159             :         case RM_DBASE_ID:
     160             :         case RM_TBLSPC_ID:
     161             :         case RM_MULTIXACT_ID:
     162             :         case RM_RELMAP_ID:
     163             :         case RM_BTREE_ID:
     164             :         case RM_HASH_ID:
     165             :         case RM_GIN_ID:
     166             :         case RM_GIST_ID:
     167             :         case RM_SEQ_ID:
     168             :         case RM_SPGIST_ID:
     169             :         case RM_BRIN_ID:
     170             :         case RM_COMMIT_TS_ID:
     171             :         case RM_REPLORIGIN_ID:
     172             :         case RM_GENERIC_ID:
     173             :             /* just deal with xid, and done */
     174      984964 :             ReorderBufferProcessXid(ctx->reorder, XLogRecGetXid(record),
     175             :                                     buf.origptr);
     176      984964 :             break;
     177             :         case RM_NEXT_ID:
     178           0 :             elog(ERROR, "unexpected RM_NEXT_ID rmgr_id: %u", (RmgrIds) XLogRecGetRmid(buf.record));
     179             :     }
     180     4375222 : }
     181             : 
     182             : /*
     183             :  * Handle rmgr XLOG_ID records for DecodeRecordIntoReorderBuffer().
     184             :  */
     185             : static void
     186        4686 : DecodeXLogOp(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
     187             : {
     188        4686 :     SnapBuild  *builder = ctx->snapshot_builder;
     189        4686 :     uint8       info = XLogRecGetInfo(buf->record) & ~XLR_INFO_MASK;
     190             : 
     191        4686 :     ReorderBufferProcessXid(ctx->reorder, XLogRecGetXid(buf->record),
     192             :                             buf->origptr);
     193             : 
     194        4686 :     switch (info)
     195             :     {
     196             :             /* this is also used in END_OF_RECOVERY checkpoints */
     197             :         case XLOG_CHECKPOINT_SHUTDOWN:
     198             :         case XLOG_END_OF_RECOVERY:
     199          30 :             SnapBuildSerializationPoint(builder, buf->origptr);
     200             : 
     201          30 :             break;
     202             :         case XLOG_CHECKPOINT_ONLINE:
     203             : 
     204             :             /*
     205             :              * a RUNNING_XACTS record will have been logged near to this, we
     206             :              * can restart from there.
     207             :              */
     208          26 :             break;
     209             :         case XLOG_NOOP:
     210             :         case XLOG_NEXTOID:
     211             :         case XLOG_SWITCH:
     212             :         case XLOG_BACKUP_END:
     213             :         case XLOG_PARAMETER_CHANGE:
     214             :         case XLOG_RESTORE_POINT:
     215             :         case XLOG_FPW_CHANGE:
     216             :         case XLOG_FPI_FOR_HINT:
     217             :         case XLOG_FPI:
     218        4630 :             break;
     219             :         default:
     220           0 :             elog(ERROR, "unexpected RM_XLOG_ID record type: %u", info);
     221             :     }
     222        4686 : }
     223             : 
     224             : /*
     225             :  * Handle rmgr XACT_ID records for DecodeRecordIntoReorderBuffer().
     226             :  */
     227             : static void
     228        9768 : DecodeXactOp(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
     229             : {
     230        9768 :     SnapBuild  *builder = ctx->snapshot_builder;
     231        9768 :     ReorderBuffer *reorder = ctx->reorder;
     232        9768 :     XLogReaderState *r = buf->record;
     233        9768 :     uint8       info = XLogRecGetInfo(r) & XLOG_XACT_OPMASK;
     234             : 
     235             :     /*
     236             :      * If the snapshot isn't yet fully built, we cannot decode anything, so
     237             :      * bail out.
     238             :      */
     239        9768 :     if (SnapBuildCurrentState(builder) < SNAPBUILD_FULL_SNAPSHOT)
     240        9782 :         return;
     241             : 
     242        9754 :     switch (info)
     243             :     {
     244             :         case XLOG_XACT_COMMIT:
     245             :         case XLOG_XACT_COMMIT_PREPARED:
     246             :             {
     247             :                 xl_xact_commit *xlrec;
     248             :                 xl_xact_parsed_commit parsed;
     249             :                 TransactionId xid;
     250             :                 bool        prepared;
     251             : 
     252        3154 :                 xlrec = (xl_xact_commit *) XLogRecGetData(r);
     253        3154 :                 ParseCommitRecord(XLogRecGetInfo(buf->record), xlrec, &parsed);
     254             : 
     255             :                 /*
     256             :                  * If this is COMMIT_PREPARED and the output plugin supports
     257             :                  * two-phase commits then set the prepared flag to true.
     258             :                  */
     259        3154 :                 prepared = ((info == XLOG_XACT_COMMIT_PREPARED) && ctx->twophase) ? true : false;
     260             : 
     261        3154 :                 if (!TransactionIdIsValid(parsed.twophase_xid))
     262        3026 :                     xid = XLogRecGetXid(r);
     263             :                 else
     264         128 :                     xid = parsed.twophase_xid;
     265             : 
     266        3154 :                 DecodeCommit(ctx, buf, &parsed, xid, prepared);
     267        3154 :                 break;
     268             :             }
     269             :         case XLOG_XACT_ABORT:
     270             :         case XLOG_XACT_ABORT_PREPARED:
     271             :             {
     272             :                 xl_xact_abort *xlrec;
     273             :                 xl_xact_parsed_abort parsed;
     274             :                 TransactionId xid;
     275             :                 bool        prepared;
     276             : 
     277         176 :                 xlrec = (xl_xact_abort *) XLogRecGetData(r);
     278         176 :                 ParseAbortRecord(XLogRecGetInfo(buf->record), xlrec, &parsed);
     279             : 
     280         176 :                 if (!TransactionIdIsValid(parsed.twophase_xid))
     281         108 :                     xid = XLogRecGetXid(r);
     282             :                 else
     283          68 :                     xid = parsed.twophase_xid;
     284             : 
     285             :                 /*
     286             :                  * If this is ABORT_PREPARED and the output plugin supports
     287             :                  * two-phase commits then set the prepared flag to true.
     288             :                  */
     289         176 :                 prepared = ((info == XLOG_XACT_ABORT_PREPARED) && ctx->twophase) ? true : false;
     290             : 
     291         176 :                 DecodeAbort(ctx, buf, &parsed, xid, prepared);
     292         176 :                 break;
     293             :             }
     294             :         case XLOG_XACT_ASSIGNMENT:
     295             : 
     296             :             /*
     297             :              * We assign subxact to the toplevel xact while processing each
     298             :              * record if required.  So, we don't need to do anything here. See
     299             :              * LogicalDecodingProcessRecord.
     300             :              */
     301         262 :             break;
     302             :         case XLOG_XACT_INVALIDATIONS:
     303             :             {
     304             :                 TransactionId xid;
     305             :                 xl_xact_invals *invals;
     306             : 
     307        5938 :                 xid = XLogRecGetXid(r);
     308        5938 :                 invals = (xl_xact_invals *) XLogRecGetData(r);
     309             : 
     310             :                 /*
     311             :                  * Execute the invalidations for xid-less transactions,
     312             :                  * otherwise, accumulate them so that they can be processed at
     313             :                  * the commit time.
     314             :                  */
     315        5938 :                 if (TransactionIdIsValid(xid))
     316             :                 {
     317        5938 :                     if (!ctx->fast_forward)
     318        5938 :                         ReorderBufferAddInvalidations(reorder, xid,
     319             :                                                       buf->origptr,
     320        5938 :                                                       invals->nmsgs,
     321        5938 :                                                       invals->msgs);
     322        5938 :                     ReorderBufferXidSetCatalogChanges(ctx->reorder, xid,
     323             :                                                       buf->origptr);
     324             :                 }
     325           0 :                 else if ((!ctx->fast_forward))
     326           0 :                     ReorderBufferImmediateInvalidation(ctx->reorder,
     327           0 :                                                        invals->nmsgs,
     328           0 :                                                        invals->msgs);
     329             :             }
     330        5938 :             break;
     331             :         case XLOG_XACT_PREPARE:
     332             :             {
     333             :                 xl_xact_parsed_prepare parsed;
     334             :                 xl_xact_prepare *xlrec;
     335             : 
     336             :                 /* check that output plugin is capable of two-phase decoding */
     337         224 :                 if (!ctx->twophase)
     338             :                 {
     339           6 :                     ReorderBufferProcessXid(reorder, XLogRecGetXid(r), buf->origptr);
     340           6 :                     break;
     341             :                 }
     342             : 
     343             :                 /* ok, parse it */
     344         218 :                 xlrec = (xl_xact_prepare *) XLogRecGetData(r);
     345         218 :                 ParsePrepareRecord(XLogRecGetInfo(buf->record),
     346             :                                    xlrec, &parsed);
     347             : 
     348             :                 /* does output plugin want this particular transaction? */
     349         358 :                 if (ctx->callbacks.filter_prepare_cb &&
     350         140 :                     ReorderBufferPrepareNeedSkip(reorder, parsed.twophase_xid,
     351             :                                                  parsed.twophase_gid))
     352             :                 {
     353          10 :                     ReorderBufferProcessXid(reorder, parsed.twophase_xid,
     354             :                                             buf->origptr);
     355          10 :                     break;
     356             :                 }
     357             : 
     358         208 :                 DecodePrepare(ctx, buf, &parsed);
     359         208 :                 break;
     360             :             }
     361             :         default:
     362           0 :             elog(ERROR, "unexpected RM_XACT_ID record type: %u", info);
     363             :     }
     364             : }
     365             : 
     366             : /*
     367             :  * Handle rmgr STANDBY_ID records for DecodeRecordIntoReorderBuffer().
     368             :  */
     369             : static void
     370        3834 : DecodeStandbyOp(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
     371             : {
     372        3834 :     SnapBuild  *builder = ctx->snapshot_builder;
     373        3834 :     XLogReaderState *r = buf->record;
     374        3834 :     uint8       info = XLogRecGetInfo(r) & ~XLR_INFO_MASK;
     375             : 
     376        3834 :     ReorderBufferProcessXid(ctx->reorder, XLogRecGetXid(r), buf->origptr);
     377             : 
     378        3834 :     switch (info)
     379             :     {
     380             :         case XLOG_RUNNING_XACTS:
     381             :             {
     382         984 :                 xl_running_xacts *running = (xl_running_xacts *) XLogRecGetData(r);
     383             : 
     384         984 :                 SnapBuildProcessRunningXacts(builder, buf->origptr, running);
     385             : 
     386             :                 /*
     387             :                  * Abort all transactions that we keep track of, that are
     388             :                  * older than the record's oldestRunningXid. This is the most
     389             :                  * convenient spot for doing so since, in contrast to shutdown
     390             :                  * or end-of-recovery checkpoints, we have information about
     391             :                  * all running transactions which includes prepared ones,
     392             :                  * while shutdown checkpoints just know that no non-prepared
     393             :                  * transactions are in progress.
     394             :                  */
     395         984 :                 ReorderBufferAbortOld(ctx->reorder, running->oldestRunningXid);
     396             :             }
     397         984 :             break;
     398             :         case XLOG_STANDBY_LOCK:
     399        2850 :             break;
     400             :         case XLOG_INVALIDATIONS:
     401             : 
     402             :             /*
     403             :              * We are processing the invalidations at the command level via
     404             :              * XLOG_XACT_INVALIDATIONS.  So we don't need to do anything here.
     405             :              */
     406           0 :             break;
     407             :         default:
     408           0 :             elog(ERROR, "unexpected RM_STANDBY_ID record type: %u", info);
     409             :     }
     410        3834 : }
     411             : 
     412             : /*
     413             :  * Handle rmgr HEAP2_ID records for DecodeRecordIntoReorderBuffer().
     414             :  */
     415             : static void
     416       42666 : DecodeHeap2Op(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
     417             : {
     418       42666 :     uint8       info = XLogRecGetInfo(buf->record) & XLOG_HEAP_OPMASK;
     419       42666 :     TransactionId xid = XLogRecGetXid(buf->record);
     420       42666 :     SnapBuild  *builder = ctx->snapshot_builder;
     421             : 
     422       42666 :     ReorderBufferProcessXid(ctx->reorder, xid, buf->origptr);
     423             : 
     424             :     /*
     425             :      * If we don't have snapshot or we are just fast-forwarding, there is no
     426             :      * point in decoding changes.
     427             :      */
     428       85316 :     if (SnapBuildCurrentState(builder) < SNAPBUILD_FULL_SNAPSHOT ||
     429       42650 :         ctx->fast_forward)
     430       42682 :         return;
     431             : 
     432       42650 :     switch (info)
     433             :     {
     434             :         case XLOG_HEAP2_MULTI_INSERT:
     435       15564 :             if (!ctx->fast_forward &&
     436        7782 :                 SnapBuildProcessChange(builder, xid, buf->origptr))
     437        7782 :                 DecodeMultiInsert(ctx, buf);
     438        7782 :             break;
     439             :         case XLOG_HEAP2_NEW_CID:
     440             :             {
     441             :                 xl_heap_new_cid *xlrec;
     442             : 
     443       31518 :                 xlrec = (xl_heap_new_cid *) XLogRecGetData(buf->record);
     444       31518 :                 SnapBuildProcessNewCid(builder, xid, buf->origptr, xlrec);
     445             : 
     446       31518 :                 break;
     447             :             }
     448             :         case XLOG_HEAP2_REWRITE:
     449             : 
     450             :             /*
     451             :              * Although these records only exist to serve the needs of logical
     452             :              * decoding, all the work happens as part of crash or archive
     453             :              * recovery, so we don't need to do anything here.
     454             :              */
     455         178 :             break;
     456             : 
     457             :             /*
     458             :              * Everything else here is just low level physical stuff we're not
     459             :              * interested in.
     460             :              */
     461             :         case XLOG_HEAP2_FREEZE_PAGE:
     462             :         case XLOG_HEAP2_CLEAN:
     463             :         case XLOG_HEAP2_CLEANUP_INFO:
     464             :         case XLOG_HEAP2_VISIBLE:
     465             :         case XLOG_HEAP2_LOCK_UPDATED:
     466        3172 :             break;
     467             :         default:
     468           0 :             elog(ERROR, "unexpected RM_HEAP2_ID record type: %u", info);
     469             :     }
     470             : }
     471             : 
     472             : /*
     473             :  * Handle rmgr HEAP_ID records for DecodeRecordIntoReorderBuffer().
     474             :  */
     475             : static void
     476     3329214 : DecodeHeapOp(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
     477             : {
     478     3329214 :     uint8       info = XLogRecGetInfo(buf->record) & XLOG_HEAP_OPMASK;
     479     3329214 :     TransactionId xid = XLogRecGetXid(buf->record);
     480     3329214 :     SnapBuild  *builder = ctx->snapshot_builder;
     481             : 
     482     3329214 :     ReorderBufferProcessXid(ctx->reorder, xid, buf->origptr);
     483             : 
     484             :     /*
     485             :      * If we don't have snapshot or we are just fast-forwarding, there is no
     486             :      * point in decoding data changes.
     487             :      */
     488     6658424 :     if (SnapBuildCurrentState(builder) < SNAPBUILD_FULL_SNAPSHOT ||
     489     3329210 :         ctx->fast_forward)
     490     3329228 :         return;
     491             : 
     492     3329196 :     switch (info)
     493             :     {
     494             :         case XLOG_HEAP_INSERT:
     495     2230384 :             if (SnapBuildProcessChange(builder, xid, buf->origptr))
     496     2230384 :                 DecodeInsert(ctx, buf);
     497     2230384 :             break;
     498             : 
     499             :             /*
     500             :              * Treat HOT update as normal updates. There is no useful
     501             :              * information in the fact that we could make it a HOT update
     502             :              * locally and the WAL layout is compatible.
     503             :              */
     504             :         case XLOG_HEAP_HOT_UPDATE:
     505             :         case XLOG_HEAP_UPDATE:
     506      317740 :             if (SnapBuildProcessChange(builder, xid, buf->origptr))
     507      317740 :                 DecodeUpdate(ctx, buf);
     508      317740 :             break;
     509             : 
     510             :         case XLOG_HEAP_DELETE:
     511      393744 :             if (SnapBuildProcessChange(builder, xid, buf->origptr))
     512      393744 :                 DecodeDelete(ctx, buf);
     513      393740 :             break;
     514             : 
     515             :         case XLOG_HEAP_TRUNCATE:
     516          50 :             if (SnapBuildProcessChange(builder, xid, buf->origptr))
     517          50 :                 DecodeTruncate(ctx, buf);
     518          50 :             break;
     519             : 
     520             :         case XLOG_HEAP_INPLACE:
     521             : 
     522             :             /*
     523             :              * Inplace updates are only ever performed on catalog tuples and
     524             :              * can, per definition, not change tuple visibility.  Since we
     525             :              * don't decode catalog tuples, we're not interested in the
     526             :              * record's contents.
     527             :              *
     528             :              * In-place updates can be used either by XID-bearing transactions
     529             :              * (e.g.  in CREATE INDEX CONCURRENTLY) or by XID-less
     530             :              * transactions (e.g.  VACUUM).  In the former case, the commit
     531             :              * record will include cache invalidations, so we mark the
     532             :              * transaction as catalog modifying here. Currently that's
     533             :              * redundant because the commit will do that as well, but once we
     534             :              * support decoding in-progress relations, this will be important.
     535             :              */
     536        1500 :             if (!TransactionIdIsValid(xid))
     537           6 :                 break;
     538             : 
     539        1494 :             SnapBuildProcessChange(builder, xid, buf->origptr);
     540        1494 :             ReorderBufferXidSetCatalogChanges(ctx->reorder, xid, buf->origptr);
     541        1494 :             break;
     542             : 
     543             :         case XLOG_HEAP_CONFIRM:
     544       35800 :             if (SnapBuildProcessChange(builder, xid, buf->origptr))
     545       35800 :                 DecodeSpecConfirm(ctx, buf);
     546       35800 :             break;
     547             : 
     548             :         case XLOG_HEAP_LOCK:
     549             :             /* we don't care about row level locks for now */
     550      349978 :             break;
     551             : 
     552             :         default:
     553           0 :             elog(ERROR, "unexpected RM_HEAP_ID record type: %u", info);
     554             :             break;
     555             :     }
     556             : }
     557             : 
     558             : static inline bool
     559     2974488 : FilterByOrigin(LogicalDecodingContext *ctx, RepOriginId origin_id)
     560             : {
     561     2974488 :     if (ctx->callbacks.filter_by_origin_cb == NULL)
     562           0 :         return false;
     563             : 
     564     2974488 :     return filter_by_origin_cb_wrapper(ctx, origin_id);
     565             : }
     566             : 
     567             : /*
     568             :  * Handle rmgr LOGICALMSG_ID records for DecodeRecordIntoReorderBuffer().
     569             :  */
     570             : static void
     571          94 : DecodeLogicalMsgOp(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
     572             : {
     573          94 :     SnapBuild  *builder = ctx->snapshot_builder;
     574          94 :     XLogReaderState *r = buf->record;
     575          94 :     TransactionId xid = XLogRecGetXid(r);
     576          94 :     uint8       info = XLogRecGetInfo(r) & ~XLR_INFO_MASK;
     577          94 :     RepOriginId origin_id = XLogRecGetOrigin(r);
     578             :     Snapshot    snapshot;
     579             :     xl_logical_message *message;
     580             : 
     581          94 :     if (info != XLOG_LOGICAL_MESSAGE)
     582           0 :         elog(ERROR, "unexpected RM_LOGICALMSG_ID record type: %u", info);
     583             : 
     584          94 :     ReorderBufferProcessXid(ctx->reorder, XLogRecGetXid(r), buf->origptr);
     585             : 
     586             :     /*
     587             :      * If we don't have snapshot or we are just fast-forwarding, there is no
     588             :      * point in decoding messages.
     589             :      */
     590         188 :     if (SnapBuildCurrentState(builder) < SNAPBUILD_FULL_SNAPSHOT ||
     591          94 :         ctx->fast_forward)
     592           0 :         return;
     593             : 
     594          94 :     message = (xl_logical_message *) XLogRecGetData(r);
     595             : 
     596         184 :     if (message->dbId != ctx->slot->data.database ||
     597          90 :         FilterByOrigin(ctx, origin_id))
     598           8 :         return;
     599             : 
     600         160 :     if (message->transactional &&
     601          74 :         !SnapBuildProcessChange(builder, xid, buf->origptr))
     602           0 :         return;
     603          98 :     else if (!message->transactional &&
     604          24 :              (SnapBuildCurrentState(builder) != SNAPBUILD_CONSISTENT ||
     605          12 :               SnapBuildXactNeedsSkip(builder, buf->origptr)))
     606           6 :         return;
     607             : 
     608          80 :     snapshot = SnapBuildGetOrBuildSnapshot(builder, xid);
     609         160 :     ReorderBufferQueueMessage(ctx->reorder, xid, snapshot, buf->endptr,
     610          80 :                               message->transactional,
     611          80 :                               message->message, /* first part of message is
     612             :                                                  * prefix */
     613             :                               message->message_size,
     614          80 :                               message->message + message->prefix_size);
     615             : }
     616             : 
     617             : /*
     618             :  * Consolidated commit record handling between the different form of commit
     619             :  * records. Handles both XLOG_XACT_COMMIT and XLOG_XACT_COMMIT_PREPARED.
     620             :  * prepared is set to true if XLOG_XACT_COMMIT_PREPARED.
     621             :  */
     622             : static void
     623        3154 : DecodeCommit(LogicalDecodingContext *ctx, XLogRecordBuffer *buf,
     624             :              xl_xact_parsed_commit *parsed, TransactionId xid, bool prepared)
     625             : {
     626        3154 :     XLogRecPtr  origin_lsn = InvalidXLogRecPtr;
     627        3154 :     TimestampTz commit_time = parsed->xact_time;
     628        3154 :     RepOriginId origin_id = XLogRecGetOrigin(buf->record);
     629             :     int         i;
     630             : 
     631        3154 :     if (parsed->xinfo & XACT_XINFO_HAS_ORIGIN)
     632             :     {
     633           4 :         origin_lsn = parsed->origin_lsn;
     634           4 :         commit_time = parsed->origin_timestamp;
     635             :     }
     636             : 
     637        3154 :     SnapBuildCommitTxn(ctx->snapshot_builder, buf->origptr, xid,
     638             :                        parsed->nsubxacts, parsed->subxacts);
     639             : 
     640             :     /* ----
     641             :      * Check whether we are interested in this specific transaction, and tell
     642             :      * the reorderbuffer to forget the content of the (sub-)transactions
     643             :      * if not.
     644             :      *
     645             :      * There can be several reasons we might not be interested in this
     646             :      * transaction:
     647             :      * 1) We might not be interested in decoding transactions up to this
     648             :      *    LSN. This can happen because we previously decoded it and now just
     649             :      *    are restarting or if we haven't assembled a consistent snapshot yet.
     650             :      * 2) The transaction happened in another database.
     651             :      * 3) The output plugin is not interested in the origin.
     652             :      * 4) We are doing fast-forwarding
     653             :      *
     654             :      * We can't just use ReorderBufferAbort() here, because we need to execute
     655             :      * the transaction's invalidations.  This currently won't be needed if
     656             :      * we're just skipping over the transaction because currently we only do
     657             :      * so during startup, to get to the first transaction the client needs. As
     658             :      * we have reset the catalog caches before starting to read WAL, and we
     659             :      * haven't yet touched any catalogs, there can't be anything to invalidate.
     660             :      * But if we're "forgetting" this commit because it's it happened in
     661             :      * another database, the invalidations might be important, because they
     662             :      * could be for shared catalogs and we might have loaded data into the
     663             :      * relevant syscaches.
     664             :      * ---
     665             :      */
     666        4294 :     if (SnapBuildXactNeedsSkip(ctx->snapshot_builder, buf->origptr) ||
     667        3418 :         (parsed->dbId != InvalidOid && parsed->dbId != ctx->slot->data.database) ||
     668        2266 :         ctx->fast_forward || FilterByOrigin(ctx, origin_id))
     669             :     {
     670        3976 :         for (i = 0; i < parsed->nsubxacts; i++)
     671             :         {
     672        1948 :             ReorderBufferForget(ctx->reorder, parsed->subxacts[i], buf->origptr);
     673             :         }
     674        2028 :         ReorderBufferForget(ctx->reorder, xid, buf->origptr);
     675             : 
     676        5182 :         return;
     677             :     }
     678             : 
     679             :     /* tell the reorderbuffer about the surviving subtransactions */
     680        1640 :     for (i = 0; i < parsed->nsubxacts; i++)
     681             :     {
     682         514 :         ReorderBufferCommitChild(ctx->reorder, xid, parsed->subxacts[i],
     683             :                                  buf->origptr, buf->endptr);
     684             :     }
     685             : 
     686             :     /*
     687             :      * This function could be called for COMMIT or COMMIT PREPARED (part of a
     688             :      * two-phase commit) determined by the flag 'prepared'. If it is a regular
     689             :      * COMMIT we need to replay all actions of the transaction in order by
     690             :      * calling ReorderBufferCommit.
     691             :      *
     692             :      * If it is a COMMIT PREPARED, we check if this has been asked to be
     693             :      * filtered using the filter prepare callback. If yes, then this
     694             :      * transaction has not been decoded at PREPARE and needs to be handled
     695             :      * like a regular COMMIT.
     696             :      *
     697             :      * If COMMIT PREPARED and not filtered we only need to call the
     698             :      * corresponding callbacks as actions of the transaction were already
     699             :      * replayed at PREPARE.
     700             :      */
     701        1142 :     if (!prepared || (ctx->callbacks.filter_prepare_cb &&
     702          16 :                       ReorderBufferPrepareNeedSkip(ctx->reorder, xid, parsed->twophase_gid)))
     703             :     {
     704        1094 :         ReorderBufferCommit(ctx->reorder, xid, buf->origptr, buf->endptr,
     705             :                             commit_time, origin_id, origin_lsn);
     706             : 
     707             :         /*
     708             :          * Update the decoding stats at transaction commit/abort. It is not
     709             :          * clear that sending more or less frequently than this would be
     710             :          * better.
     711             :          */
     712        1094 :         UpdateDecodingStats(ctx);
     713             :     }
     714             :     else
     715             :     {
     716          32 :         ReorderBufferFinishPrepared(ctx->reorder, xid, buf->origptr, buf->endptr,
     717             :                                     commit_time, origin_id, origin_lsn,
     718          32 :                                     parsed->twophase_gid, true);
     719             :     }
     720             : 
     721             : }
     722             : 
     723             : /*
     724             :  * Decode PREPARE record. Similar logic as in COMMIT
     725             :  */
     726             : static void
     727         208 : DecodePrepare(LogicalDecodingContext *ctx, XLogRecordBuffer *buf,
     728             :               xl_xact_parsed_prepare *parsed)
     729             : {
     730         208 :     XLogRecPtr  origin_lsn = parsed->origin_lsn;
     731         208 :     TimestampTz commit_time = parsed->origin_timestamp;
     732         208 :     XLogRecPtr  origin_id = XLogRecGetOrigin(buf->record);
     733             :     int         i;
     734         208 :     TransactionId xid = parsed->twophase_xid;
     735             : 
     736             :     /* ----
     737             :      * Check whether we are interested in this specific transaction, and tell
     738             :      * the reorderbuffer to forget the content of the (sub-)transactions
     739             :      * if not.
     740             :      *
     741             :      * There can be several reasons we might not be interested in this
     742             :      * transaction:
     743             :      * 1) We might not be interested in decoding transactions up to this
     744             :      *    LSN. This can happen because we previously decoded it and now just
     745             :      *    are restarting or if we haven't assembled a consistent snapshot yet.
     746             :      * 2) The transaction happened in another database.
     747             :      * 3) The output plugin is not interested in the origin.
     748             :      * 4) We are doing fast-forwarding
     749             :      *
     750             :      * We can't call ReorderBufferForget like we did in DecodeCommit as the
     751             :      * txn hasn't yet been committed, removing the reorderbuffers before a
     752             :      * commit might result in the computation of an incorrect restart_lsn.
     753             :      * But we need to process cache invalidation if there are any.
     754             :      * Even if we're not interested in the transaction's contents, it could
     755             :      * have manipulated the catalog and we need to update the caches accordingly.
     756             :      */
     757             : 
     758         260 :     if (SnapBuildXactNeedsSkip(ctx->snapshot_builder, buf->origptr) ||
     759         156 :         (parsed->dbId != InvalidOid && parsed->dbId != ctx->slot->data.database) ||
     760         104 :         ctx->fast_forward || FilterByOrigin(ctx, origin_id))
     761             :     {
     762         156 :         ReorderBufferInvalidate(ctx->reorder, xid, buf->origptr);
     763         364 :         return;
     764             :     }
     765             : 
     766             :     /*
     767             :      * Tell the reorderbuffer about the surviving subtransactions. We need to
     768             :      * do this because the main transaction itself has not committed since we
     769             :      * are in the prepare phase right now. So we need to be sure the snapshot
     770             :      * is setup correctly for the main transaction in case all changes
     771             :      * happened in subtransanctions
     772             :      */
     773          56 :     for (i = 0; i < parsed->nsubxacts; i++)
     774             :     {
     775           4 :         ReorderBufferCommitChild(ctx->reorder, xid, parsed->subxacts[i],
     776             :                                  buf->origptr, buf->endptr);
     777             :     }
     778             : 
     779             :     /* replay actions of all transaction + subtransactions in order */
     780          52 :     ReorderBufferPrepare(ctx->reorder, xid, buf->origptr, buf->endptr,
     781          52 :                          commit_time, origin_id, origin_lsn, parsed->twophase_gid);
     782             : 
     783             :     /*
     784             :      * Update the decoding stats at transaction commit/two-phase
     785             :      * prepare/abort. It is not clear that sending more or less frequently
     786             :      * than this would be better.
     787             :      */
     788          52 :     UpdateDecodingStats(ctx);
     789             : }
     790             : 
     791             : 
     792             : /*
     793             :  * Get the data from the various forms of abort records and pass it on to
     794             :  * snapbuild.c and reorderbuffer.c. This could be called either on
     795             :  * a XLOG_XACT_ABORT or on  a XLOG_XACT_ABORT_PREPARED. The prepared flag
     796             :  * is set if called on a XLOG_XACT_ABORT_PREPARED.
     797             :  */
     798             : static void
     799         176 : DecodeAbort(LogicalDecodingContext *ctx, XLogRecordBuffer *buf,
     800             :             xl_xact_parsed_abort *parsed, TransactionId xid, bool prepared)
     801             : {
     802             :     int         i;
     803         176 :     XLogRecPtr  origin_lsn = InvalidXLogRecPtr;
     804         176 :     TimestampTz commit_time = 0;
     805         176 :     XLogRecPtr  origin_id = XLogRecGetOrigin(buf->record);
     806             : 
     807         176 :     if (parsed->xinfo & XACT_XINFO_HAS_ORIGIN)
     808             :     {
     809           0 :         origin_lsn = parsed->origin_lsn;
     810           0 :         commit_time = parsed->origin_timestamp;
     811             :     }
     812             : 
     813             :     /*
     814             :      * If this is a regular ABORT or to be filtered then just clean up by
     815             :      * calling ReorderBufferAbort, otherwise if not to be skipped or filtered
     816             :      * and previously prepared then it is a ROLLBACK PREPARED.
     817             :      */
     818         242 :     if (!prepared ||
     819         100 :         (ctx->callbacks.filter_prepare_cb &&
     820         100 :          ReorderBufferPrepareNeedSkip(ctx->reorder, xid, parsed->twophase_gid)) ||
     821         132 :         FilterByOrigin(ctx, origin_id) ||
     822          90 :         SnapBuildXactNeedsSkip(ctx->snapshot_builder, buf->origptr) ||
     823          24 :         parsed->dbId != ctx->slot->data.database)
     824             :     {
     825         160 :         for (i = 0; i < parsed->nsubxacts; i++)
     826             :         {
     827           8 :             ReorderBufferAbort(ctx->reorder, parsed->subxacts[i],
     828           8 :                                buf->record->EndRecPtr);
     829             :         }
     830             : 
     831         152 :         ReorderBufferAbort(ctx->reorder, xid, buf->record->EndRecPtr);
     832             : 
     833             :     }
     834             :     else
     835             :     {
     836             :         /*
     837             :          * ROLLBACK PREPARED of a previously prepared txn, need to call the
     838             :          * callbacks.
     839             :          */
     840          24 :         ReorderBufferFinishPrepared(ctx->reorder, xid, buf->origptr, buf->endptr,
     841             :                                     commit_time, origin_id, origin_lsn,
     842          24 :                                     parsed->twophase_gid, false);
     843             :     }
     844             : 
     845             :     /* update the decoding stats */
     846         176 :     UpdateDecodingStats(ctx);
     847         176 : }
     848             : 
     849             : /*
     850             :  * Parse XLOG_HEAP_INSERT (not MULTI_INSERT!) records into tuplebufs.
     851             :  *
     852             :  * Deletes can contain the new tuple.
     853             :  */
     854             : static void
     855     2230384 : DecodeInsert(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
     856             : {
     857             :     Size        datalen;
     858             :     char       *tupledata;
     859             :     Size        tuplelen;
     860     2230384 :     XLogReaderState *r = buf->record;
     861             :     xl_heap_insert *xlrec;
     862             :     ReorderBufferChange *change;
     863             :     RelFileNode target_node;
     864             : 
     865     2230384 :     xlrec = (xl_heap_insert *) XLogRecGetData(r);
     866             : 
     867             :     /*
     868             :      * Ignore insert records without new tuples (this does happen when
     869             :      * raw_heap_insert marks the TOAST record as HEAP_INSERT_NO_LOGICAL).
     870             :      */
     871     2230384 :     if (!(xlrec->flags & XLH_INSERT_CONTAINS_NEW_TUPLE))
     872        9176 :         return;
     873             : 
     874             :     /* only interested in our database */
     875     2225802 :     XLogRecGetBlockTag(r, 0, &target_node, NULL, NULL);
     876     2225802 :     if (target_node.dbNode != ctx->slot->data.database)
     877           0 :         return;
     878             : 
     879             :     /* output plugin doesn't look for this origin, no need to queue */
     880     2225802 :     if (FilterByOrigin(ctx, XLogRecGetOrigin(r)))
     881          12 :         return;
     882             : 
     883     2225790 :     change = ReorderBufferGetChange(ctx->reorder);
     884     2225790 :     if (!(xlrec->flags & XLH_INSERT_IS_SPECULATIVE))
     885     2189990 :         change->action = REORDER_BUFFER_CHANGE_INSERT;
     886             :     else
     887       35800 :         change->action = REORDER_BUFFER_CHANGE_INTERNAL_SPEC_INSERT;
     888     2225790 :     change->origin_id = XLogRecGetOrigin(r);
     889             : 
     890     2225790 :     memcpy(&change->data.tp.relnode, &target_node, sizeof(RelFileNode));
     891             : 
     892     2225790 :     tupledata = XLogRecGetBlockData(r, 0, &datalen);
     893     2225790 :     tuplelen = datalen - SizeOfHeapHeader;
     894             : 
     895     2225790 :     change->data.tp.newtuple =
     896     2225790 :         ReorderBufferGetTupleBuf(ctx->reorder, tuplelen);
     897             : 
     898     2225790 :     DecodeXLogTuple(tupledata, datalen, change->data.tp.newtuple);
     899             : 
     900     2225790 :     change->data.tp.clear_toast_afterwards = true;
     901             : 
     902     2225790 :     ReorderBufferQueueChange(ctx->reorder, XLogRecGetXid(r), buf->origptr,
     903             :                              change,
     904     2225790 :                              xlrec->flags & XLH_INSERT_ON_TOAST_RELATION);
     905             : }
     906             : 
     907             : /*
     908             :  * Parse XLOG_HEAP_UPDATE and XLOG_HEAP_HOT_UPDATE, which have the same layout
     909             :  * in the record, from wal into proper tuplebufs.
     910             :  *
     911             :  * Updates can possibly contain a new tuple and the old primary key.
     912             :  */
     913             : static void
     914      317740 : DecodeUpdate(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
     915             : {
     916      317740 :     XLogReaderState *r = buf->record;
     917             :     xl_heap_update *xlrec;
     918             :     ReorderBufferChange *change;
     919             :     char       *data;
     920             :     RelFileNode target_node;
     921             : 
     922      317740 :     xlrec = (xl_heap_update *) XLogRecGetData(r);
     923             : 
     924             :     /* only interested in our database */
     925      317740 :     XLogRecGetBlockTag(r, 0, &target_node, NULL, NULL);
     926      317740 :     if (target_node.dbNode != ctx->slot->data.database)
     927           0 :         return;
     928             : 
     929             :     /* output plugin doesn't look for this origin, no need to queue */
     930      317740 :     if (FilterByOrigin(ctx, XLogRecGetOrigin(r)))
     931           0 :         return;
     932             : 
     933      317740 :     change = ReorderBufferGetChange(ctx->reorder);
     934      317740 :     change->action = REORDER_BUFFER_CHANGE_UPDATE;
     935      317740 :     change->origin_id = XLogRecGetOrigin(r);
     936      317740 :     memcpy(&change->data.tp.relnode, &target_node, sizeof(RelFileNode));
     937             : 
     938      317740 :     if (xlrec->flags & XLH_UPDATE_CONTAINS_NEW_TUPLE)
     939             :     {
     940             :         Size        datalen;
     941             :         Size        tuplelen;
     942             : 
     943      315316 :         data = XLogRecGetBlockData(r, 0, &datalen);
     944             : 
     945      315316 :         tuplelen = datalen - SizeOfHeapHeader;
     946             : 
     947      315316 :         change->data.tp.newtuple =
     948      315316 :             ReorderBufferGetTupleBuf(ctx->reorder, tuplelen);
     949             : 
     950      315316 :         DecodeXLogTuple(data, datalen, change->data.tp.newtuple);
     951             :     }
     952             : 
     953      317740 :     if (xlrec->flags & XLH_UPDATE_CONTAINS_OLD)
     954             :     {
     955             :         Size        datalen;
     956             :         Size        tuplelen;
     957             : 
     958             :         /* caution, remaining data in record is not aligned */
     959         784 :         data = XLogRecGetData(r) + SizeOfHeapUpdate;
     960         784 :         datalen = XLogRecGetDataLen(r) - SizeOfHeapUpdate;
     961         784 :         tuplelen = datalen - SizeOfHeapHeader;
     962             : 
     963         784 :         change->data.tp.oldtuple =
     964         784 :             ReorderBufferGetTupleBuf(ctx->reorder, tuplelen);
     965             : 
     966         784 :         DecodeXLogTuple(data, datalen, change->data.tp.oldtuple);
     967             :     }
     968             : 
     969      317740 :     change->data.tp.clear_toast_afterwards = true;
     970             : 
     971      317740 :     ReorderBufferQueueChange(ctx->reorder, XLogRecGetXid(r), buf->origptr,
     972             :                              change, false);
     973             : }
     974             : 
     975             : /*
     976             :  * Parse XLOG_HEAP_DELETE from wal into proper tuplebufs.
     977             :  *
     978             :  * Deletes can possibly contain the old primary key.
     979             :  */
     980             : static void
     981      393744 : DecodeDelete(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
     982             : {
     983      393744 :     XLogReaderState *r = buf->record;
     984             :     xl_heap_delete *xlrec;
     985             :     ReorderBufferChange *change;
     986             :     RelFileNode target_node;
     987             : 
     988      393744 :     xlrec = (xl_heap_delete *) XLogRecGetData(r);
     989             : 
     990             :     /* only interested in our database */
     991      393744 :     XLogRecGetBlockTag(r, 0, &target_node, NULL, NULL);
     992      393744 :     if (target_node.dbNode != ctx->slot->data.database)
     993           0 :         return;
     994             : 
     995             :     /*
     996             :      * Super deletions are irrelevant for logical decoding, it's driven by the
     997             :      * confirmation records.
     998             :      */
     999      393744 :     if (xlrec->flags & XLH_DELETE_IS_SUPER)
    1000           0 :         return;
    1001             : 
    1002             :     /* output plugin doesn't look for this origin, no need to queue */
    1003      393744 :     if (FilterByOrigin(ctx, XLogRecGetOrigin(r)))
    1004           0 :         return;
    1005             : 
    1006      393744 :     change = ReorderBufferGetChange(ctx->reorder);
    1007      393744 :     change->action = REORDER_BUFFER_CHANGE_DELETE;
    1008      393744 :     change->origin_id = XLogRecGetOrigin(r);
    1009             : 
    1010      393744 :     memcpy(&change->data.tp.relnode, &target_node, sizeof(RelFileNode));
    1011             : 
    1012             :     /* old primary key stored */
    1013      393744 :     if (xlrec->flags & XLH_DELETE_CONTAINS_OLD)
    1014             :     {
    1015      267178 :         Size        datalen = XLogRecGetDataLen(r) - SizeOfHeapDelete;
    1016      267178 :         Size        tuplelen = datalen - SizeOfHeapHeader;
    1017             : 
    1018      267178 :         Assert(XLogRecGetDataLen(r) > (SizeOfHeapDelete + SizeOfHeapHeader));
    1019             : 
    1020      267178 :         change->data.tp.oldtuple =
    1021      267178 :             ReorderBufferGetTupleBuf(ctx->reorder, tuplelen);
    1022             : 
    1023      267178 :         DecodeXLogTuple((char *) xlrec + SizeOfHeapDelete,
    1024             :                         datalen, change->data.tp.oldtuple);
    1025             :     }
    1026             : 
    1027      393744 :     change->data.tp.clear_toast_afterwards = true;
    1028             : 
    1029      393744 :     ReorderBufferQueueChange(ctx->reorder, XLogRecGetXid(r), buf->origptr,
    1030             :                              change, false);
    1031             : }
    1032             : 
    1033             : /*
    1034             :  * Parse XLOG_HEAP_TRUNCATE from wal
    1035             :  */
    1036             : static void
    1037          50 : DecodeTruncate(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
    1038             : {
    1039          50 :     XLogReaderState *r = buf->record;
    1040             :     xl_heap_truncate *xlrec;
    1041             :     ReorderBufferChange *change;
    1042             : 
    1043          50 :     xlrec = (xl_heap_truncate *) XLogRecGetData(r);
    1044             : 
    1045             :     /* only interested in our database */
    1046          50 :     if (xlrec->dbId != ctx->slot->data.database)
    1047           0 :         return;
    1048             : 
    1049             :     /* output plugin doesn't look for this origin, no need to queue */
    1050          50 :     if (FilterByOrigin(ctx, XLogRecGetOrigin(r)))
    1051           0 :         return;
    1052             : 
    1053          50 :     change = ReorderBufferGetChange(ctx->reorder);
    1054          50 :     change->action = REORDER_BUFFER_CHANGE_TRUNCATE;
    1055          50 :     change->origin_id = XLogRecGetOrigin(r);
    1056          50 :     if (xlrec->flags & XLH_TRUNCATE_CASCADE)
    1057           2 :         change->data.truncate.cascade = true;
    1058          50 :     if (xlrec->flags & XLH_TRUNCATE_RESTART_SEQS)
    1059           4 :         change->data.truncate.restart_seqs = true;
    1060          50 :     change->data.truncate.nrelids = xlrec->nrelids;
    1061          50 :     change->data.truncate.relids = ReorderBufferGetRelids(ctx->reorder,
    1062          50 :                                                           xlrec->nrelids);
    1063          50 :     memcpy(change->data.truncate.relids, xlrec->relids,
    1064          50 :            xlrec->nrelids * sizeof(Oid));
    1065          50 :     ReorderBufferQueueChange(ctx->reorder, XLogRecGetXid(r),
    1066             :                              buf->origptr, change, false);
    1067             : }
    1068             : 
    1069             : /*
    1070             :  * Decode XLOG_HEAP2_MULTI_INSERT_insert record into multiple tuplebufs.
    1071             :  *
    1072             :  * Currently MULTI_INSERT will always contain the full tuples.
    1073             :  */
    1074             : static void
    1075        7782 : DecodeMultiInsert(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
    1076             : {
    1077        7782 :     XLogReaderState *r = buf->record;
    1078             :     xl_heap_multi_insert *xlrec;
    1079             :     int         i;
    1080             :     char       *data;
    1081             :     char       *tupledata;
    1082             :     Size        tuplelen;
    1083             :     RelFileNode rnode;
    1084             : 
    1085        7782 :     xlrec = (xl_heap_multi_insert *) XLogRecGetData(r);
    1086             : 
    1087             :     /*
    1088             :      * Ignore insert records without new tuples.  This happens when a
    1089             :      * multi_insert is done on a catalog or on a non-persistent relation.
    1090             :      */
    1091        7782 :     if (!(xlrec->flags & XLH_INSERT_CONTAINS_NEW_TUPLE))
    1092       15430 :         return;
    1093             : 
    1094             :     /* only interested in our database */
    1095         118 :     XLogRecGetBlockTag(r, 0, &rnode, NULL, NULL);
    1096         118 :     if (rnode.dbNode != ctx->slot->data.database)
    1097         102 :         return;
    1098             : 
    1099             :     /* output plugin doesn't look for this origin, no need to queue */
    1100          16 :     if (FilterByOrigin(ctx, XLogRecGetOrigin(r)))
    1101           0 :         return;
    1102             : 
    1103             :     /*
    1104             :      * We know that this multi_insert isn't for a catalog, so the block should
    1105             :      * always have data even if a full-page write of it is taken.
    1106             :      */
    1107          16 :     tupledata = XLogRecGetBlockData(r, 0, &tuplelen);
    1108          16 :     Assert(tupledata != NULL);
    1109             : 
    1110          16 :     data = tupledata;
    1111        1640 :     for (i = 0; i < xlrec->ntuples; i++)
    1112             :     {
    1113             :         ReorderBufferChange *change;
    1114             :         xl_multi_insert_tuple *xlhdr;
    1115             :         int         datalen;
    1116             :         ReorderBufferTupleBuf *tuple;
    1117             :         HeapTupleHeader header;
    1118             : 
    1119        1624 :         change = ReorderBufferGetChange(ctx->reorder);
    1120        1624 :         change->action = REORDER_BUFFER_CHANGE_INSERT;
    1121        1624 :         change->origin_id = XLogRecGetOrigin(r);
    1122             : 
    1123        1624 :         memcpy(&change->data.tp.relnode, &rnode, sizeof(RelFileNode));
    1124             : 
    1125        1624 :         xlhdr = (xl_multi_insert_tuple *) SHORTALIGN(data);
    1126        1624 :         data = ((char *) xlhdr) + SizeOfMultiInsertTuple;
    1127        1624 :         datalen = xlhdr->datalen;
    1128             : 
    1129        1624 :         change->data.tp.newtuple =
    1130        1624 :             ReorderBufferGetTupleBuf(ctx->reorder, datalen);
    1131             : 
    1132        1624 :         tuple = change->data.tp.newtuple;
    1133        1624 :         header = tuple->tuple.t_data;
    1134             : 
    1135             :         /* not a disk based tuple */
    1136        1624 :         ItemPointerSetInvalid(&tuple->tuple.t_self);
    1137             : 
    1138             :         /*
    1139             :          * We can only figure this out after reassembling the transactions.
    1140             :          */
    1141        1624 :         tuple->tuple.t_tableOid = InvalidOid;
    1142             : 
    1143        1624 :         tuple->tuple.t_len = datalen + SizeofHeapTupleHeader;
    1144             : 
    1145        1624 :         memset(header, 0, SizeofHeapTupleHeader);
    1146             : 
    1147        1624 :         memcpy((char *) tuple->tuple.t_data + SizeofHeapTupleHeader,
    1148             :                (char *) data,
    1149             :                datalen);
    1150        1624 :         header->t_infomask = xlhdr->t_infomask;
    1151        1624 :         header->t_infomask2 = xlhdr->t_infomask2;
    1152        1624 :         header->t_hoff = xlhdr->t_hoff;
    1153             : 
    1154             :         /*
    1155             :          * Reset toast reassembly state only after the last row in the last
    1156             :          * xl_multi_insert_tuple record emitted by one heap_multi_insert()
    1157             :          * call.
    1158             :          */
    1159        1872 :         if (xlrec->flags & XLH_INSERT_LAST_IN_MULTI &&
    1160         248 :             (i + 1) == xlrec->ntuples)
    1161           8 :             change->data.tp.clear_toast_afterwards = true;
    1162             :         else
    1163        1616 :             change->data.tp.clear_toast_afterwards = false;
    1164             : 
    1165        1624 :         ReorderBufferQueueChange(ctx->reorder, XLogRecGetXid(r),
    1166             :                                  buf->origptr, change, false);
    1167             : 
    1168             :         /* move to the next xl_multi_insert_tuple entry */
    1169        1624 :         data += datalen;
    1170             :     }
    1171          16 :     Assert(data == tupledata + tuplelen);
    1172             : }
    1173             : 
    1174             : /*
    1175             :  * Parse XLOG_HEAP_CONFIRM from wal into a confirmation change.
    1176             :  *
    1177             :  * This is pretty trivial, all the state essentially already setup by the
    1178             :  * speculative insertion.
    1179             :  */
    1180             : static void
    1181       35800 : DecodeSpecConfirm(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
    1182             : {
    1183       35800 :     XLogReaderState *r = buf->record;
    1184             :     ReorderBufferChange *change;
    1185             :     RelFileNode target_node;
    1186             : 
    1187             :     /* only interested in our database */
    1188       35800 :     XLogRecGetBlockTag(r, 0, &target_node, NULL, NULL);
    1189       35800 :     if (target_node.dbNode != ctx->slot->data.database)
    1190           0 :         return;
    1191             : 
    1192             :     /* output plugin doesn't look for this origin, no need to queue */
    1193       35800 :     if (FilterByOrigin(ctx, XLogRecGetOrigin(r)))
    1194           0 :         return;
    1195             : 
    1196       35800 :     change = ReorderBufferGetChange(ctx->reorder);
    1197       35800 :     change->action = REORDER_BUFFER_CHANGE_INTERNAL_SPEC_CONFIRM;
    1198       35800 :     change->origin_id = XLogRecGetOrigin(r);
    1199             : 
    1200       35800 :     memcpy(&change->data.tp.relnode, &target_node, sizeof(RelFileNode));
    1201             : 
    1202       35800 :     change->data.tp.clear_toast_afterwards = true;
    1203             : 
    1204       35800 :     ReorderBufferQueueChange(ctx->reorder, XLogRecGetXid(r), buf->origptr,
    1205             :                              change, false);
    1206             : }
    1207             : 
    1208             : 
    1209             : /*
    1210             :  * Read a HeapTuple as WAL logged by heap_insert, heap_update and heap_delete
    1211             :  * (but not by heap_multi_insert) into a tuplebuf.
    1212             :  *
    1213             :  * The size 'len' and the pointer 'data' in the record need to be
    1214             :  * computed outside as they are record specific.
    1215             :  */
    1216             : static void
    1217     2809068 : DecodeXLogTuple(char *data, Size len, ReorderBufferTupleBuf *tuple)
    1218             : {
    1219             :     xl_heap_header xlhdr;
    1220     2809068 :     int         datalen = len - SizeOfHeapHeader;
    1221             :     HeapTupleHeader header;
    1222             : 
    1223     2809068 :     Assert(datalen >= 0);
    1224             : 
    1225     2809068 :     tuple->tuple.t_len = datalen + SizeofHeapTupleHeader;
    1226     2809068 :     header = tuple->tuple.t_data;
    1227             : 
    1228             :     /* not a disk based tuple */
    1229     2809068 :     ItemPointerSetInvalid(&tuple->tuple.t_self);
    1230             : 
    1231             :     /* we can only figure this out after reassembling the transactions */
    1232     2809068 :     tuple->tuple.t_tableOid = InvalidOid;
    1233             : 
    1234             :     /* data is not stored aligned, copy to aligned storage */
    1235     2809068 :     memcpy((char *) &xlhdr,
    1236             :            data,
    1237             :            SizeOfHeapHeader);
    1238             : 
    1239     2809068 :     memset(header, 0, SizeofHeapTupleHeader);
    1240             : 
    1241     5618136 :     memcpy(((char *) tuple->tuple.t_data) + SizeofHeapTupleHeader,
    1242     2809068 :            data + SizeOfHeapHeader,
    1243             :            datalen);
    1244             : 
    1245     2809068 :     header->t_infomask = xlhdr.t_infomask;
    1246     2809068 :     header->t_infomask2 = xlhdr.t_infomask2;
    1247     2809068 :     header->t_hoff = xlhdr.t_hoff;
    1248     2809068 : }

Generated by: LCOV version 1.14