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

Generated by: LCOV version 1.14