*** a/contrib/pg_upgrade/pg_upgrade.c
--- b/contrib/pg_upgrade/pg_upgrade.c
***************
*** 519,527 **** set_frozenxids(void)
  		 */
  		if (strcmp(datallowconn, "f") == 0)
  			PQclear(executeQueryOrDie(conn_template1,
! 									  "UPDATE pg_catalog.pg_database "
! 									  "SET	datallowconn = true "
! 									  "WHERE datname = '%s'", datname));
  
  		conn = connectToServer(&new_cluster, datname);
  
--- 519,526 ----
  		 */
  		if (strcmp(datallowconn, "f") == 0)
  			PQclear(executeQueryOrDie(conn_template1,
! 									  "ALTER DATABASE %s ALLOW_CONNECTIONS = true",
! 									  datname));
  
  		conn = connectToServer(&new_cluster, datname);
  
***************
*** 537,545 **** set_frozenxids(void)
  		/* Reset datallowconn flag */
  		if (strcmp(datallowconn, "f") == 0)
  			PQclear(executeQueryOrDie(conn_template1,
! 									  "UPDATE pg_catalog.pg_database "
! 									  "SET	datallowconn = false "
! 									  "WHERE datname = '%s'", datname));
  	}
  
  	PQclear(dbres);
--- 536,543 ----
  		/* Reset datallowconn flag */
  		if (strcmp(datallowconn, "f") == 0)
  			PQclear(executeQueryOrDie(conn_template1,
! 									  "ALTER DATABASE %s ALLOW_CONNECTIONS = false",
! 									  datname));
  	}
  
  	PQclear(dbres);
*** a/doc/src/sgml/ref/alter_database.sgml
--- b/doc/src/sgml/ref/alter_database.sgml
***************
*** 25,30 **** ALTER DATABASE <replaceable class="PARAMETER">name</replaceable> [ [ WITH ] <rep
--- 25,32 ----
  
  <phrase>where <replaceable class="PARAMETER">option</replaceable> can be:</phrase>
  
+     IS_TEMPLATE <replaceable class="PARAMETER">istemplate</replaceable>
+     ALLOW_CONNECTIONS <replaceable class="PARAMETER">allowconn</replaceable>
      CONNECTION_LIMIT <replaceable class="PARAMETER">connection_limit</replaceable>
  
  ALTER DATABASE <replaceable class="PARAMETER">name</replaceable> RENAME TO <replaceable>new_name</replaceable>
***************
*** 107,112 **** ALTER DATABASE <replaceable class="PARAMETER">name</replaceable> RESET ALL
--- 109,134 ----
       </varlistentry>
  
       <varlistentry>
+        <term><replaceable class="parameter">istemplate</replaceable></term>
+        <listitem>
+         <para>
+          If true, then this database can be cloned by any user with CREATEDB
+          privileges; if false, then only superusers or the owner of the
+          database can clone it.
+         </para>
+        </listitem>
+       </varlistentry>
+  
+       <varlistentry>
+        <term><replaceable class="parameter">allowconn</replaceable></term>
+        <listitem>
+         <para>
+          If false then no one can connect to this database.
+         </para>
+        </listitem>
+       </varlistentry>
+  
+       <varlistentry>
        <term><replaceable class="parameter">connection_limit</replaceable></term>
        <listitem>
         <para>
*** a/doc/src/sgml/ref/create_database.sgml
--- b/doc/src/sgml/ref/create_database.sgml
***************
*** 28,33 **** CREATE DATABASE <replaceable class="PARAMETER">name</replaceable>
--- 28,35 ----
             [ LC_COLLATE [=] <replaceable class="parameter">lc_collate</replaceable> ]
             [ LC_CTYPE [=] <replaceable class="parameter">lc_ctype</replaceable> ]
             [ TABLESPACE [=] <replaceable class="parameter">tablespace_name</replaceable> ]
+            [ IS_TEMPLATE [=] <replaceable class="parameter">istemplate</replaceable>]
+            [ ALLOW_CONNECTIONS [=] <replaceable class="parameter">allowconn</replaceable>]
             [ CONNECTION_LIMIT [=] <replaceable class="parameter">connection_limit</replaceable> ] ]
  </synopsis>
   </refsynopsisdiv>
***************
*** 148,153 **** CREATE DATABASE <replaceable class="PARAMETER">name</replaceable>
--- 150,175 ----
       </varlistentry>
  
       <varlistentry>
+        <term><replaceable class="parameter">istemplate</replaceable></term>
+        <listitem>
+         <para>
+          If true, then this database can be cloned by any user with CREATEDB
+          privileges; if false, then only superusers or the owner of the
+          database can clone it.
+         </para>
+        </listitem>
+       </varlistentry>
+  
+       <varlistentry>
+        <term><replaceable class="parameter">allowconn</replaceable></term>
+        <listitem>
+         <para>
+          If false then no one can connect to this database.
+         </para>
+        </listitem>
+       </varlistentry>
+  
+       <varlistentry>
        <term><replaceable class="parameter">connection_limit</replaceable></term>
        <listitem>
         <para>
