*** 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">connlimit</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">connlimit</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">connlimit</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">connlimit</replaceable></term>
        <listitem>
         <para>
*** a/src/backend/commands/dbcommands.c
--- b/src/backend/commands/dbcommands.c
***************
*** 39,44 ****
--- 39,45 ----
  #include "catalog/pg_tablespace.h"
  #include "commands/comment.h"
  #include "commands/dbcommands.h"
+ #include "commands/defrem.h"
  #include "commands/seclabel.h"
  #include "commands/tablespace.h"
  #include "mb/pg_wchar.h"
***************
*** 122,127 **** createdb(const CreatedbStmt *stmt)
--- 123,130 ----
  	DefElem    *dencoding = NULL;
  	DefElem    *dcollate = NULL;
  	DefElem    *dctype = NULL;
+ 	DefElem    *distemplate = NULL;
+ 	DefElem	   *dallowconn = NULL;
  	DefElem    *dconnlimit = NULL;
  	char	   *dbname = stmt->dbname;
  	char	   *dbowner = NULL;
***************
*** 130,135 **** createdb(const CreatedbStmt *stmt)
--- 133,140 ----
  	char	   *dbctype = NULL;
  	char	   *canonname;
  	int			encoding = -1;
+ 	bool	    istemplate = false;
+ 	bool	    allowconn = true;
  	int			dbconnlimit = -1;
  	int			notherbackends;
  	int			npreparedxacts;
***************
*** 188,193 **** createdb(const CreatedbStmt *stmt)
--- 193,214 ----
  						 errmsg("conflicting or redundant options")));
  			dctype = defel;
  		}
