From 78a572bf0c7ba7ec3a4558fd88870f8fdff30dac Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Sat, 12 Mar 2005 21:33:55 +0000
Subject: [PATCH] When cloning template0 (or other fully-frozen databases), set
 the new database's datallowconn and datfrozenxid to the current transaction
 ID instead of copying the source database's values.  This is OK because we
 assume the source DB contains no normal transaction IDs whatsoever. This
 keeps VACUUM from immediately starting to complain about unvacuumed databases
 in the situation where we are more than 2 billion transactions out from the
 XID stamp of template0.  Per discussion with Milen Radev (although his
 complaint turned out to be due to something else, but the problem is real
 anyway).

---
 src/backend/commands/dbcommands.c | 32 ++++++++++++++++++++++++-------
 1 file changed, 25 insertions(+), 7 deletions(-)

diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index a076ff4c9c..f3327b1fb2 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.153 2005/03/12 21:11:50 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.154 2005/03/12 21:33:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -54,7 +54,8 @@
 
 /* non-export function prototypes */
 static bool get_db_info(const char *name, Oid *dbIdP, int4 *ownerIdP,
-			int *encodingP, bool *dbIsTemplateP, Oid *dbLastSysOidP,
+			int *encodingP, bool *dbIsTemplateP, bool *dbAllowConnP,
+			Oid *dbLastSysOidP,
 			TransactionId *dbVacuumXidP, TransactionId *dbFrozenXidP,
 			Oid *dbTablespace);
 static bool have_createdb_privilege(void);
@@ -73,6 +74,7 @@ createdb(const CreatedbStmt *stmt)
 	AclId		src_owner;
 	int			src_encoding;
 	bool		src_istemplate;
+	bool		src_allowconn;
 	Oid			src_lastsysoid;
 	TransactionId src_vacuumxid;
 	TransactionId src_frozenxid;
@@ -217,7 +219,8 @@ createdb(const CreatedbStmt *stmt)
 	 * idea, so accept possibility of race to create.  We will check again
 	 * after we grab the exclusive lock.
 	 */
-	if (get_db_info(dbname, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL))
+	if (get_db_info(dbname, NULL, NULL, NULL,
+					NULL, NULL, NULL, NULL, NULL, NULL))
 		ereport(ERROR,
 				(errcode(ERRCODE_DUPLICATE_DATABASE),
 				 errmsg("database \"%s\" already exists", dbname)));
@@ -229,7 +232,7 @@ createdb(const CreatedbStmt *stmt)
 		dbtemplate = "template1";		/* Default template database name */
 
 	if (!get_db_info(dbtemplate, &src_dboid, &src_owner, &src_encoding,
-					 &src_istemplate, &src_lastsysoid,
+					 &src_istemplate, &src_allowconn, &src_lastsysoid,
 					 &src_vacuumxid, &src_frozenxid, &src_deftablespace))
 		ereport(ERROR,
 				(errcode(ERRCODE_UNDEFINED_DATABASE),
@@ -328,6 +331,16 @@ createdb(const CreatedbStmt *stmt)
 		/* Note there is no additional permission check in this path */
 	}
 
+	/*
+	 * Normally we mark the new database with the same datvacuumxid and
+	 * datfrozenxid as the source.  However, if the source is not allowing
+	 * connections then we assume it is fully frozen, and we can set the
+	 * current transaction ID as the xid limits.  This avoids immediately
+	 * starting to generate warnings after cloning template0.
+	 */
+	if (!src_allowconn)
+		src_vacuumxid = src_frozenxid = GetCurrentTransactionId();
+
 	/*
 	 * Preassign OID for pg_database tuple, so that we can compute db
 	 * path.
@@ -455,7 +468,8 @@ createdb(const CreatedbStmt *stmt)
 	pg_database_rel = heap_openr(DatabaseRelationName, ExclusiveLock);
 
 	/* Check to see if someone else created same DB name meanwhile. */
-	if (get_db_info(dbname, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL))
+	if (get_db_info(dbname, NULL, NULL, NULL,
+					NULL, NULL, NULL, NULL, NULL, NULL))
 	{
 		/* Don't hold lock while doing recursive remove */
 		heap_close(pg_database_rel, ExclusiveLock);
@@ -552,7 +566,7 @@ dropdb(const char *dbname)
 	pgdbrel = heap_openr(DatabaseRelationName, ExclusiveLock);
 
 	if (!get_db_info(dbname, &db_id, &db_owner, NULL,
-					 &db_istemplate, NULL, NULL, NULL, NULL))
+					 &db_istemplate, NULL, NULL, NULL, NULL, NULL))
 		ereport(ERROR,
 				(errcode(ERRCODE_UNDEFINED_DATABASE),
 				 errmsg("database \"%s\" does not exist", dbname)));
@@ -936,7 +950,8 @@ AlterDatabaseOwner(const char *dbname, AclId newOwnerSysId)
 
 static bool
 get_db_info(const char *name, Oid *dbIdP, int4 *ownerIdP,
-			int *encodingP, bool *dbIsTemplateP, Oid *dbLastSysOidP,
+			int *encodingP, bool *dbIsTemplateP, bool *dbAllowConnP,
+			Oid *dbLastSysOidP,
 			TransactionId *dbVacuumXidP, TransactionId *dbFrozenXidP,
 			Oid *dbTablespace)
 {
@@ -978,6 +993,9 @@ get_db_info(const char *name, Oid *dbIdP, int4 *ownerIdP,
 		/* allowed as template? */
 		if (dbIsTemplateP)
 			*dbIsTemplateP = dbform->datistemplate;
+		/* allowing connections? */
+		if (dbAllowConnP)
+			*dbAllowConnP = dbform->datallowconn;
 		/* last system OID used in database */
 		if (dbLastSysOidP)
 			*dbLastSysOidP = dbform->datlastsysoid;
-- 
2.24.1