From def30e84c41389225ee9e56cb7c722980bab9746 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Wed, 20 Oct 2010 12:48:51 -0400
Subject: [PATCH] Don't try to fetch database name when SetTransactionIdLimit()
 is executed outside a transaction.

This repairs brain fade in my patch of 2009-08-30: the reason we had been
storing oldest-database name, not OID, in ShmemVariableCache was of course
to avoid having to do a catalog lookup at times when it might be unsafe.

This error explains why Aleksandr Dushein is having trouble getting out of
an XID wraparound state in bug #5718, though not how he got into that state
in the first place.  I suspect pg_upgrade is at fault there.
---
 src/backend/access/transam/varsup.c | 18 ++++++++++++++----
 1 file changed, 14 insertions(+), 4 deletions(-)

diff --git a/src/backend/access/transam/varsup.c b/src/backend/access/transam/varsup.c
index 391724ea0fe..6f423bcf3a3 100644
--- a/src/backend/access/transam/varsup.c
+++ b/src/backend/access/transam/varsup.c
@@ -16,6 +16,7 @@
 #include "access/clog.h"
 #include "access/subtrans.h"
 #include "access/transam.h"
+#include "access/xact.h"
 #include "commands/dbcommands.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -346,13 +347,22 @@ SetTransactionIdLimit(TransactionId oldest_datfrozenxid, Oid oldest_datoid)
 	/* Give an immediate warning if past the wrap warn point */
 	if (TransactionIdFollowsOrEquals(curXid, xidWarnLimit) && !InRecovery)
 	{
-		char	   *oldest_datname = get_database_name(oldest_datoid);
+		char	   *oldest_datname;
 
 		/*
-		 * Note: it's possible that get_database_name fails and returns NULL,
-		 * for example because the database just got dropped.  We'll still
-		 * warn, even though the warning might now be unnecessary.
+		 * We can be called when not inside a transaction, for example
+		 * during StartupXLOG().  In such a case we cannot do database
+		 * access, so we must just report the oldest DB's OID.
+		 *
+		 * Note: it's also possible that get_database_name fails and returns
+		 * NULL, for example because the database just got dropped.  We'll
+		 * still warn, even though the warning might now be unnecessary.
 		 */
+		if (IsTransactionState())
+			oldest_datname = get_database_name(oldest_datoid);
+		else
+			oldest_datname = NULL;
+
 		if (oldest_datname)
 			ereport(WARNING,
 			(errmsg("database \"%s\" must be vacuumed within %u transactions",
-- 
GitLab