From 429e9fbcd5c623684ee2efc38f2d18d026c49bab Mon Sep 17 00:00:00 2001
From: Jim Jones <jim.jones@uni-muenster.de>
Date: Sat, 10 Oct 2026 22:45:57 +0200
Subject: [PATCH v7 2/2] Log DROP DATABASE LSN before the database is marked
 invalid

---
 src/backend/commands/dbcommands.c | 24 +++++++++++++++---------
 1 file changed, 15 insertions(+), 9 deletions(-)

diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index 3184fd34574..9b570e66f3b 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -1729,6 +1729,7 @@ dropdb(const char *dbname, bool missing_ok, bool force)
 	int			nslots,
 				nslots_active;
 	int			nsubscriptions;
+	XLogRecPtr	drop_lsn = InvalidXLogRecPtr;
 
 	/*
 	 * Look up the target database's OID, and get exclusive lock on it. We
@@ -1860,6 +1861,16 @@ dropdb(const char *dbname, bool missing_ok, bool force)
 	 */
 	pgstat_drop_database(db_id);
 
+	/*
+	 * Capture the recovery target LSN for log_object_drops before the
+	 * non-transactional in-place update below.  Everything WAL-logged before
+	 * this point is transactional, so recovering up to (but not including)
+	 * this LSN yields a fully valid database.  Any later LSN would replay the
+	 * invalid marker set below.
+	 */
+	if (log_object_drops)
+		drop_lsn = GetXLogInsertRecPtr();
+
 	/*
 	 * Except for the deletion of the catalog row, subsequent actions are not
 	 * transactional (consider DropDatabaseBuffers() discarding modified
@@ -1893,19 +1904,14 @@ dropdb(const char *dbname, bool missing_ok, bool force)
 	CatalogTupleDelete(pgdbrel, &tup->t_self);
 	heap_freetuple(tup);
 
-	/* Log LSN after database drop operation completes */
+	/* Log the LSN captured before the database was marked invalid */
 	if (log_object_drops)
-	{
-		XLogRecPtr	current_lsn = GetXLogInsertRecPtr();
-
 		ereport(LOG,
 				(errmsg("database \"%s\" (OID %u) dropped, lsn=%X/%08X",
-						dbname, db_id, LSN_FORMAT_ARGS(current_lsn)),
+						dbname, db_id, LSN_FORMAT_ARGS(drop_lsn)),
 				 errhint("To recover to the point before this drop, use recovery_target_lsn = '%X/%08X' "
-						 "with recovery_target_inclusive = false. See the log_object_drops "
-						 "documentation for caveats and required manual cleanup steps.",
-						 LSN_FORMAT_ARGS(current_lsn))));
-	}
+						 "with recovery_target_inclusive = false.",
+						 LSN_FORMAT_ARGS(drop_lsn))));
 
 	/*
 	 * Drop db-specific replication slots.
-- 
2.56.0

