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

Generated by: LCOV version 1.14