*** a/src/backend/commands/dbcommands.c
--- b/src/backend/commands/dbcommands.c
***************
*** 123,128 **** createdb(const CreatedbStmt *stmt)
--- 123,130 ----
  	DefElem    *dencoding = NULL;
  	DefElem    *dcollate = NULL;
  	DefElem    *dctype = NULL;
+ 	DefElem	   *distemplate = NULL;
+ 	DefElem	   *dallowconnections = NULL;
  	DefElem    *dconnlimit = NULL;
  	char	   *dbname = stmt->dbname;
  	char	   *dbowner = NULL;
***************
*** 131,136 **** createdb(const CreatedbStmt *stmt)
--- 133,140 ----
  	char	   *dbctype = NULL;
  	char	   *canonname;
  	int			encoding = -1;
+ 	bool		dbistemplate = false;
+ 	bool		dballowconnections = true;
  	int			dbconnlimit = -1;
  	int			notherbackends;
  	int			npreparedxacts;
***************
*** 189,194 **** createdb(const CreatedbStmt *stmt)
--- 193,214 ----
  						 errmsg("conflicting or redundant options")));
  			dctype = defel;
  		}
+ 		else if (strcmp(defel->defname, "is_template") == 0)
+ 		{
+ 			if (distemplate)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						 errmsg("conflicting or redundant options")));
+ 			distemplate = defel;
+ 		}
+ 		else if (strcmp(defel->defname, "allow_connections") == 0)
+ 		{
+ 			if (dallowconnections)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						 errmsg("conflicting or redundant options")));
+ 			dallowconnections = defel;
+ 		}
  		else if (strcmp(defel->defname, "connection_limit") == 0)
  		{
  			if (dconnlimit)
***************
*** 248,253 **** createdb(const CreatedbStmt *stmt)
--- 268,277 ----
  	if (dctype)
  		dbctype = defGetString(dctype);
  
+ 	if (distemplate)
+ 		dbistemplate = defGetBoolean(distemplate);
+ 	if (dallowconnections)
+ 		dballowconnections = defGetBoolean(dallowconnections);
  	if (dconnlimit)
  	{
  		dbconnlimit = defGetInt32(dconnlimit);
***************
*** 490,497 **** createdb(const CreatedbStmt *stmt)
  		DirectFunctionCall1(namein, CStringGetDatum(dbcollate));
  	new_record[Anum_pg_database_datctype - 1] =
  		DirectFunctionCall1(namein, CStringGetDatum(dbctype));
! 	new_record[Anum_pg_database_datistemplate - 1] = BoolGetDatum(false);
! 	new_record[Anum_pg_database_datallowconn - 1] = BoolGetDatum(true);
  	new_record[Anum_pg_database_datconnlimit - 1] = Int32GetDatum(dbconnlimit);
  	new_record[Anum_pg_database_datlastsysoid - 1] = ObjectIdGetDatum(src_lastsysoid);
  	new_record[Anum_pg_database_datfrozenxid - 1] = TransactionIdGetDatum(src_frozenxid);
--- 514,521 ----
  		DirectFunctionCall1(namein, CStringGetDatum(dbcollate));
  	new_record[Anum_pg_database_datctype - 1] =
  		DirectFunctionCall1(namein, CStringGetDatum(dbctype));
! 	new_record[Anum_pg_database_datistemplate - 1] = BoolGetDatum(dbistemplate);
! 	new_record[Anum_pg_database_datallowconn - 1] = BoolGetDatum(dballowconnections);
  	new_record[Anum_pg_database_datconnlimit - 1] = Int32GetDatum(dbconnlimit);
  	new_record[Anum_pg_database_datlastsysoid - 1] = ObjectIdGetDatum(src_lastsysoid);
  	new_record[Anum_pg_database_datfrozenxid - 1] = TransactionIdGetDatum(src_frozenxid);
***************
*** 1331,1337 **** AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
  	ScanKeyData scankey;
  	SysScanDesc scan;
  	ListCell   *option;
! 	int			connlimit = -1;
  	DefElem    *dconnlimit = NULL;
  	DefElem    *dtablespace = NULL;
  	Datum		new_record[Natts_pg_database];
--- 1355,1365 ----
  	ScanKeyData scankey;
  	SysScanDesc scan;
  	ListCell   *option;
! 	bool		dbistemplate = false;
! 	bool		dballowconnections = true;
! 	int			dbconnlimit = -1;
! 	DefElem	   *distemplate = NULL;
! 	DefElem	   *dallowconnections = NULL;
  	DefElem    *dconnlimit = NULL;
  	DefElem    *dtablespace = NULL;
  	Datum		new_record[Natts_pg_database];
***************
*** 1343,1349 **** AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
  	{
  		DefElem    *defel = (DefElem *) lfirst(option);
  
! 		if (strcmp(defel->defname, "connection_limit") == 0)
  		{
  			if (dconnlimit)
  				ereport(ERROR,
--- 1371,1393 ----
  	{
  		DefElem    *defel = (DefElem *) lfirst(option);
  
! 		if (strcmp(defel->defname, "is_template") == 0)
! 		{
! 			if (distemplate)
! 				ereport(ERROR,
! 						(errcode(ERRCODE_SYNTAX_ERROR),
! 						 errmsg("conflicting or redundant options")));
! 			distemplate = defel;
! 		}
! 		else if (strcmp(defel->defname, "allow_connections") == 0)
! 		{
! 			if (dallowconnections)
! 				ereport(ERROR,
! 						(errcode(ERRCODE_SYNTAX_ERROR),
! 						 errmsg("conflicting or redundant options")));
! 			dallowconnections = defel;
! 		}
! 		else if (strcmp(defel->defname, "connection_limit") == 0)
  		{
  			if (dconnlimit)
  				ereport(ERROR,
***************
*** 1367,1386 **** AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
  	if (dtablespace)
  	{
  		/* currently, can't be specified along with any other options */
! 		Assert(!dconnlimit);
  		/* this case isn't allowed within a transaction block */
  		PreventTransactionChain(isTopLevel, "ALTER DATABASE SET TABLESPACE");
  		movedb(stmt->dbname, strVal(dtablespace->arg));
  		return InvalidOid;
  	}
  
  	if (dconnlimit)
  	{
! 		connlimit = defGetInt32(dconnlimit);
! 		if (connlimit < -1)
  			ereport(ERROR,
  					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! 					 errmsg("invalid connection limit: %d", connlimit)));
  	}
  
  	/*
--- 1411,1434 ----
  	if (dtablespace)
  	{
  		/* currently, can't be specified along with any other options */
! 		Assert(!distemplate && !dallowconnections && !dconnlimit);
  		/* this case isn't allowed within a transaction block */
  		PreventTransactionChain(isTopLevel, "ALTER DATABASE SET TABLESPACE");
  		movedb(stmt->dbname, strVal(dtablespace->arg));
  		return InvalidOid;
  	}
  
+ 	if (distemplate)
+ 		dbistemplate = defGetBoolean(distemplate);
+ 	if (dallowconnections)
+ 		dballowconnections = defGetBoolean(dallowconnections);
  	if (dconnlimit)
  	{
! 		dbconnlimit = defGetInt32(dconnlimit);
! 		if (dbconnlimit < -1)
  			ereport(ERROR,
  					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! 					 errmsg("invalid connection limit: %d", dbconnlimit)));
  	}
  
  	/*
***************
*** 1408,1422 **** AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
  					   stmt->dbname);
  
  	/*
  	 * Build an updated tuple, perusing the information just obtained
  	 */
  	MemSet(new_record, 0, sizeof(new_record));
  	MemSet(new_record_nulls, false, sizeof(new_record_nulls));
  	MemSet(new_record_repl, false, sizeof(new_record_repl));
  
  	if (dconnlimit)
  	{
! 		new_record[Anum_pg_database_datconnlimit - 1] = Int32GetDatum(connlimit);
  		new_record_repl[Anum_pg_database_datconnlimit - 1] = true;
  	}
  
--- 1456,1491 ----
  					   stmt->dbname);
  
  	/*
+ 	 * In order to avoid getting locked out and having to go through standalone
+ 	 * mode, we refuse to disallow connections on the database we're currently
+ 	 * connected to.  Lockout can still happen with concurrent sessions but the
+ 	 * likeliness of that is not high enough to worry about.
+ 	 */
+ 	if (!dballowconnections && dboid == MyDatabaseId)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 				 errmsg("cannot disallow connections for current database")));
+ 
+ 	/*
  	 * Build an updated tuple, perusing the information just obtained
  	 */
  	MemSet(new_record, 0, sizeof(new_record));
  	MemSet(new_record_nulls, false, sizeof(new_record_nulls));
  	MemSet(new_record_repl, false, sizeof(new_record_repl));
  
+ 	if (distemplate)
+ 	{
+ 		new_record[Anum_pg_database_datistemplate - 1] = BoolGetDatum(dbistemplate);
+ 		new_record_repl[Anum_pg_database_datistemplate - 1] = true;
+ 	}
+ 	if (dallowconnections)
+ 	{
+ 		new_record[Anum_pg_database_datallowconn - 1] = BoolGetDatum(dballowconnections);
+ 		new_record_repl[Anum_pg_database_datallowconn - 1] = true;
+ 	}
  	if (dconnlimit)
  	{
! 		new_record[Anum_pg_database_datconnlimit - 1] = Int32GetDatum(dbconnlimit);
  		new_record_repl[Anum_pg_database_datconnlimit - 1] = true;
  	}
  
*** a/src/bin/initdb/initdb.c
--- b/src/bin/initdb/initdb.c
***************
*** 2288,2298 **** make_template0(void)
  	PG_CMD_DECL;
  	const char **line;
  	static const char *template0_setup[] = {
! 		"CREATE DATABASE template0;\n",
! 		"UPDATE pg_database SET "
! 		"	datistemplate = 't', "
! 		"	datallowconn = 'f' "
! 		"    WHERE datname = 'template0';\n",
  
  		/*
  		 * We use the OID of template0 to determine lastsysoid
--- 2288,2294 ----
  	PG_CMD_DECL;
  	const char **line;
  	static const char *template0_setup[] = {
! 		"CREATE DATABASE template0 IS_TEMPLATE = true ALLOW_CONNECTIONS = false;\n",
  
  		/*
  		 * We use the OID of template0 to determine lastsysoid
*** a/src/bin/pg_dump/pg_dumpall.c
--- b/src/bin/pg_dump/pg_dumpall.c
***************
*** 1374,1392 **** dumpCreateDB(PGconn *conn)
  				appendPQExpBuffer(buf, " TABLESPACE = %s",
  								  fmtId(dbtablespace));
  
  			if (strcmp(dbconnlimit, "-1") != 0)
  				appendPQExpBuffer(buf, " CONNECTION_LIMIT = %s",
  								  dbconnlimit);
  
  			appendPQExpBufferStr(buf, ";\n");
  
- 			if (strcmp(dbistemplate, "t") == 0)
- 			{
- 				appendPQExpBufferStr(buf, "UPDATE pg_catalog.pg_database SET datistemplate = 't' WHERE datname = ");
- 				appendStringLiteralConn(buf, dbname, conn);
- 				appendPQExpBufferStr(buf, ";\n");
- 			}
- 
  			if (binary_upgrade)
  			{
  				appendPQExpBufferStr(buf, "-- For binary upgrade, set datfrozenxid.\n");
--- 1374,1388 ----
  				appendPQExpBuffer(buf, " TABLESPACE = %s",
  								  fmtId(dbtablespace));
  
+ 			if (strcmp(dbistemplate, "t") == 0)
+ 				appendPQExpBuffer(buf, " IS_TEMPLATE = true");
+ 
  			if (strcmp(dbconnlimit, "-1") != 0)
  				appendPQExpBuffer(buf, " CONNECTION_LIMIT = %s",
  								  dbconnlimit);
  
  			appendPQExpBufferStr(buf, ";\n");
  
  			if (binary_upgrade)
  			{
  				appendPQExpBufferStr(buf, "-- For binary upgrade, set datfrozenxid.\n");
*** a/src/bin/psql/tab-complete.c
--- b/src/bin/psql/tab-complete.c
***************
*** 1004,1010 **** psql_completion(const char *text, int start, int end)
  			 pg_strcasecmp(prev2_wd, "DATABASE") == 0)
  	{
  		static const char *const list_ALTERDATABASE[] =
! 		{"RESET", "SET", "OWNER TO", "RENAME TO", "CONNECTION_LIMIT", NULL};
  
  		COMPLETE_WITH_LIST(list_ALTERDATABASE);
  	}
--- 1004,1011 ----
  			 pg_strcasecmp(prev2_wd, "DATABASE") == 0)
  	{
  		static const char *const list_ALTERDATABASE[] =
! 		{"RESET", "SET", "OWNER TO", "RENAME TO", "IS_TEMPLATE",
! 		"ALLOW_CONNECTIONS", "CONNECTION_LIMIT", NULL};
  
  		COMPLETE_WITH_LIST(list_ALTERDATABASE);
  	}
***************
*** 2045,2052 **** psql_completion(const char *text, int start, int end)
  			 pg_strcasecmp(prev2_wd, "DATABASE") == 0)
  	{
  		static const char *const list_DATABASE[] =
! 		{"OWNER", "TEMPLATE", "ENCODING", "TABLESPACE", "CONNECTION_LIMIT",
! 		NULL};
  
  		COMPLETE_WITH_LIST(list_DATABASE);
  	}
--- 2046,2053 ----
  			 pg_strcasecmp(prev2_wd, "DATABASE") == 0)
  	{
  		static const char *const list_DATABASE[] =
! 		{"OWNER", "TEMPLATE", "ENCODING", "TABLESPACE", "IS_TEMPLATE",
! 		"ALLOW_CONNECTIONS", "CONNECTION_LIMIT", NULL};
  
  		COMPLETE_WITH_LIST(list_DATABASE);
  	}
