From a0701c4ab494f93d488c898969d2f44da79bb135 Mon Sep 17 00:00:00 2001 From: Zhijie Hou Date: Sat, 17 Oct 2026 19:55:45 +0800 Subject: [PATCH v1-topup] Add hint for SET ROLE failure during sequence synchronization. When run_as_owner is disabled, sequence synchronization switches to the sequence owner before checking UPDATE privileges on the sequence. If the subscription owner cannot SET ROLE to the sequence owner, reporting a bare "insufficient privileges on subscriber sequence" warning without a hint makes it unclear whether the subscription owner lacks UPDATE on the sequence or permission to SET ROLE to the sequence owner. Fix this by tracking sequences whose owner cannot be switched to separately from sequences whose owner lacks UPDATE privilege, and emit a HINT suggesting granting the sequence owner role with the SET option to the subscription owner. --- .../replication/logical/sequencesync.c | 44 ++++++++++++++++--- src/test/subscription/t/036_sequences.pl | 2 +- 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/src/backend/replication/logical/sequencesync.c b/src/backend/replication/logical/sequencesync.c index 325eb049209..3d9834f473a 100644 --- a/src/backend/replication/logical/sequencesync.c +++ b/src/backend/replication/logical/sequencesync.c @@ -80,6 +80,7 @@ typedef enum CopySeqResult COPYSEQ_SUCCESS, COPYSEQ_MISMATCH, COPYSEQ_SUBSCRIBER_INSUFFICIENT_PERM, + COPYSEQ_SUBSCRIBER_NO_SETROLE, COPYSEQ_PUBLISHER_INSUFFICIENT_PERM, COPYSEQ_SKIPPED, COPYSEQ_NOT_SUBSCRIBED @@ -177,6 +178,7 @@ get_sequences_string(List *seqindexes, StringInfo buf) static void report_sequence_errors(List *mismatched_seqs_idx, List *sub_insuffperm_seqs_idx, + List *sub_no_setrole_seqs_idx, List *pub_insuffperm_seqs_idx, List *missing_seqs_idx) { @@ -184,7 +186,8 @@ report_sequence_errors(List *mismatched_seqs_idx, /* Quick exit if there are no errors to report */ if (!mismatched_seqs_idx && !sub_insuffperm_seqs_idx && - !pub_insuffperm_seqs_idx && !missing_seqs_idx) + !sub_no_setrole_seqs_idx && !pub_insuffperm_seqs_idx && + !missing_seqs_idx) return; initStringInfo(&seqstr); @@ -208,9 +211,7 @@ report_sequence_errors(List *mismatched_seqs_idx, * With run_as_owner enabled, sequence synchronization runs as the * subscription owner, so a missing UPDATE privilege should be granted * to that role. Otherwise, the worker switches to the sequence owner - * before checking privileges, and the problem can be either that the - * subscription owner cannot SET ROLE to the sequence owner or that - * the sequence owner lacks UPDATE, so no useful GRANT hint can be + * before checking privileges, so no useful GRANT hint can be * provided. */ ereport(WARNING, @@ -227,6 +228,22 @@ report_sequence_errors(List *mismatched_seqs_idx, list_length(sub_insuffperm_seqs_idx)) : 0); } + if (sub_no_setrole_seqs_idx) + { + get_sequences_string(sub_no_setrole_seqs_idx, &seqstr); + ereport(WARNING, + errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg_plural("insufficient privileges on subscriber sequence (%s)", + "insufficient privileges on subscriber sequences (%s)", + list_length(sub_no_setrole_seqs_idx), + seqstr.data), + errhint_plural("Grant the sequence owner role with the SET option " + "to the subscription owner on the subscriber.", + "Grant the sequence owner roles with the SET option " + "to the subscription owner on the subscriber.", + list_length(sub_no_setrole_seqs_idx))); + } + if (pub_insuffperm_seqs_idx) { get_sequences_string(pub_insuffperm_seqs_idx, &seqstr); @@ -460,7 +477,7 @@ copy_sequence(LogicalRepSequenceInfo *seqinfo, Oid seqowner) if (!run_as_owner) { if (!member_can_set_role(GetUserId(), seqowner)) - return COPYSEQ_SUBSCRIBER_INSUFFICIENT_PERM; + return COPYSEQ_SUBSCRIBER_NO_SETROLE; SwitchToUntrustedUser(seqowner, &ucxt); } @@ -515,6 +532,7 @@ copy_sequences(WalReceiverConn *conn) List *mismatched_seqs_idx = NIL; List *missing_seqs_idx = NIL; List *sub_insuffperm_seqs_idx = NIL; + List *sub_no_setrole_seqs_idx = NIL; List *pub_insuffperm_seqs_idx = NIL; StringInfoData seqstr; StringInfoData cmd; @@ -686,6 +704,19 @@ copy_sequences(WalReceiverConn *conn) MemoryContextSwitchTo(oldctx); batch_sub_insuffperm_count++; break; + case COPYSEQ_SUBSCRIBER_NO_SETROLE: + + /* + * Remember sequences whose owner cannot be switched to in + * a long-lived memory context since these will be used + * after the transaction is committed. + */ + oldctx = MemoryContextSwitchTo(ApplyContext); + sub_no_setrole_seqs_idx = lappend_int(sub_no_setrole_seqs_idx, + seqidx); + MemoryContextSwitchTo(oldctx); + batch_sub_insuffperm_count++; + break; case COPYSEQ_PUBLISHER_INSUFFICIENT_PERM: /* @@ -778,7 +809,8 @@ copy_sequences(WalReceiverConn *conn) /* Report mismatches, permission issues, or missing sequences */ report_sequence_errors(mismatched_seqs_idx, sub_insuffperm_seqs_idx, - pub_insuffperm_seqs_idx, missing_seqs_idx); + sub_no_setrole_seqs_idx, pub_insuffperm_seqs_idx, + missing_seqs_idx); } /* diff --git a/src/test/subscription/t/036_sequences.pl b/src/test/subscription/t/036_sequences.pl index 361b32f950b..159a4b739cf 100644 --- a/src/test/subscription/t/036_sequences.pl +++ b/src/test/subscription/t/036_sequences.pl @@ -415,7 +415,7 @@ $node_subscriber->safe_psql('postgres', "ALTER SUBSCRIPTION regress_seq_sub REFRESH SEQUENCES"); $node_subscriber->wait_for_log( - qr/WARNING: ( [A-Z0-9]+:)? insufficient privileges on subscriber sequence \("public.regress_s4"\)/, + qr/WARNING: ( [A-Z0-9]+:)? insufficient privileges on subscriber sequence \("public.regress_s4"\)\n.*HINT: Grant the sequence owner role with the SET option to the subscription owner on the subscriber\./, $log_offset); # Check - the other sequences are synced and marked as ready -- 2.50.1 (Apple Git-155)