LCOV - code coverage report
Current view: top level - src/backend/replication/logical - proto.c (source / functions) Hit Total Coverage
Test: PostgreSQL 14devel Lines: 354 379 93.4 %
Date: 2020-11-10 11:26:58 Functions: 33 35 94.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * proto.c
       4             :  *      logical replication protocol functions
       5             :  *
       6             :  * Copyright (c) 2015-2020, PostgreSQL Global Development Group
       7             :  *
       8             :  * IDENTIFICATION
       9             :  *      src/backend/replication/logical/proto.c
      10             :  *
      11             :  *-------------------------------------------------------------------------
      12             :  */
      13             : #include "postgres.h"
      14             : 
      15             : #include "access/sysattr.h"
      16             : #include "catalog/pg_namespace.h"
      17             : #include "catalog/pg_type.h"
      18             : #include "libpq/pqformat.h"
      19             : #include "replication/logicalproto.h"
      20             : #include "utils/lsyscache.h"
      21             : #include "utils/syscache.h"
      22             : 
      23             : /*
      24             :  * Protocol message flags.
      25             :  */
      26             : #define LOGICALREP_IS_REPLICA_IDENTITY 1
      27             : 
      28             : #define TRUNCATE_CASCADE        (1<<0)
      29             : #define TRUNCATE_RESTART_SEQS   (1<<1)
      30             : 
      31             : static void logicalrep_write_attrs(StringInfo out, Relation rel);
      32             : static void logicalrep_write_tuple(StringInfo out, Relation rel,
      33             :                                    HeapTuple tuple, bool binary);
      34             : 
      35             : static void logicalrep_read_attrs(StringInfo in, LogicalRepRelation *rel);
      36             : static void logicalrep_read_tuple(StringInfo in, LogicalRepTupleData *tuple);
      37             : 
      38             : static void logicalrep_write_namespace(StringInfo out, Oid nspid);
      39             : static const char *logicalrep_read_namespace(StringInfo in);
      40             : 
      41             : /*
      42             :  * Write BEGIN to the output stream.
      43             :  */
      44             : void
      45         356 : logicalrep_write_begin(StringInfo out, ReorderBufferTXN *txn)
      46             : {
      47         356 :     pq_sendbyte(out, LOGICAL_REP_MSG_BEGIN);
      48             : 
      49             :     /* fixed fields */
      50         356 :     pq_sendint64(out, txn->final_lsn);
      51         356 :     pq_sendint64(out, txn->commit_time);
      52         356 :     pq_sendint32(out, txn->xid);
      53         356 : }
      54             : 
      55             : /*
      56             :  * Read transaction BEGIN from the stream.
      57             :  */
      58             : void
      59         500 : logicalrep_read_begin(StringInfo in, LogicalRepBeginData *begin_data)
      60             : {
      61             :     /* read fields */
      62         500 :     begin_data->final_lsn = pq_getmsgint64(in);
      63         500 :     if (begin_data->final_lsn == InvalidXLogRecPtr)
      64           0 :         elog(ERROR, "final_lsn not set in begin message");
      65         500 :     begin_data->committime = pq_getmsgint64(in);
      66         500 :     begin_data->xid = pq_getmsgint(in, 4);
      67         500 : }
      68             : 
      69             : 
      70             : /*
      71             :  * Write COMMIT to the output stream.
      72             :  */
      73             : void
      74         342 : logicalrep_write_commit(StringInfo out, ReorderBufferTXN *txn,
      75             :                         XLogRecPtr commit_lsn)
      76             : {
      77         342 :     uint8       flags = 0;
      78             : 
      79         342 :     pq_sendbyte(out, LOGICAL_REP_MSG_COMMIT);
      80             : 
      81             :     /* send the flags field */
      82         342 :     pq_sendbyte(out, flags);
      83             : 
      84             :     /* send fields */
      85         342 :     pq_sendint64(out, commit_lsn);
      86         342 :     pq_sendint64(out, txn->end_lsn);
      87         342 :     pq_sendint64(out, txn->commit_time);
      88         342 : }
      89             : 
      90             : /*
      91             :  * Read transaction COMMIT from the stream.
      92             :  */
      93             : void
      94         488 : logicalrep_read_commit(StringInfo in, LogicalRepCommitData *commit_data)
      95             : {
      96             :     /* read flags (unused for now) */
      97         488 :     uint8       flags = pq_getmsgbyte(in);
      98             : 
      99         488 :     if (flags != 0)
     100           0 :         elog(ERROR, "unrecognized flags %u in commit message", flags);
     101             : 
     102             :     /* read fields */
     103         488 :     commit_data->commit_lsn = pq_getmsgint64(in);
     104         488 :     commit_data->end_lsn = pq_getmsgint64(in);
     105         488 :     commit_data->committime = pq_getmsgint64(in);
     106         488 : }
     107             : 
     108             : /*
     109             :  * Write PREPARE to the output stream.
     110             :  */
     111             : void
     112          50 : logicalrep_write_prepare(StringInfo out, ReorderBufferTXN *txn,
     113             :                          XLogRecPtr prepare_lsn)
     114             : {
     115          50 :     uint8       flags = 0;
     116             : 
     117          50 :     pq_sendbyte(out, LOGICAL_REP_MSG_PREPARE);
     118             : 
     119             :     /*
     120             :      * This should only ever happen for two-phase commit transactions. In
     121             :      * which case we expect to have a valid GID.
     122             :      */
     123          50 :     Assert(rbtxn_prepared(txn));
     124          50 :     Assert(txn->gid != NULL);
     125             : 
     126             :     /*
     127             :      * Flags are determined from the state of the transaction. We know we
     128             :      * always get PREPARE first and then [COMMIT|ROLLBACK] PREPARED, so if
     129             :      * it's already marked as committed then it has to be COMMIT PREPARED (and
     130             :      * likewise for abort / ROLLBACK PREPARED).
     131             :      */
     132          50 :     if (rbtxn_commit_prepared(txn))
     133          20 :         flags = LOGICALREP_IS_COMMIT_PREPARED;
     134          30 :     else if (rbtxn_rollback_prepared(txn))
     135          16 :         flags = LOGICALREP_IS_ROLLBACK_PREPARED;
     136             :     else
     137          14 :         flags = LOGICALREP_IS_PREPARE;
     138             : 
     139             :     /* send the flags field */
     140          50 :     pq_sendbyte(out, flags);
     141             : 
     142             :     /* send fields */
     143          50 :     pq_sendint64(out, prepare_lsn);
     144          50 :     pq_sendint64(out, txn->end_lsn);
     145          50 :     pq_sendint64(out, txn->commit_time);
     146             : 
     147             :     /* send gid */
     148          50 :     pq_sendstring(out, txn->gid);
     149          50 : }
     150             : 
     151             : /*
     152             :  * Read transaction PREPARE from the stream.
     153             :  */
     154             : void
     155          26 : logicalrep_read_prepare(StringInfo in, LogicalRepPrepareData *prepare_data)
     156             : {
     157             :     /* read flags */
     158          26 :     uint8       flags = pq_getmsgbyte(in);
     159             : 
     160          26 :     if (!PrepareFlagsAreValid(flags))
     161           0 :         elog(ERROR, "unrecognized flags %u in prepare message", flags);
     162             : 
     163             :     /* set the action (reuse the constants used for the flags) */
     164          26 :     prepare_data->prepare_type = flags;
     165             : 
     166             :     /* read fields */
     167          26 :     prepare_data->prepare_lsn = pq_getmsgint64(in);
     168          26 :     prepare_data->end_lsn = pq_getmsgint64(in);
     169          26 :     prepare_data->preparetime = pq_getmsgint64(in);
     170             : 
     171             :     /* read gid (copy it into a pre-allocated buffer) */
     172          26 :     strcpy(prepare_data->gid, pq_getmsgstring(in));
     173          26 : }
     174             : 
     175             : /*
     176             :  * Write STREAM PREPARE to the output stream.
     177             :  */
     178             : void
     179          18 : logicalrep_write_stream_prepare(StringInfo out,
     180             :                                 ReorderBufferTXN *txn,
     181             :                                 XLogRecPtr prepare_lsn)
     182             : {
     183          18 :     uint8       flags = 0;
     184             : 
     185          18 :     pq_sendbyte(out, LOGICAL_REP_MSG_STREAM_PREPARE);
     186             : 
     187             :     /*
     188             :      * This should only ever happen for two-phase transactions. In which case
     189             :      * we expect to have a valid GID.
     190             :      */
     191          18 :     Assert(rbtxn_prepared(txn));
     192          18 :     Assert(txn->gid != NULL);
     193             : 
     194             :     /*
     195             :      * For streaming APIs only PREPARE is supported. [COMMIT|ROLLBACK]
     196             :      * PREPARED uses non-streaming APIs
     197             :      */
     198          18 :     flags = LOGICALREP_IS_PREPARE;
     199             : 
     200             :     /* transaction ID */
     201          18 :     Assert(TransactionIdIsValid(txn->xid));
     202          18 :     pq_sendint32(out, txn->xid);
     203             : 
     204             :     /* send the flags field */
     205          18 :     pq_sendbyte(out, flags);
     206             : 
     207             :     /* send fields */
     208          18 :     pq_sendint64(out, prepare_lsn);
     209          18 :     pq_sendint64(out, txn->end_lsn);
     210          18 :     pq_sendint64(out, txn->commit_time);
     211             : 
     212             :     /* send gid */
     213          18 :     pq_sendstring(out, txn->gid);
     214          18 : }
     215             : 
     216             : /*
     217             :  * Read STREAM PREPARE from the output stream.
     218             :  */
     219             : TransactionId
     220          12 : logicalrep_read_stream_prepare(StringInfo in, LogicalRepPrepareData *prepare_data)
     221             : {
     222             :     TransactionId xid;
     223             :     uint8       flags;
     224             : 
     225          12 :     xid = pq_getmsgint(in, 4);
     226             : 
     227             :     /* read flags */
     228          12 :     flags = pq_getmsgbyte(in);
     229             : 
     230          12 :     if (flags != LOGICALREP_IS_PREPARE)
     231           0 :         elog(ERROR, "unrecognized flags %u in prepare message", flags);
     232             : 
     233             :     /* set the action (reuse the constants used for the flags) */
     234          12 :     prepare_data->prepare_type = flags;
     235             : 
     236             :     /* read fields */
     237          12 :     prepare_data->prepare_lsn = pq_getmsgint64(in);
     238          12 :     prepare_data->end_lsn = pq_getmsgint64(in);
     239          12 :     prepare_data->preparetime = pq_getmsgint64(in);
     240             : 
     241             :     /* read gid (copy it into a pre-allocated buffer) */
     242          12 :     strcpy(prepare_data->gid, pq_getmsgstring(in));
     243             : 
     244          12 :     return xid;
     245             : }
     246             : 
     247             : /*
     248             :  * Write ORIGIN to the output stream.
     249             :  */
     250             : void
     251           0 : logicalrep_write_origin(StringInfo out, const char *origin,
     252             :                         XLogRecPtr origin_lsn)
     253             : {
     254           0 :     pq_sendbyte(out, LOGICAL_REP_MSG_ORIGIN);
     255             : 
     256             :     /* fixed fields */
     257           0 :     pq_sendint64(out, origin_lsn);
     258             : 
     259             :     /* origin string */
     260           0 :     pq_sendstring(out, origin);
     261           0 : }
     262             : 
     263             : /*
     264             :  * Read ORIGIN from the output stream.
     265             :  */
     266             : char *
     267           0 : logicalrep_read_origin(StringInfo in, XLogRecPtr *origin_lsn)
     268             : {
     269             :     /* fixed fields */
     270           0 :     *origin_lsn = pq_getmsgint64(in);
     271             : 
     272             :     /* return origin */
     273           0 :     return pstrdup(pq_getmsgstring(in));
     274             : }
     275             : 
     276             : /*
     277             :  * Write INSERT to the output stream.
     278             :  */
     279             : void
     280      162716 : logicalrep_write_insert(StringInfo out, TransactionId xid, Relation rel,
     281             :                         HeapTuple newtuple, bool binary)
     282             : {
     283      162716 :     pq_sendbyte(out, LOGICAL_REP_MSG_INSERT);
     284             : 
     285             :     /* transaction ID (if not valid, we're not streaming) */
     286      162716 :     if (TransactionIdIsValid(xid))
     287      161872 :         pq_sendint32(out, xid);
     288             : 
     289             :     /* use Oid as relation identifier */
     290      162716 :     pq_sendint32(out, RelationGetRelid(rel));
     291             : 
     292      162716 :     pq_sendbyte(out, 'N');      /* new tuple follows */
     293      162716 :     logicalrep_write_tuple(out, rel, newtuple, binary);
     294      162716 : }
     295             : 
     296             : /*
     297             :  * Read INSERT from stream.
     298             :  *
     299             :  * Fills the new tuple.
     300             :  */
     301             : LogicalRepRelId
     302      102854 : logicalrep_read_insert(StringInfo in, LogicalRepTupleData *newtup)
     303             : {
     304             :     char        action;
     305             :     LogicalRepRelId relid;
     306             : 
     307             :     /* read the relation id */
     308      102854 :     relid = pq_getmsgint(in, 4);
     309             : 
     310      102854 :     action = pq_getmsgbyte(in);
     311      102854 :     if (action != 'N')
     312           0 :         elog(ERROR, "expected new tuple but got %d",
     313             :              action);
     314             : 
     315      102854 :     logicalrep_read_tuple(in, newtup);
     316             : 
     317      102854 :     return relid;
     318             : }
     319             : 
     320             : /*
     321             :  * Write UPDATE to the output stream.
     322             :  */
     323             : void
     324       83104 : logicalrep_write_update(StringInfo out, TransactionId xid, Relation rel,
     325             :                         HeapTuple oldtuple, HeapTuple newtuple, bool binary)
     326             : {
     327       83104 :     pq_sendbyte(out, LOGICAL_REP_MSG_UPDATE);
     328             : 
     329       83104 :     Assert(rel->rd_rel->relreplident == REPLICA_IDENTITY_DEFAULT ||
     330             :            rel->rd_rel->relreplident == REPLICA_IDENTITY_FULL ||
     331             :            rel->rd_rel->relreplident == REPLICA_IDENTITY_INDEX);
     332             : 
     333             :     /* transaction ID (if not valid, we're not streaming) */
     334       83104 :     if (TransactionIdIsValid(xid))
     335       82856 :         pq_sendint32(out, xid);
     336             : 
     337             :     /* use Oid as relation identifier */
     338       83104 :     pq_sendint32(out, RelationGetRelid(rel));
     339             : 
     340       83104 :     if (oldtuple != NULL)
     341             :     {
     342         136 :         if (rel->rd_rel->relreplident == REPLICA_IDENTITY_FULL)
     343          46 :             pq_sendbyte(out, 'O');  /* old tuple follows */
     344             :         else
     345          90 :             pq_sendbyte(out, 'K');  /* old key follows */
     346         136 :         logicalrep_write_tuple(out, rel, oldtuple, binary);
     347             :     }
     348             : 
     349       83104 :     pq_sendbyte(out, 'N');      /* new tuple follows */
     350       83104 :     logicalrep_write_tuple(out, rel, newtuple, binary);
     351       83104 : }
     352             : 
     353             : /*
     354             :  * Read UPDATE from stream.
     355             :  */
     356             : LogicalRepRelId
     357       62776 : logicalrep_read_update(StringInfo in, bool *has_oldtuple,
     358             :                        LogicalRepTupleData *oldtup,
     359             :                        LogicalRepTupleData *newtup)
     360             : {
     361             :     char        action;
     362             :     LogicalRepRelId relid;
     363             : 
     364             :     /* read the relation id */
     365       62776 :     relid = pq_getmsgint(in, 4);
     366             : 
     367             :     /* read and verify action */
     368       62776 :     action = pq_getmsgbyte(in);
     369       62776 :     if (action != 'K' && action != 'O' && action != 'N')
     370           0 :         elog(ERROR, "expected action 'N', 'O' or 'K', got %c",
     371             :              action);
     372             : 
     373             :     /* check for old tuple */
     374       62776 :     if (action == 'K' || action == 'O')
     375             :     {
     376         160 :         logicalrep_read_tuple(in, oldtup);
     377         160 :         *has_oldtuple = true;
     378             : 
     379         160 :         action = pq_getmsgbyte(in);
     380             :     }
     381             :     else
     382       62616 :         *has_oldtuple = false;
     383             : 
     384             :     /* check for new  tuple */
     385       62776 :     if (action != 'N')
     386           0 :         elog(ERROR, "expected action 'N', got %c",
     387             :              action);
     388             : 
     389       62776 :     logicalrep_read_tuple(in, newtup);
     390             : 
     391       62776 :     return relid;
     392             : }
     393             : 
     394             : /*
     395             :  * Write DELETE to the output stream.
     396             :  */
     397             : void
     398       72498 : logicalrep_write_delete(StringInfo out, TransactionId xid, Relation rel,
     399             :                         HeapTuple oldtuple, bool binary)
     400             : {
     401       72498 :     Assert(rel->rd_rel->relreplident == REPLICA_IDENTITY_DEFAULT ||
     402             :            rel->rd_rel->relreplident == REPLICA_IDENTITY_FULL ||
     403             :            rel->rd_rel->relreplident == REPLICA_IDENTITY_INDEX);
     404             : 
     405       72498 :     pq_sendbyte(out, LOGICAL_REP_MSG_DELETE);
     406             : 
     407             :     /* transaction ID (if not valid, we're not streaming) */
     408       72498 :     if (TransactionIdIsValid(xid))
     409       72102 :         pq_sendint32(out, xid);
     410             : 
     411             :     /* use Oid as relation identifier */
     412       72498 :     pq_sendint32(out, RelationGetRelid(rel));
     413             : 
     414       72498 :     if (rel->rd_rel->relreplident == REPLICA_IDENTITY_FULL)
     415         200 :         pq_sendbyte(out, 'O');  /* old tuple follows */
     416             :     else
     417       72298 :         pq_sendbyte(out, 'K');  /* old key follows */
     418             : 
     419       72498 :     logicalrep_write_tuple(out, rel, oldtuple, binary);
     420       72498 : }
     421             : 
     422             : /*
     423             :  * Read DELETE from stream.
     424             :  *
     425             :  * Fills the old tuple.
     426             :  */
     427             : LogicalRepRelId
     428       62100 : logicalrep_read_delete(StringInfo in, LogicalRepTupleData *oldtup)
     429             : {
     430             :     char        action;
     431             :     LogicalRepRelId relid;
     432             : 
     433             :     /* read the relation id */
     434       62100 :     relid = pq_getmsgint(in, 4);
     435             : 
     436             :     /* read and verify action */
     437       62100 :     action = pq_getmsgbyte(in);
     438       62100 :     if (action != 'K' && action != 'O')
     439           0 :         elog(ERROR, "expected action 'O' or 'K', got %c", action);
     440             : 
     441       62100 :     logicalrep_read_tuple(in, oldtup);
     442             : 
     443       62100 :     return relid;
     444             : }
     445             : 
     446             : /*
     447             :  * Write TRUNCATE to the output stream.
     448             :  */
     449             : void
     450           6 : logicalrep_write_truncate(StringInfo out,
     451             :                           TransactionId xid,
     452             :                           int nrelids,
     453             :                           Oid relids[],
     454             :                           bool cascade, bool restart_seqs)
     455             : {
     456             :     int         i;
     457           6 :     uint8       flags = 0;
     458             : 
     459           6 :     pq_sendbyte(out, LOGICAL_REP_MSG_TRUNCATE);
     460             : 
     461             :     /* transaction ID (if not valid, we're not streaming) */
     462           6 :     if (TransactionIdIsValid(xid))
     463           0 :         pq_sendint32(out, xid);
     464             : 
     465           6 :     pq_sendint32(out, nrelids);
     466             : 
     467             :     /* encode and send truncate flags */
     468           6 :     if (cascade)
     469           0 :         flags |= TRUNCATE_CASCADE;
     470           6 :     if (restart_seqs)
     471           0 :         flags |= TRUNCATE_RESTART_SEQS;
     472           6 :     pq_sendint8(out, flags);
     473             : 
     474          16 :     for (i = 0; i < nrelids; i++)
     475          10 :         pq_sendint32(out, relids[i]);
     476           6 : }
     477             : 
     478             : /*
     479             :  * Read TRUNCATE from stream.
     480             :  */
     481             : List *
     482          24 : logicalrep_read_truncate(StringInfo in,
     483             :                          bool *cascade, bool *restart_seqs)
     484             : {
     485             :     int         i;
     486             :     int         nrelids;
     487          24 :     List       *relids = NIL;
     488             :     uint8       flags;
     489             : 
     490          24 :     nrelids = pq_getmsgint(in, 4);
     491             : 
     492             :     /* read and decode truncate flags */
     493          24 :     flags = pq_getmsgint(in, 1);
     494          24 :     *cascade = (flags & TRUNCATE_CASCADE) > 0;
     495          24 :     *restart_seqs = (flags & TRUNCATE_RESTART_SEQS) > 0;
     496             : 
     497          64 :     for (i = 0; i < nrelids; i++)
     498          40 :         relids = lappend_oid(relids, pq_getmsgint(in, 4));
     499             : 
     500          24 :     return relids;
     501             : }
     502             : 
     503             : /*
     504             :  * Write relation description to the output stream.
     505             :  */
     506             : void
     507         196 : logicalrep_write_rel(StringInfo out, TransactionId xid, Relation rel)
     508             : {
     509             :     char       *relname;
     510             : 
     511         196 :     pq_sendbyte(out, LOGICAL_REP_MSG_RELATION);
     512             : 
     513             :     /* transaction ID (if not valid, we're not streaming) */
     514         196 :     if (TransactionIdIsValid(xid))
     515          68 :         pq_sendint32(out, xid);
     516             : 
     517             :     /* use Oid as relation identifier */
     518         196 :     pq_sendint32(out, RelationGetRelid(rel));
     519             : 
     520             :     /* send qualified relation name */
     521         196 :     logicalrep_write_namespace(out, RelationGetNamespace(rel));
     522         196 :     relname = RelationGetRelationName(rel);
     523         196 :     pq_sendstring(out, relname);
     524             : 
     525             :     /* send replica identity */
     526         196 :     pq_sendbyte(out, rel->rd_rel->relreplident);
     527             : 
     528             :     /* send the attribute info */
     529         196 :     logicalrep_write_attrs(out, rel);
     530         196 : }
     531             : 
     532             : /*
     533             :  * Read the relation info from stream and return as LogicalRepRelation.
     534             :  */
     535             : LogicalRepRelation *
     536         250 : logicalrep_read_rel(StringInfo in)
     537             : {
     538         250 :     LogicalRepRelation *rel = palloc(sizeof(LogicalRepRelation));
     539             : 
     540         250 :     rel->remoteid = pq_getmsgint(in, 4);
     541             : 
     542             :     /* Read relation name from stream */
     543         250 :     rel->nspname = pstrdup(logicalrep_read_namespace(in));
     544         250 :     rel->relname = pstrdup(pq_getmsgstring(in));
     545             : 
     546             :     /* Read the replica identity. */
     547         250 :     rel->replident = pq_getmsgbyte(in);
     548             : 
     549             :     /* Get attribute description */
     550         250 :     logicalrep_read_attrs(in, rel);
     551             : 
     552         250 :     return rel;
     553             : }
     554             : 
     555             : /*
     556             :  * Write type info to the output stream.
     557             :  *
     558             :  * This function will always write base type info.
     559             :  */
     560             : void
     561          32 : logicalrep_write_typ(StringInfo out, TransactionId xid, Oid typoid)
     562             : {
     563          32 :     Oid         basetypoid = getBaseType(typoid);
     564             :     HeapTuple   tup;
     565             :     Form_pg_type typtup;
     566             : 
     567          32 :     pq_sendbyte(out, LOGICAL_REP_MSG_TYPE);
     568             : 
     569             :     /* transaction ID (if not valid, we're not streaming) */
     570          32 :     if (TransactionIdIsValid(xid))
     571           0 :         pq_sendint32(out, xid);
     572             : 
     573          32 :     tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(basetypoid));
     574          32 :     if (!HeapTupleIsValid(tup))
     575           0 :         elog(ERROR, "cache lookup failed for type %u", basetypoid);
     576          32 :     typtup = (Form_pg_type) GETSTRUCT(tup);
     577             : 
     578             :     /* use Oid as relation identifier */
     579          32 :     pq_sendint32(out, typoid);
     580             : 
     581             :     /* send qualified type name */
     582          32 :     logicalrep_write_namespace(out, typtup->typnamespace);
     583          32 :     pq_sendstring(out, NameStr(typtup->typname));
     584             : 
     585          32 :     ReleaseSysCache(tup);
     586          32 : }
     587             : 
     588             : /*
     589             :  * Read type info from the output stream.
     590             :  */
     591             : void
     592          32 : logicalrep_read_typ(StringInfo in, LogicalRepTyp *ltyp)
     593             : {
     594          32 :     ltyp->remoteid = pq_getmsgint(in, 4);
     595             : 
     596             :     /* Read type name from stream */
     597          32 :     ltyp->nspname = pstrdup(logicalrep_read_namespace(in));
     598          32 :     ltyp->typname = pstrdup(pq_getmsgstring(in));
     599          32 : }
     600             : 
     601             : /*
     602             :  * Write a tuple to the outputstream, in the most efficient format possible.
     603             :  */
     604             : static void
     605      318454 : logicalrep_write_tuple(StringInfo out, Relation rel, HeapTuple tuple, bool binary)
     606             : {
     607             :     TupleDesc   desc;
     608             :     Datum       values[MaxTupleAttributeNumber];
     609             :     bool        isnull[MaxTupleAttributeNumber];
     610             :     int         i;
     611      318454 :     uint16      nliveatts = 0;
     612             : 
     613      318454 :     desc = RelationGetDescr(rel);
     614             : 
     615      979570 :     for (i = 0; i < desc->natts; i++)
     616             :     {
     617      661116 :         if (TupleDescAttr(desc, i)->attisdropped || TupleDescAttr(desc, i)->attgenerated)
     618           4 :             continue;
     619      661112 :         nliveatts++;
     620             :     }
     621      318454 :     pq_sendint16(out, nliveatts);
     622             : 
     623             :     /* try to allocate enough memory from the get-go */
     624      636908 :     enlargeStringInfo(out, tuple->t_len +
     625      318454 :                       nliveatts * (1 + 4));
     626             : 
     627      318454 :     heap_deform_tuple(tuple, desc, values, isnull);
     628             : 
     629             :     /* Write the values */
     630      979570 :     for (i = 0; i < desc->natts; i++)
     631             :     {
     632             :         HeapTuple   typtup;
     633             :         Form_pg_type typclass;
     634      661116 :         Form_pg_attribute att = TupleDescAttr(desc, i);
     635             :         char       *outputstr;
     636             : 
     637      661116 :         if (att->attisdropped || att->attgenerated)
     638           4 :             continue;
     639             : 
     640      661112 :         if (isnull[i])
     641             :         {
     642       72488 :             pq_sendbyte(out, LOGICALREP_COLUMN_NULL);
     643       72488 :             continue;
     644             :         }
     645             : 
     646      588624 :         if (att->attlen == -1 && VARATT_IS_EXTERNAL_ONDISK(values[i]))
     647             :         {
     648             :             /*
     649             :              * Unchanged toasted datum.  (Note that we don't promise to detect
     650             :              * unchanged data in general; this is just a cheap check to avoid
     651             :              * sending large values unnecessarily.)
     652             :              */
     653           6 :             pq_sendbyte(out, LOGICALREP_COLUMN_UNCHANGED);
     654           6 :             continue;
     655             :         }
     656             : 
     657      588618 :         typtup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(att->atttypid));
     658      588618 :         if (!HeapTupleIsValid(typtup))
     659           0 :             elog(ERROR, "cache lookup failed for type %u", att->atttypid);
     660      588618 :         typclass = (Form_pg_type) GETSTRUCT(typtup);
     661             : 
     662             :         /*
     663             :          * Send in binary if requested and type has suitable send function.
     664             :          */
     665      588618 :         if (binary && OidIsValid(typclass->typsend))
     666       66746 :         {
     667             :             bytea      *outputbytes;
     668             :             int         len;
     669             : 
     670       66746 :             pq_sendbyte(out, LOGICALREP_COLUMN_BINARY);
     671       66746 :             outputbytes = OidSendFunctionCall(typclass->typsend, values[i]);
     672       66746 :             len = VARSIZE(outputbytes) - VARHDRSZ;
     673       66746 :             pq_sendint(out, len, 4);    /* length */
     674       66746 :             pq_sendbytes(out, VARDATA(outputbytes), len);   /* data */
     675       66746 :             pfree(outputbytes);
     676             :         }
     677             :         else
     678             :         {
     679      521872 :             pq_sendbyte(out, LOGICALREP_COLUMN_TEXT);
     680      521872 :             outputstr = OidOutputFunctionCall(typclass->typoutput, values[i]);
     681      521872 :             pq_sendcountedtext(out, outputstr, strlen(outputstr), false);
     682      521872 :             pfree(outputstr);
     683             :         }
     684             : 
     685      588618 :         ReleaseSysCache(typtup);
     686             :     }
     687      318454 : }
     688             : 
     689             : /*
     690             :  * Read tuple in logical replication format from stream.
     691             :  */
     692             : static void
     693      227890 : logicalrep_read_tuple(StringInfo in, LogicalRepTupleData *tuple)
     694             : {
     695             :     int         i;
     696             :     int         natts;
     697             : 
     698             :     /* Get number of attributes */
     699      227890 :     natts = pq_getmsgint(in, 2);
     700             : 
     701             :     /* Allocate space for per-column values; zero out unused StringInfoDatas */
     702      227890 :     tuple->colvalues = (StringInfoData *) palloc0(natts * sizeof(StringInfoData));
     703      227890 :     tuple->colstatus = (char *) palloc(natts * sizeof(char));
     704      227890 :     tuple->ncols = natts;
     705             : 
     706             :     /* Read the data */
     707      707872 :     for (i = 0; i < natts; i++)
     708             :     {
     709             :         char        kind;
     710             :         int         len;
     711      479982 :         StringInfo  value = &tuple->colvalues[i];
     712             : 
     713      479982 :         kind = pq_getmsgbyte(in);
     714      479982 :         tuple->colstatus[i] = kind;
     715             : 
     716      479982 :         switch (kind)
     717             :         {
     718             :             case LOGICALREP_COLUMN_NULL:
     719             :                 /* nothing more to do */
     720       62210 :                 break;
     721             :             case LOGICALREP_COLUMN_UNCHANGED:
     722             :                 /* we don't receive the value of an unchanged column */
     723           6 :                 break;
     724             :             case LOGICALREP_COLUMN_TEXT:
     725      351028 :                 len = pq_getmsgint(in, 4);  /* read length */
     726             : 
     727             :                 /* and data */
     728      351028 :                 value->data = palloc(len + 1);
     729      351028 :                 pq_copymsgbytes(in, value->data, len);
     730      351028 :                 value->data[len] = '\0';
     731             :                 /* make StringInfo fully valid */
     732      351028 :                 value->len = len;
     733      351028 :                 value->cursor = 0;
     734      351028 :                 value->maxlen = len;
     735      351028 :                 break;
     736             :             case LOGICALREP_COLUMN_BINARY:
     737       66738 :                 len = pq_getmsgint(in, 4);  /* read length */
     738             : 
     739             :                 /* and data */
     740       66738 :                 value->data = palloc(len + 1);
     741       66738 :                 pq_copymsgbytes(in, value->data, len);
     742             :                 /* not strictly necessary but per StringInfo practice */
     743       66738 :                 value->data[len] = '\0';
     744             :                 /* make StringInfo fully valid */
     745       66738 :                 value->len = len;
     746       66738 :                 value->cursor = 0;
     747       66738 :                 value->maxlen = len;
     748       66738 :                 break;
     749             :             default:
     750           0 :                 elog(ERROR, "unrecognized data representation type '%c'", kind);
     751             :         }
     752             :     }
     753      227890 : }
     754             : 
     755             : /*
     756             :  * Write relation attribute metadata to the stream.
     757             :  */
     758             : static void
     759         196 : logicalrep_write_attrs(StringInfo out, Relation rel)
     760             : {
     761             :     TupleDesc   desc;
     762             :     int         i;
     763         196 :     uint16      nliveatts = 0;
     764         196 :     Bitmapset  *idattrs = NULL;
     765             :     bool        replidentfull;
     766             : 
     767         196 :     desc = RelationGetDescr(rel);
     768             : 
     769             :     /* send number of live attributes */
     770         628 :     for (i = 0; i < desc->natts; i++)
     771             :     {
     772         432 :         if (TupleDescAttr(desc, i)->attisdropped || TupleDescAttr(desc, i)->attgenerated)
     773           2 :             continue;
     774         430 :         nliveatts++;
     775             :     }
     776         196 :     pq_sendint16(out, nliveatts);
     777             : 
     778             :     /* fetch bitmap of REPLICATION IDENTITY attributes */
     779         196 :     replidentfull = (rel->rd_rel->relreplident == REPLICA_IDENTITY_FULL);
     780         196 :     if (!replidentfull)
     781         184 :         idattrs = RelationGetIndexAttrBitmap(rel,
     782             :                                              INDEX_ATTR_BITMAP_IDENTITY_KEY);
     783             : 
     784             :     /* send the attributes */
     785         628 :     for (i = 0; i < desc->natts; i++)
     786             :     {
     787         432 :         Form_pg_attribute att = TupleDescAttr(desc, i);
     788         432 :         uint8       flags = 0;
     789             : 
     790         432 :         if (att->attisdropped || att->attgenerated)
     791           2 :             continue;
     792             : 
     793             :         /* REPLICA IDENTITY FULL means all columns are sent as part of key. */
     794         846 :         if (replidentfull ||
     795         416 :             bms_is_member(att->attnum - FirstLowInvalidHeapAttributeNumber,
     796             :                           idattrs))
     797         178 :             flags |= LOGICALREP_IS_REPLICA_IDENTITY;
     798             : 
     799         430 :         pq_sendbyte(out, flags);
     800             : 
     801             :         /* attribute name */
     802         430 :         pq_sendstring(out, NameStr(att->attname));
     803             : 
     804             :         /* attribute type id */
     805         430 :         pq_sendint32(out, (int) att->atttypid);
     806             : 
     807             :         /* attribute mode */
     808         430 :         pq_sendint32(out, att->atttypmod);
     809             :     }
     810             : 
     811         196 :     bms_free(idattrs);
     812         196 : }
     813             : 
     814             : /*
     815             :  * Read relation attribute metadata from the stream.
     816             :  */
     817             : static void
     818         250 : logicalrep_read_attrs(StringInfo in, LogicalRepRelation *rel)
     819             : {
     820             :     int         i;
     821             :     int         natts;
     822             :     char      **attnames;
     823             :     Oid        *atttyps;
     824         250 :     Bitmapset  *attkeys = NULL;
     825             : 
     826         250 :     natts = pq_getmsgint(in, 2);
     827         250 :     attnames = palloc(natts * sizeof(char *));
     828         250 :     atttyps = palloc(natts * sizeof(Oid));
     829             : 
     830             :     /* read the attributes */
     831         782 :     for (i = 0; i < natts; i++)
     832             :     {
     833             :         uint8       flags;
     834             : 
     835             :         /* Check for replica identity column */
     836         532 :         flags = pq_getmsgbyte(in);
     837         532 :         if (flags & LOGICALREP_IS_REPLICA_IDENTITY)
     838         232 :             attkeys = bms_add_member(attkeys, i);
     839             : 
     840             :         /* attribute name */
     841         532 :         attnames[i] = pstrdup(pq_getmsgstring(in));
     842             : 
     843             :         /* attribute type id */
     844         532 :         atttyps[i] = (Oid) pq_getmsgint(in, 4);
     845             : 
     846             :         /* we ignore attribute mode for now */
     847         532 :         (void) pq_getmsgint(in, 4);
     848             :     }
     849             : 
     850         250 :     rel->attnames = attnames;
     851         250 :     rel->atttyps = atttyps;
     852         250 :     rel->attkeys = attkeys;
     853         250 :     rel->natts = natts;
     854         250 : }
     855             : 
     856             : /*
     857             :  * Write the namespace name or empty string for pg_catalog (to save space).
     858             :  */
     859             : static void
     860         228 : logicalrep_write_namespace(StringInfo out, Oid nspid)
     861             : {
     862         228 :     if (nspid == PG_CATALOG_NAMESPACE)
     863           2 :         pq_sendbyte(out, '\0');
     864             :     else
     865             :     {
     866         226 :         char       *nspname = get_namespace_name(nspid);
     867             : 
     868         226 :         if (nspname == NULL)
     869           0 :             elog(ERROR, "cache lookup failed for namespace %u",
     870             :                  nspid);
     871             : 
     872         226 :         pq_sendstring(out, nspname);
     873             :     }
     874         228 : }
     875             : 
     876             : /*
     877             :  * Read the namespace name while treating empty string as pg_catalog.
     878             :  */
     879             : static const char *
     880         282 : logicalrep_read_namespace(StringInfo in)
     881             : {
     882         282 :     const char *nspname = pq_getmsgstring(in);
     883             : 
     884         282 :     if (nspname[0] == '\0')
     885           2 :         nspname = "pg_catalog";
     886             : 
     887         282 :     return nspname;
     888             : }
     889             : 
     890             : /*
     891             :  * Write the information for the start stream message to the output stream.
     892             :  */
     893             : void
     894         796 : logicalrep_write_stream_start(StringInfo out,
     895             :                               TransactionId xid, bool first_segment)
     896             : {
     897         796 :     pq_sendbyte(out, LOGICAL_REP_MSG_STREAM_START);
     898             : 
     899         796 :     Assert(TransactionIdIsValid(xid));
     900             : 
     901             :     /* transaction ID (we're starting to stream, so must be valid) */
     902         796 :     pq_sendint32(out, xid);
     903             : 
     904             :     /* 1 if this is the first streaming segment for this xid */
     905         796 :     pq_sendbyte(out, first_segment ? 1 : 0);
     906         796 : }
     907             : 
     908             : /*
     909             :  * Read the information about the start stream message from output stream.
     910             :  */
     911             : TransactionId
     912         610 : logicalrep_read_stream_start(StringInfo in, bool *first_segment)
     913             : {
     914             :     TransactionId xid;
     915             : 
     916         610 :     Assert(first_segment);
     917             : 
     918         610 :     xid = pq_getmsgint(in, 4);
     919         610 :     *first_segment = (pq_getmsgbyte(in) == 1);
     920             : 
     921         610 :     return xid;
     922             : }
     923             : 
     924             : /*
     925             :  * Write the stop stream message to the output stream.
     926             :  */
     927             : void
     928         792 : logicalrep_write_stream_stop(StringInfo out)
     929             : {
     930         792 :     pq_sendbyte(out, LOGICAL_REP_MSG_STREAM_END);
     931         792 : }
     932             : 
     933             : /*
     934             :  * Write STREAM COMMIT to the output stream.
     935             :  */
     936             : void
     937          28 : logicalrep_write_stream_commit(StringInfo out, ReorderBufferTXN *txn,
     938             :                                XLogRecPtr commit_lsn)
     939             : {
     940          28 :     uint8       flags = 0;
     941             : 
     942          28 :     pq_sendbyte(out, LOGICAL_REP_MSG_STREAM_COMMIT);
     943             : 
     944          28 :     Assert(TransactionIdIsValid(txn->xid));
     945             : 
     946             :     /* transaction ID */
     947          28 :     pq_sendint32(out, txn->xid);
     948             : 
     949             :     /* send the flags field (unused for now) */
     950          28 :     pq_sendbyte(out, flags);
     951             : 
     952             :     /* send fields */
     953          28 :     pq_sendint64(out, commit_lsn);
     954          28 :     pq_sendint64(out, txn->end_lsn);
     955          28 :     pq_sendint64(out, txn->commit_time);
     956          28 : }
     957             : 
     958             : /*
     959             :  * Read STREAM COMMIT from the output stream.
     960             :  */
     961             : TransactionId
     962          28 : logicalrep_read_stream_commit(StringInfo in, LogicalRepCommitData *commit_data)
     963             : {
     964             :     TransactionId xid;
     965             :     uint8       flags;
     966             : 
     967          28 :     xid = pq_getmsgint(in, 4);
     968             : 
     969             :     /* read flags (unused for now) */
     970          28 :     flags = pq_getmsgbyte(in);
     971             : 
     972          28 :     if (flags != 0)
     973           0 :         elog(ERROR, "unrecognized flags %u in commit message", flags);
     974             : 
     975             :     /* read fields */
     976          28 :     commit_data->commit_lsn = pq_getmsgint64(in);
     977          28 :     commit_data->end_lsn = pq_getmsgint64(in);
     978          28 :     commit_data->committime = pq_getmsgint64(in);
     979             : 
     980          28 :     return xid;
     981             : }
     982             : 
     983             : /*
     984             :  * Write STREAM ABORT to the output stream. Note that xid and subxid will be
     985             :  * same for the top-level transaction abort.
     986             :  */
     987             : void
     988          26 : logicalrep_write_stream_abort(StringInfo out, TransactionId xid,
     989             :                               TransactionId subxid)
     990             : {
     991          26 :     pq_sendbyte(out, LOGICAL_REP_MSG_STREAM_ABORT);
     992             : 
     993          26 :     Assert(TransactionIdIsValid(xid) && TransactionIdIsValid(subxid));
     994             : 
     995             :     /* transaction ID */
     996          26 :     pq_sendint32(out, xid);
     997          26 :     pq_sendint32(out, subxid);
     998          26 : }
     999             : 
    1000             : /*
    1001             :  * Read STREAM ABORT from the output stream.
    1002             :  */
    1003             : void
    1004          26 : logicalrep_read_stream_abort(StringInfo in, TransactionId *xid,
    1005             :                              TransactionId *subxid)
    1006             : {
    1007          26 :     Assert(xid && subxid);
    1008             : 
    1009          26 :     *xid = pq_getmsgint(in, 4);
    1010          26 :     *subxid = pq_getmsgint(in, 4);
    1011          26 : }

Generated by: LCOV version 1.14