+ 		else if (strcmp(defel->defname, "istemplate") == 0)
+ 		{
+ 			if (distemplate)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						 errmsg("conflicting or redundant options")));
+ 			distemplate = defel;
+ 		}
+ 		else if (strcmp(defel->defname, "allowconnections") == 0)
+ 		{
+ 			if (dallowconn)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						 errmsg("conflicting or redundant options")));
+ 			dallowconn = defel;
+ 		}
  		else if (strcmp(defel->defname, "connectionlimit") == 0)
  		{
  			if (dconnlimit)
***************
*** 246,251 **** createdb(const CreatedbStmt *stmt)
--- 267,277 ----
  	if (dctype && dctype->arg)
  		dbctype = strVal(dctype->arg);
  
+ 	if (distemplate && distemplate->arg)
+ 		istemplate = defGetBoolean(distemplate);
+ 	if (dallowconn && dallowconn->arg)
+ 		allowconn = defGetBoolean(dallowconn);
+ 
  	if (dconnlimit && dconnlimit->arg)
  	{
  		dbconnlimit = intVal(dconnlimit->arg);
***************
*** 488,495 **** 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(istemplate);
! 	new_record[Anum_pg_database_datallowconn - 1] = BoolGetDatum(allowconn);
  	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);
***************
*** 1329,1335 **** AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
--- 1355,1365 ----
  	ScanKeyData scankey;
  	SysScanDesc scan;
  	ListCell   *option;
+ 	bool		istemplate = false;
+ 	bool		allowconn = true;
  	int			connlimit = -1;
+ 	DefElem    *distemplate = NULL;
+ 	DefElem    *dallowconn = NULL;
  	DefElem    *dconnlimit = NULL;
  	DefElem    *dtablespace = NULL;
  	Datum		new_record[Natts_pg_database];
***************
*** 1341,1347 **** AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
  	{
  		DefElem    *defel = (DefElem *) lfirst(option);
  
! 		if (strcmp(defel->defname, "connectionlimit") == 0)
  		{
  			if (dconnlimit)
  				ereport(ERROR,
--- 1371,1393 ----
  	{
  		DefElem    *defel = (DefElem *) lfirst(option);
  
! 		if (strcmp(defel->defname, "istemplate") == 0)
! 		{
! 			if (distemplate)
! 				ereport(ERROR,
! 						(errcode(ERRCODE_SYNTAX_ERROR),
! 						 errmsg("conflicting or redundant options")));
! 			distemplate = defel;
! 		}
! 		else if (strcmp(defel->defname, "allowconnections") == 0)
! 		{
! 			if (dallowconn)
! 				ereport(ERROR,
! 						(errcode(ERRCODE_SYNTAX_ERROR),
! 						 errmsg("conflicting or redundant options")));
! 			dallowconn = defel;
! 		}
! 		else if (strcmp(defel->defname, "connectionlimit") == 0)
  		{
  			if (dconnlimit)
  				ereport(ERROR,
***************
*** 1365,1377 **** 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 = intVal(dconnlimit->arg);
--- 1411,1428 ----
  	if (dtablespace)
  	{
  		/* currently, can't be specified along with any other options */
! 		Assert(!distemplate && !dallowconn && !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)
+ 		istemplate = defGetBoolean(distemplate);
+ 	if (dallowconn)
+ 		allowconn = defGetBoolean(dallowconn);
+ 
  	if (dconnlimit)
  	{
  		connlimit = intVal(dconnlimit->arg);
***************
*** 1412,1417 **** AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
--- 1463,1480 ----
  	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(istemplate);
+ 		new_record_repl[Anum_pg_database_datistemplate -1] = true;
+ 	}
+ 
+ 	if (dallowconn)
+ 	{
+ 		new_record[Anum_pg_database_datallowconn - 1] = BoolGetDatum(allowconn);
+ 		new_record_repl[Anum_pg_database_datallowconn - 1] = true;
+ 	}
+ 
  	if (dconnlimit)
  	{
  		new_record[Anum_pg_database_datconnlimit - 1] = Int32GetDatum(connlimit);
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 522,528 **** static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
  
  /* ordinary key words in alphabetical order */
  %token <keyword> ABORT_P ABSOLUTE_P ACCESS ACTION ADD_P ADMIN AFTER
! 	AGGREGATE ALL ALSO ALTER ALWAYS ANALYSE ANALYZE AND ANY ARRAY AS ASC
  	ASSERTION ASSIGNMENT ASYMMETRIC AT ATTRIBUTE AUTHORIZATION
  
  	BACKWARD BEFORE BEGIN_P BETWEEN BIGINT BINARY BIT
--- 522,528 ----
  
  /* ordinary key words in alphabetical order */
  %token <keyword> ABORT_P ABSOLUTE_P ACCESS ACTION ADD_P ADMIN AFTER
! 	AGGREGATE ALL ALLOW ALSO ALTER ALWAYS ANALYSE ANALYZE AND ANY ARRAY AS ASC
  	ASSERTION ASSIGNMENT ASYMMETRIC AT ATTRIBUTE AUTHORIZATION
  
  	BACKWARD BEFORE BEGIN_P BETWEEN BIGINT BINARY BIT
***************
*** 531,537 **** static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
  	CACHE CALLED CASCADE CASCADED CASE CAST CATALOG_P CHAIN CHAR_P
  	CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE
  	CLUSTER COALESCE COLLATE COLLATION COLUMN COMMENT COMMENTS COMMIT
! 	COMMITTED CONCURRENTLY CONFIGURATION CONNECTION CONSTRAINT CONSTRAINTS
  	CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE
  	CROSS CSV CURRENT_P
  	CURRENT_CATALOG CURRENT_DATE CURRENT_ROLE CURRENT_SCHEMA
--- 531,537 ----
  	CACHE CALLED CASCADE CASCADED CASE CAST CATALOG_P CHAIN CHAR_P
  	CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE
  	CLUSTER COALESCE COLLATE COLLATION COLUMN COMMENT COMMENTS COMMIT
! 	COMMITTED CONCURRENTLY CONFIGURATION CONNECTION CONNECTIONS CONSTRAINT CONSTRAINTS
  	CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE
  	CROSS CSV CURRENT_P
  	CURRENT_CATALOG CURRENT_DATE CURRENT_ROLE CURRENT_SCHEMA
***************
*** 8374,8379 **** createdb_opt_item:
--- 8374,8387 ----
  				{
  					$$ = makeDefElem("lc_ctype", NULL);
  				}
+ 			| IS TEMPLATE opt_equal opt_boolean_or_string
+ 				{
+ 					$$ = makeDefElem("istemplate", (Node *)makeString($4));
+ 				}
+ 			| ALLOW CONNECTIONS opt_equal opt_boolean_or_string
+ 				{
+ 					$$ = makeDefElem("allowconnections", (Node *)makeString($4));
+ 				}
  			| CONNECTION LIMIT opt_equal SignedIconst
  				{
  					$$ = makeDefElem("connectionlimit", (Node *)makeInteger($4));
***************
*** 8438,8444 **** alterdb_opt_list:
  		;
  
  alterdb_opt_item:
! 			CONNECTION LIMIT opt_equal SignedIconst
  				{
  					$$ = makeDefElem("connectionlimit", (Node *)makeInteger($4));
  				}
--- 8446,8460 ----
  		;
  
  alterdb_opt_item:
! 			IS TEMPLATE opt_equal opt_boolean_or_string
! 				{
! 					$$ = makeDefElem("istemplate", (Node *)makeString($4));
! 				}
! 			| ALLOW CONNECTIONS opt_equal opt_boolean_or_string
! 				{
! 					$$ = makeDefElem("allowconnections", (Node *)makeString($4));
! 				}
! 			| CONNECTION LIMIT opt_equal SignedIconst
  				{
  					$$ = makeDefElem("connectionlimit", (Node *)makeInteger($4));
  				}
***************
*** 12805,12810 **** unreserved_keyword:
--- 12821,12827 ----
  			| ADMIN
  			| AFTER
  			| AGGREGATE
+ 			| ALLOW
  			| ALSO
  			| ALTER
  			| ALWAYS
***************
*** 12833,12838 **** unreserved_keyword:
--- 12850,12856 ----
  			| COMMITTED
  			| CONFIGURATION
  			| CONNECTION
+ 			| CONNECTIONS
  			| CONSTRAINTS
  			| CONTENT_P
  			| CONTINUE_P
*** a/src/bin/psql/tab-complete.c
--- b/src/bin/psql/tab-complete.c
***************
*** 987,994 **** psql_completion(const char *text, int start, int end)
  		{"OWNER TO", "RENAME TO", "SET SCHEMA", NULL};
  
  		COMPLETE_WITH_LIST(list_ALTERGEN);
! 	}
! 
  	/* ALTER CONVERSION <name> */
  	else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
  			 pg_strcasecmp(prev2_wd, "CONVERSION") == 0)
--- 987,993 ----
  		{"OWNER TO", "RENAME TO", "SET SCHEMA", NULL};
  
  		COMPLETE_WITH_LIST(list_ALTERGEN);
! 	} 
  	/* ALTER CONVERSION <name> */
  	else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
  			 pg_strcasecmp(prev2_wd, "CONVERSION") == 0)
***************
*** 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);
  	}
--- 1003,1010 ----
  			 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);
  	}
--- 2045,2052 ----
  			 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);
  	}
