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 : }
|