From bb6b72a6cf1ab672fc2706eb25e479618f7d66d0 Mon Sep 17 00:00:00 2001
From: Peter Smith <peter.b.smith@fujitsu.com>
Date: Mon, 20 Oct 2025 18:13:19 +1100
Subject: [PATCH v1] fix wal_level equality checks and messages

---
 src/backend/access/heap/heapam.c            | 2 +-
 src/backend/commands/publicationcmds.c      | 4 ++--
 src/backend/utils/cache/inval.c             | 4 ++--
 src/bin/pg_basebackup/pg_createsubscriber.c | 2 +-
 src/bin/pg_upgrade/check.c                  | 2 +-
 src/test/regress/expected/publication.out   | 4 ++--
 6 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 5686963..dcf0279 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -8891,7 +8891,7 @@ log_heap_update(Relation reln, Buffer oldbuf,
 	 *
 	 * Skip this if we're taking a full-page image of the new page, as we
 	 * don't include the new tuple in the WAL record in that case.  Also
-	 * disable if wal_level='logical', as logical decoding needs to be able to
+	 * disable if wal_level >= logical, as logical decoding needs to be able to
 	 * read the new tuple in whole from the WAL record alone.
 	 */
 	if (oldbuf == newbuf && !need_tuple_data &&
diff --git a/src/backend/commands/publicationcmds.c b/src/backend/commands/publicationcmds.c
index 1faf3a8..efe3d13 100644
--- a/src/backend/commands/publicationcmds.c
+++ b/src/backend/commands/publicationcmds.c
@@ -975,11 +975,11 @@ CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
 
 	InvokeObjectPostCreateHook(PublicationRelationId, puboid, 0);
 
-	if (wal_level != WAL_LEVEL_LOGICAL)
+	if (wal_level < WAL_LEVEL_LOGICAL)
 		ereport(WARNING,
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 				 errmsg("\"wal_level\" is insufficient to publish logical changes"),
-				 errhint("Set \"wal_level\" to \"logical\" before creating subscriptions.")));
+				 errhint("Set \"wal_level\" >= \"logical\" before creating subscriptions.")));
 
 	return myself;
 }
diff --git a/src/backend/utils/cache/inval.c b/src/backend/utils/cache/inval.c
index 02505c8..2b2e995 100644
--- a/src/backend/utils/cache/inval.c
+++ b/src/backend/utils/cache/inval.c
@@ -98,7 +98,7 @@
  *	likewise send the invalidation immediately, before ending the change's
  *	critical section.  This includes inplace heap updates, relmap, and smgr.
  *
- *	When wal_level=logical, write invalidations into WAL at each command end to
+ *	When wal_level>=logical, write invalidations into WAL at each command end to
  *	support the decoding of the in-progress transactions.  See
  *	CommandEndInvalidationMessages.
  *
@@ -1419,7 +1419,7 @@ CommandEndInvalidationMessages(void)
 	ProcessInvalidationMessages(&transInvalInfo->ii.CurrentCmdInvalidMsgs,
 								LocalExecuteInvalidationMessage);
 
-	/* WAL Log per-command invalidation messages for wal_level=logical */
+	/* WAL Log per-command invalidation messages for wal_level>=logical */
 	if (XLogLogicalInfoActive())
 		LogLogicalInvalidations();
 
diff --git a/src/bin/pg_basebackup/pg_createsubscriber.c b/src/bin/pg_basebackup/pg_createsubscriber.c
index b33566e..a53305e 100644
--- a/src/bin/pg_basebackup/pg_createsubscriber.c
+++ b/src/bin/pg_basebackup/pg_createsubscriber.c
@@ -897,7 +897,7 @@ check_publisher(const struct LogicalRepInfo *dbinfo)
 	 * Since these parameters are not a requirement for physical replication,
 	 * we should check it to make sure it won't fail.
 	 *
-	 * - wal_level = logical
+	 * - wal_level >= logical
 	 * - max_replication_slots >= current + number of dbs to be converted
 	 * - max_wal_senders >= current + number of dbs to be converted
 	 * - max_slot_wal_keep_size = -1 (to prevent deletion of required WAL files)
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 1e17d64..ddc89ac 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -2132,7 +2132,7 @@ check_new_cluster_replication_slots(void)
 	wal_level = PQgetvalue(res, 0, 0);
 
 	if (nslots_on_old > 0 && strcmp(wal_level, "logical") != 0)
-		pg_fatal("\"wal_level\" must be \"logical\" but is set to \"%s\"",
+		pg_fatal("\"wal_level\" must be \"logical\" or higher but is set to \"%s\"",
 				 wal_level);
 
 	if (old_cluster.sub_retain_dead_tuples &&
diff --git a/src/test/regress/expected/publication.out b/src/test/regress/expected/publication.out
index e72d130..6a40cba 100644
--- a/src/test/regress/expected/publication.out
+++ b/src/test/regress/expected/publication.out
@@ -289,11 +289,11 @@ SET client_min_messages = 'NOTICE';
 CREATE PUBLICATION regress_pub_for_allsequences_alltables_withclause FOR ALL SEQUENCES, ALL TABLES WITH (publish = 'insert');
 NOTICE:  publication parameters are not applicable to sequence synchronization and will be ignored for sequences
 WARNING:  "wal_level" is insufficient to publish logical changes
-HINT:  Set "wal_level" to "logical" before creating subscriptions.
+HINT:  Set "wal_level" >= "logical" before creating subscriptions.
 CREATE PUBLICATION regress_pub_for_allsequences_withclause FOR ALL SEQUENCES WITH (publish_generated_columns = 'stored');
 NOTICE:  publication parameters are not applicable to sequence synchronization and will be ignored for sequences
 WARNING:  "wal_level" is insufficient to publish logical changes
-HINT:  Set "wal_level" to "logical" before creating subscriptions.
+HINT:  Set "wal_level" >= "logical" before creating subscriptions.
 RESET client_min_messages;
 SELECT pubname, puballtables, puballsequences FROM pg_publication WHERE pubname = 'regress_pub_for_allsequences_alltables';
                 pubname                 | puballtables | puballsequences 
-- 
1.8.3.1