*** a/src/include/parser/kwlist.h
--- b/src/include/parser/kwlist.h
***************
*** 35,40 **** PG_KEYWORD("admin", ADMIN, UNRESERVED_KEYWORD)
--- 35,41 ----
  PG_KEYWORD("after", AFTER, UNRESERVED_KEYWORD)
  PG_KEYWORD("aggregate", AGGREGATE, UNRESERVED_KEYWORD)
  PG_KEYWORD("all", ALL, RESERVED_KEYWORD)
+ PG_KEYWORD("allow", ALLOW, UNRESERVED_KEYWORD)
  PG_KEYWORD("also", ALSO, UNRESERVED_KEYWORD)
  PG_KEYWORD("alter", ALTER, UNRESERVED_KEYWORD)
  PG_KEYWORD("always", ALWAYS, UNRESERVED_KEYWORD)
***************
*** 88,93 **** PG_KEYWORD("committed", COMMITTED, UNRESERVED_KEYWORD)
--- 89,95 ----
  PG_KEYWORD("concurrently", CONCURRENTLY, TYPE_FUNC_NAME_KEYWORD)
  PG_KEYWORD("configuration", CONFIGURATION, UNRESERVED_KEYWORD)
  PG_KEYWORD("connection", CONNECTION, UNRESERVED_KEYWORD)
+ PG_KEYWORD("connections", CONNECTIONS, UNRESERVED_KEYWORD)
  PG_KEYWORD("constraint", CONSTRAINT, RESERVED_KEYWORD)
  PG_KEYWORD("constraints", CONSTRAINTS, UNRESERVED_KEYWORD)
  PG_KEYWORD("content", CONTENT_P, UNRESERVED_KEYWORD)
