PostgreSQL Source Code git master
Loading...
Searching...
No Matches
sequencesync.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 * sequencesync.c
3 * PostgreSQL logical replication: sequence synchronization
4 *
5 * Copyright (c) 2025-2026, PostgreSQL Global Development Group
6 *
7 * IDENTIFICATION
8 * src/backend/replication/logical/sequencesync.c
9 *
10 * NOTES
11 * This file contains code for sequence synchronization for
12 * logical replication.
13 *
14 * Sequences requiring synchronization are tracked in the pg_subscription_rel
15 * catalog.
16 *
17 * Sequences to be synchronized will be added with state INIT when either of
18 * the following commands is executed:
19 * CREATE SUBSCRIPTION
20 * ALTER SUBSCRIPTION ... REFRESH PUBLICATION
21 *
22 * Executing the following command resets all sequences in the subscription to
23 * state INIT, triggering re-synchronization:
24 * ALTER SUBSCRIPTION ... REFRESH SEQUENCES
25 *
26 * The apply worker periodically scans pg_subscription_rel for sequences in
27 * INIT state. When such sequences are found, it spawns a sequencesync worker
28 * to handle synchronization.
29 *
30 * A single sequencesync worker is responsible for synchronizing all sequences.
31 * It begins by retrieving the list of sequences that are flagged for
32 * synchronization, i.e., those in the INIT state. These sequences are then
33 * processed in batches, allowing multiple entries to be synchronized within a
34 * single transaction. The worker fetches the current sequence values and page
35 * LSNs from the remote publisher, updates the corresponding sequences on the
36 * local subscriber, and finally marks each sequence as READY upon successful
37 * synchronization.
38 *
39 * Sequence state transitions follow this pattern:
40 * INIT -> READY
41 *
42 * To avoid creating too many transactions, up to MAX_SEQUENCES_SYNC_PER_BATCH
43 * sequences are synchronized per transaction. The locks on the sequence
44 * relation will be periodically released at each transaction commit.
45 *
46 * XXX: We didn't choose launcher process to maintain the launch of sequencesync
47 * worker as it didn't have database connection to access the sequences from the
48 * pg_subscription_rel system catalog that need to be synchronized.
49 *-------------------------------------------------------------------------
50 */
51
52#include "postgres.h"
53
54#include "access/genam.h"
55#include "access/table.h"
56#include "catalog/pg_sequence.h"
58#include "commands/sequence.h"
59#include "pgstat.h"
63#include "storage/lwlock.h"
64#include "utils/acl.h"
65#include "utils/builtins.h"
66#include "utils/fmgroids.h"
67#include "utils/guc.h"
68#include "utils/inval.h"
69#include "utils/lsyscache.h"
70#include "utils/memutils.h"
71#include "utils/pg_lsn.h"
72#include "utils/syscache.h"
73#include "utils/usercontext.h"
74
75#define REMOTE_SEQ_COL_COUNT 11
76
85
86static List *seqinfos = NIL;
87
88/*
89 * Apply worker determines if sequence synchronization is needed.
90 *
91 * Start a sequencesync worker if one is not already running. The active
92 * sequencesync worker will handle all pending sequence synchronization. If any
93 * sequences remain unsynchronized after it exits, a new worker can be started
94 * in the next iteration.
95 */
96void
98{
100 int nsyncworkers;
102 bool started_tx;
103
105
106 if (started_tx)
107 {
109 pgstat_report_stat(true);
110 }
111
113 return;
114
116
117 /* Check if there is a sequencesync worker already running? */
120 InvalidOid, true);
122 {
124 return;
125 }
126
127 /*
128 * Count running sync workers for this subscription, while we have the
129 * lock.
130 */
133
134 /*
135 * It is okay to read/update last_seqsync_start_time here in apply worker
136 * as we have already ensured that sync worker doesn't exist.
137 */
140}
141
142/*
143 * get_sequences_string
144 *
145 * Build a comma-separated string of schema-qualified sequence names
146 * for the given list of sequence indexes.
147 */
148static void
150{
153 {
156
157 if (buf->len > 0)
159
160 appendStringInfo(buf, "\"%s.%s\"", seqinfo->nspname, seqinfo->seqname);
161 }
162}
163
164/*
165 * report_sequence_errors
166 *
167 * Report discrepancies found during sequence synchronization between
168 * the publisher and subscriber. Emits warnings for:
169 * a) mismatched definitions or concurrent rename
170 * b) insufficient privileges on the subscriber
171 * c) insufficient privileges on the publisher
172 * d) missing sequences on the publisher
173 * Then raises an ERROR to indicate synchronization failure.
174 */
175static void
180{
182
183 /* Quick exit if there are no errors to report */
186 return;
187
189
191 {
195 errmsg_plural("mismatched or renamed sequence on subscriber (%s)",
196 "mismatched or renamed sequences on subscriber (%s)",
198 seqstr.data));
199 }
200
202 {
204
205 /*
206 * With run_as_owner enabled, sequence synchronization runs as the
207 * subscription owner, so a missing UPDATE privilege should be granted
208 * to that role. Otherwise, the worker switches to the sequence owner
209 * before checking privileges, so no useful GRANT hint can be
210 * provided.
211 */
214 errmsg_plural("insufficient privileges on subscriber sequence (%s)",
215 "insufficient privileges on subscriber sequences (%s)",
217 seqstr.data),
219 errhint_plural("Grant UPDATE on the sequence to the subscription "
220 "owner on the subscriber.",
221 "Grant UPDATE on the sequences to the subscription "
222 "owner on the subscriber.",
224 }
225
227 {
231 errmsg_plural("insufficient privileges on publisher sequence (%s)",
232 "insufficient privileges on publisher sequences (%s)",
234 seqstr.data),
235 errhint_plural("Grant SELECT on the sequence to the role used for "
236 "the replication connection on the publisher.",
237 "Grant SELECT on the sequences to the role used for "
238 "the replication connection on the publisher.",
240 }
241
243 {
247 errmsg_plural("missing sequence on publisher (%s)",
248 "missing sequences on publisher (%s)",
250 seqstr.data));
251 }
252
255 errmsg("logical replication sequence synchronization failed for subscription \"%s\"",
257}
258
259/*
260 * get_and_validate_seq_info
261 *
262 * Extracts remote sequence information from the tuple slot received from the
263 * publisher, and validates it against the corresponding local sequence
264 * definition.
265 */
266static CopySeqResult
269{
270 bool isnull;
271 int col = 0;
272 Datum datum;
279 bool remote_cycle;
284
285 *seqidx = DatumGetInt32(slot_getattr(slot, ++col, &isnull));
286 Assert(!isnull);
287
288 /* Identify the corresponding local sequence for the given index. */
291
292 /*
293 * has_sequence_privilege() itself returns NULL, rather than false, when
294 * the sequence has been dropped concurrently after it was identified in
295 * the catalog snapshot (see has_sequence_privilege_id()). Treat that as a
296 * missing sequence on the publisher.
297 */
298 datum = slot_getattr(slot, ++col, &isnull);
299 if (isnull)
300 return COPYSEQ_SKIPPED;
301
303
304 /*
305 * The remote sequence state can be NULL if the publisher lacks the
306 * required privileges or if the sequence was dropped concurrently after
307 * it was identified in the catalog snapshot (see pg_get_sequence_data()).
308 */
309 datum = slot_getattr(slot, ++col, &isnull);
310 if (isnull)
311 {
312 /*
313 * The sequence was dropped concurrently after it was identified in
314 * the catalog snapshot. Treat it as skipped (and, since it no longer
315 * exists on the publisher, ultimately missing).
316 */
318 return COPYSEQ_SKIPPED;
319
320 /*
321 * The publisher lacks the SELECT privilege required by
322 * pg_get_sequence_data(). Since has_sequence_privilege() returned
323 * false, not NULL, do not classify this sequence as missing on the
324 * publisher.
325 */
326 seqinfo_local->found_on_pub = true;
328 }
329
330 seqinfo_local->last_value = DatumGetInt64(datum);
331
332 seqinfo_local->is_called = DatumGetBool(slot_getattr(slot, ++col, &isnull));
333 Assert(!isnull);
334
335 seqinfo_local->page_lsn = DatumGetLSN(slot_getattr(slot, ++col, &isnull));
336 Assert(!isnull);
337
338 remote_typid = DatumGetObjectId(slot_getattr(slot, ++col, &isnull));
339 Assert(!isnull);
340
341 remote_start = DatumGetInt64(slot_getattr(slot, ++col, &isnull));
342 Assert(!isnull);
343
344 remote_increment = DatumGetInt64(slot_getattr(slot, ++col, &isnull));
345 Assert(!isnull);
346
347 remote_min = DatumGetInt64(slot_getattr(slot, ++col, &isnull));
348 Assert(!isnull);
349
350 remote_max = DatumGetInt64(slot_getattr(slot, ++col, &isnull));
351 Assert(!isnull);
352
353 remote_cycle = DatumGetBool(slot_getattr(slot, ++col, &isnull));
354 Assert(!isnull);
355
356 /* Sanity check */
358
359 seqinfo_local->found_on_pub = true;
360
362
363 /* Sequence was concurrently dropped? */
364 if (!*sequence_rel)
365 return COPYSEQ_SKIPPED;
366
368
369 /* Sequence was concurrently dropped? */
370 if (!HeapTupleIsValid(tup))
371 elog(ERROR, "cache lookup failed for sequence %u",
372 seqinfo_local->localrelid);
373
375
376 /* Sequence parameters for remote/local are the same? */
377 if (local_seq->seqtypid != remote_typid ||
378 local_seq->seqstart != remote_start ||
379 local_seq->seqincrement != remote_increment ||
380 local_seq->seqmin != remote_min ||
381 local_seq->seqmax != remote_max ||
382 local_seq->seqcycle != remote_cycle)
384
385 /* Sequence was concurrently renamed? */
386 if (strcmp(seqinfo_local->nspname,
390
392 return result;
393}
394
395/*
396 * Apply remote sequence state to local sequence and mark it as
397 * synchronized (READY).
398 */
399static CopySeqResult
401{
405 Oid seqoid = seqinfo->localrelid;
406
407 /*
408 * If the user did not opt to run as the owner of the subscription
409 * ('run_as_owner'), then copy the sequence as the owner of the sequence.
410 */
411 if (!run_as_owner)
413
415
416 if (aclresult != ACLCHECK_OK)
417 {
418 if (!run_as_owner)
420
422 }
423
424 /*
425 * The log counter (log_cnt) tracks how many sequence values are still
426 * unused locally. It is only relevant to the local node and managed
427 * internally by nextval() when allocating new ranges. Since log_cnt does
428 * not affect the visible sequence state (like last_value or is_called)
429 * and is only used for local caching, it need not be copied to the
430 * subscriber during synchronization.
431 */
432 SetSequence(seqoid, seqinfo->last_value, seqinfo->is_called);
433
434 if (!run_as_owner)
436
437 /*
438 * Record the remote sequence's LSN in pg_subscription_rel and mark the
439 * sequence as READY.
440 */
442 seqinfo->page_lsn, false);
443
444 return COPYSEQ_SUCCESS;
445}
446
447/*
448 * Copy existing data of sequences from the publisher.
449 */
450static void
452{
453 int cur_batch_base_index = 0;
460 StringInfoData cmd;
462
463 /*
464 * Sequence synchronization depends on publisher-side functionality
465 * introduced in PostgreSQL 19, so it cannot work against an older
466 * publisher.
467 */
468 if (walrcv_server_version(conn) < 190000)
471 errmsg("cannot synchronize sequences if the publisher is running a version earlier than PostgreSQL 19"));
472
474 initStringInfo(&cmd);
475
476#define MAX_SEQUENCES_SYNC_PER_BATCH 100
477
478 elog(DEBUG1,
479 "logical replication sequence synchronization for subscription \"%s\" - total unsynchronized: %d",
481
483 {
486 int batch_size = 0;
487 int batch_succeeded_count = 0;
489 int batch_skipped_count = 0;
493
494 WalRcvExecResult *res;
495 TupleTableSlot *slot;
496
498
499 for (int idx = cur_batch_base_index; idx < n_seqinfos; idx++)
500 {
501 char *nspname_literal;
502 char *seqname_literal;
503
506
507 if (seqstr.len > 0)
509
512
513 appendStringInfo(&seqstr, "(%s, %s, %d)",
515
516 if (++batch_size == MAX_SEQUENCES_SYNC_PER_BATCH)
517 break;
518 }
519
520 /*
521 * We deliberately avoid acquiring a local lock on the sequence before
522 * querying the publisher to prevent potential distributed deadlocks
523 * in bi-directional replication setups.
524 *
525 * Example scenario:
526 *
527 * - On each node, a background worker acquires a lock on a sequence
528 * as part of a sync operation.
529 *
530 * - Concurrently, a user transaction attempts to alter the same
531 * sequence, waiting on the background worker's lock.
532 *
533 * - Meanwhile, a query from the other node tries to access metadata
534 * that depends on the completion of the alter operation.
535 *
536 * - This creates a circular wait across nodes:
537 *
538 * Node-1: Query -> waits on Alter -> waits on Sync Worker
539 *
540 * Node-2: Query -> waits on Alter -> waits on Sync Worker
541 *
542 * Since each node only sees part of the wait graph, the deadlock may
543 * go undetected, leading to indefinite blocking.
544 *
545 * Note: Each entry in VALUES includes an index 'seqidx' that
546 * represents the sequence's position in the local 'seqinfos' list.
547 * This index is propagated to the query results and later used to
548 * directly map the fetched publisher sequence rows back to their
549 * corresponding local entries without relying on result order or name
550 * matching.
551 */
552 appendStringInfo(&cmd,
553 "SELECT s.seqidx, has_sequence_privilege(c.oid, 'SELECT'),\n"
554 " ps.*, seq.seqtypid,\n"
555 " seq.seqstart, seq.seqincrement, seq.seqmin,\n"
556 " seq.seqmax, seq.seqcycle\n"
557 "FROM ( VALUES %s ) AS s (schname, seqname, seqidx)\n"
558 "JOIN pg_namespace n ON n.nspname = s.schname\n"
559 "JOIN pg_class c ON c.relnamespace = n.oid AND c.relname = s.seqname\n"
560 "JOIN pg_sequence seq ON seq.seqrelid = c.oid\n"
561 "JOIN LATERAL pg_get_sequence_data(seq.seqrelid) AS ps ON true\n",
562 seqstr.data);
563
564 res = walrcv_exec(conn, cmd.data, lengthof(seqRow), seqRow);
565 if (res->status != WALRCV_OK_TUPLES)
568 errmsg("could not fetch sequence information from the publisher: %s",
569 res->err));
570
572 while (tuplestore_gettupleslot(res->tuplestore, true, false, slot))
573 {
577 int seqidx;
578
580
582 {
583 ConfigReloadPending = false;
585 }
586
588 &seqinfo, &seqidx);
591 sequence_rel->rd_rel->relowner);
592
593 switch (sync_status)
594 {
595 case COPYSEQ_SUCCESS:
596 elog(DEBUG1,
597 "logical replication synchronization for subscription \"%s\", sequence \"%s.%s\" has finished",
598 MySubscription->name, seqinfo->nspname,
599 seqinfo->seqname);
601 break;
602 case COPYSEQ_MISMATCH:
603
604 /*
605 * Remember mismatched sequences in a long-lived memory
606 * context since these will be used after the transaction
607 * is committed.
608 */
611 seqidx);
614 break;
616
617 /*
618 * Remember sequences with insufficient privileges in a
619 * long-lived memory context since these will be used
620 * after the transaction is committed.
621 */
624 seqidx);
627 break;
629
630 /*
631 * Remember sequences for which the publisher lacks the
632 * privileges required by pg_get_sequence_data().
633 */
636 seqidx);
639 break;
640 case COPYSEQ_SKIPPED:
641
642 /*
643 * Concurrent removal of a sequence on the subscriber is
644 * treated as success, since the only viable action is to
645 * skip the corresponding sequence data. Missing sequences
646 * on the publisher are treated as ERROR.
647 */
648 if (seqinfo->found_on_pub)
649 {
650 ereport(LOG,
651 errmsg("skip synchronization of sequence \"%s.%s\" because it has been dropped concurrently",
652 seqinfo->nspname,
653 seqinfo->seqname));
655 }
656 break;
657 }
658
659 if (sequence_rel)
661 }
662
666 resetStringInfo(&cmd);
667
673
674 elog(DEBUG1,
675 "logical replication sequence synchronization for subscription \"%s\" - batch #%d = %d attempted, %d succeeded, %d mismatched, %d subscriber insufficient permission, %d publisher insufficient permission, %d missing from publisher, %d skipped",
680
681 /* Commit this batch, and prepare for next batch */
683
685 {
686 for (int idx = cur_batch_base_index; idx < cur_batch_base_index + batch_size; idx++)
687 {
690
691 /* If the sequence was not found on publisher, record it */
692 if (!seqinfo->found_on_pub)
694 }
695 }
696
697 /*
698 * cur_batch_base_index is not incremented sequentially because some
699 * sequences may be missing, and the number of fetched rows may not
700 * match the batch size.
701 */
702 cur_batch_base_index += batch_size;
703 }
704
705 /* Report mismatches, permission issues, or missing sequences */
708}
709
710/*
711 * Identifies sequences that require synchronization and initiates the
712 * synchronization process.
713 */
714static void
716{
717 char *err;
719 Relation rel;
721 ScanKeyData skey[2];
722 SysScanDesc scan;
725
727
729
730 ScanKeyInit(&skey[0],
733 ObjectIdGetDatum(subid));
734
735 ScanKeyInit(&skey[1],
739
740 scan = systable_beginscan(rel, InvalidOid, false,
741 NULL, 2, skey);
742 while (HeapTupleIsValid(tup = systable_getnext(scan)))
743 {
748
750
752
754
755 /* Skip if sequence was dropped concurrently */
756 if (!sequence_rel)
757 continue;
758
759 /* Skip if the relation is not a sequence */
760 if (sequence_rel->rd_rel->relkind != RELKIND_SEQUENCE)
761 {
763 continue;
764 }
765
766 /*
767 * Worker needs to process sequences across transaction boundary, so
768 * allocate them under long-lived context.
769 */
771
773 seq->localrelid = subrel->srrelid;
777
779
781 }
782
783 /* Cleanup */
784 systable_endscan(scan);
786
788
789 /*
790 * Exit early if no catalog entries found, likely due to concurrent drops.
791 */
792 if (!seqinfos)
793 return;
794
795 /* Is the use of a password mandatory? */
798
800 appendStringInfo(&app_name, "pg_%u_sequence_sync_" UINT64_FORMAT,
802
803 /*
804 * Establish the connection to the publisher for sequence synchronization.
805 */
809 app_name.data, &err);
813 errmsg("sequencesync worker for subscription \"%s\" could not connect to the publisher: %s",
815
816 pfree(app_name.data);
817
819}
820
821/*
822 * Execute the initial sync with error handling. Disable the subscription,
823 * if required.
824 *
825 * Note that we don't handle FATAL errors which are probably because of system
826 * resource error and are not repeatable.
827 */
828static void
830{
832
833 PG_TRY();
834 {
835 /* Call initial sync. */
837 }
838 PG_CATCH();
839 {
842 else
843 {
844 /*
845 * Report the worker failed during sequence synchronization. Abort
846 * the current transaction so that the stats message is sent in an
847 * idle state.
848 */
851
852 PG_RE_THROW();
853 }
854 }
855 PG_END_TRY();
856}
857
858/* Logical Replication sequencesync worker entry point */
859void
Datum idx(PG_FUNCTION_ARGS)
Definition _int_op.c:263
AclResult
Definition acl.h:183
@ ACLCHECK_OK
Definition acl.h:184
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition aclchk.c:4105
void DisableSubscriptionAndExit(void)
Definition worker.c:6031
MemoryContext ApplyContext
Definition worker.c:477
void SetupApplyOrSyncWorker(int worker_slot)
Definition worker.c:5971
WalReceiverConn * LogRepWorkerWalRcvConn
Definition worker.c:482
Subscription * MySubscription
Definition worker.c:484
#define Assert(condition)
Definition c.h:1002
int64_t int64
Definition c.h:680
#define UINT64_FORMAT
Definition c.h:694
#define lengthof(array)
Definition c.h:932
uint32 result
void SetSequence(Oid relid, int64 next, bool iscalled)
Definition sequence.c:946
int errcode(int sqlerrcode)
Definition elog.c:875
#define LOG
Definition elog.h:32
#define PG_RE_THROW()
Definition elog.h:407
int int int errhint_plural(const char *fmt_singular, const char *fmt_plural, unsigned long n,...) pg_attribute_printf(1
#define PG_TRY(...)
Definition elog.h:374
#define WARNING
Definition elog.h:37
int int int errmsg_plural(const char *fmt_singular, const char *fmt_plural, unsigned long n,...) pg_attribute_printf(1
#define PG_END_TRY(...)
Definition elog.h:399
#define DEBUG1
Definition elog.h:31
#define ERROR
Definition elog.h:40
#define PG_CATCH(...)
Definition elog.h:384
#define elog(elevel,...)
Definition elog.h:228
#define ereport(elevel,...)
Definition elog.h:152
void err(int eval, const char *fmt,...)
Definition err.c:43
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
const TupleTableSlotOps TTSOpsMinimalTuple
Definition execTuples.c:86
#define palloc0_object(type)
Definition fe_memutils.h:90
void systable_endscan(SysScanDesc sysscan)
Definition genam.c:604
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition genam.c:515
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition genam.c:388
void ProcessConfigFile(GucContext context)
Definition guc-file.l:120
@ PGC_SIGHUP
Definition guc.h:75
#define HeapTupleIsValid(tuple)
Definition htup.h:78
static void * GETSTRUCT(const HeapTupleData *tuple)
volatile sig_atomic_t ConfigReloadPending
Definition interrupt.c:27
LogicalRepWorker * logicalrep_worker_find(LogicalRepWorkerType wtype, Oid subid, Oid relid, bool only_running)
Definition launcher.c:268
LogicalRepWorker * MyLogicalRepWorker
Definition launcher.c:58
int logicalrep_sync_worker_count(Oid subid)
Definition launcher.c:937
List * lappend(List *list, void *datum)
Definition list.c:339
List * lappend_int(List *list, int datum)
Definition list.c:357
#define NoLock
Definition lockdefs.h:34
#define AccessShareLock
Definition lockdefs.h:36
#define RowExclusiveLock
Definition lockdefs.h:38
char * get_namespace_name(Oid nspid)
Definition lsyscache.c:3682
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition lwlock.c:1150
void LWLockRelease(LWLock *lock)
Definition lwlock.c:1767
@ LW_SHARED
Definition lwlock.h:105
char * pstrdup(const char *in)
Definition mcxt.c:1910
void pfree(void *pointer)
Definition mcxt.c:1619
#define CHECK_FOR_INTERRUPTS()
Definition miscadmin.h:125
Oid GetUserId(void)
Definition miscinit.c:470
static char * errmsg
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:138
#define ACL_UPDATE
Definition parsenodes.h:78
static int list_length(const List *l)
Definition pg_list.h:152
#define NIL
Definition pg_list.h:68
static void * list_nth(const List *list, int n)
Definition pg_list.h:331
#define foreach_int(var, lst)
Definition pg_list.h:502
static XLogRecPtr DatumGetLSN(Datum X)
Definition pg_lsn.h:25
END_CATALOG_STRUCT typedef FormData_pg_sequence * Form_pg_sequence
Definition pg_sequence.h:44
void UpdateSubscriptionRelState(Oid subid, Oid relid, char state, XLogRecPtr sublsn, bool already_locked)
END_CATALOG_STRUCT typedef FormData_pg_subscription_rel * Form_pg_subscription_rel
static char buf[DEFAULT_XLOG_SEG_SIZE]
long pgstat_report_stat(bool force)
Definition pgstat.c:722
void pgstat_report_subscription_error(Oid subid)
static bool DatumGetBool(Datum X)
Definition postgres.h:100
static int64 DatumGetInt64(Datum X)
Definition postgres.h:416
static Oid DatumGetObjectId(Datum X)
Definition postgres.h:242
static Datum ObjectIdGetDatum(Oid X)
Definition postgres.h:252
uint64_t Datum
Definition postgres.h:70
static int32 DatumGetInt32(Datum X)
Definition postgres.h:202
static Datum CharGetDatum(char X)
Definition postgres.h:132
#define InvalidOid
unsigned int Oid
static int fb(int x)
char * quote_literal_cstr(const char *rawstr)
Definition quote.c:101
#define RelationGetRelationName(relation)
Definition rel.h:550
#define RelationGetNamespace(relation)
Definition rel.h:557
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition scankey.c:76
#define REMOTE_SEQ_COL_COUNT
CopySeqResult
@ COPYSEQ_SUBSCRIBER_INSUFFICIENT_PERM
@ COPYSEQ_MISMATCH
@ COPYSEQ_SUCCESS
@ COPYSEQ_SKIPPED
@ COPYSEQ_PUBLISHER_INSUFFICIENT_PERM
static CopySeqResult get_and_validate_seq_info(TupleTableSlot *slot, Relation *sequence_rel, LogicalRepSequenceInfo **seqinfo, int *seqidx)
static List * seqinfos
#define MAX_SEQUENCES_SYNC_PER_BATCH
void SequenceSyncWorkerMain(Datum main_arg)
static void report_sequence_errors(List *mismatched_seqs_idx, List *sub_insuffperm_seqs_idx, List *pub_insuffperm_seqs_idx, List *missing_seqs_idx)
static void start_sequence_sync(void)
static void LogicalRepSyncSequences(void)
static void copy_sequences(WalReceiverConn *conn)
static void get_sequences_string(List *seqindexes, StringInfo buf)
void ProcessSequencesForSync(void)
static CopySeqResult copy_sequence(LogicalRepSequenceInfo *seqinfo, Oid seqowner)
#define BTEqualStrategyNumber
Definition stratnum.h:31
PGconn * conn
Definition streamutil.c:52
void resetStringInfo(StringInfo str)
Definition stringinfo.c:126
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition stringinfo.c:145
void appendStringInfoString(StringInfo str, const char *s)
Definition stringinfo.c:230
void initStringInfo(StringInfo str)
Definition stringinfo.c:97
Definition pg_list.h:54
TimestampTz last_seqsync_start_time
Tuplestorestate * tuplestore
TupleDesc tupledesc
WalRcvExecStatus status
void launch_sync_worker(LogicalRepWorkerType wtype, int nsyncworkers, Oid relid, TimestampTz *last_start_time)
Definition syncutils.c:118
pg_noreturn void FinishSyncWorker(void)
Definition syncutils.c:50
void FetchRelationStates(bool *has_pending_subtables, bool *has_pending_subsequences, bool *started_tx)
Definition syncutils.c:203
void ReleaseSysCache(HeapTuple tuple)
Definition syscache.c:265
HeapTuple SearchSysCache1(SysCacheIdentifier cacheId, Datum key1)
Definition syscache.c:221
Relation try_table_open(Oid relationId, LOCKMODE lockmode)
Definition table.c:60
void table_close(Relation relation, LOCKMODE lockmode)
Definition table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition table.c:40
bool tuplestore_gettupleslot(Tuplestorestate *state, bool forward, bool copy, TupleTableSlot *slot)
static Datum slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
Definition tuptable.h:417
void SwitchToUntrustedUser(Oid userid, UserContext *context)
Definition usercontext.c:33
void RestoreUserContext(UserContext *context)
Definition usercontext.c:87
#define walrcv_connect(conninfo, replication, logical, must_use_password, appname, err)
@ WALRCV_OK_TUPLES
static void walrcv_clear_result(WalRcvExecResult *walres)
#define walrcv_server_version(conn)
#define walrcv_exec(conn, exec, nRetTypes, retTypes)
@ WORKERTYPE_SEQUENCESYNC
static bool am_sequencesync_worker(void)
void StartTransactionCommand(void)
Definition xact.c:3112
void CommitTransactionCommand(void)
Definition xact.c:3210
void AbortOutOfAnyTransaction(void)
Definition xact.c:4916
uint64 GetSystemIdentifier(void)
Definition xlog.c:4642