*** a/contrib/hstore/Makefile
--- b/contrib/hstore/Makefile
***************
*** 5,11 **** OBJS = hstore_io.o hstore_op.o hstore_gist.o hstore_gin.o hstore_compat.o \
  	crc32.o
  
  EXTENSION = hstore
! DATA = hstore--1.1.sql hstore--1.0--1.1.sql hstore--unpackaged--1.0.sql
  
  REGRESS = hstore
  
--- 5,11 ----
  	crc32.o
  
  EXTENSION = hstore
! DATA = hstore--1.0.sql hstore--1.0--1.1.sql hstore--unpackaged--1.0.sql
  
  REGRESS = hstore
  
*** a/contrib/hstore/hstore.control
--- b/contrib/hstore/hstore.control
***************
*** 1,5 ****
--- 1,6 ----
  # hstore extension
  comment = 'data type for storing sets of (key, value) pairs'
  default_version = '1.1'
+ default_full_version = '1.0'
  module_pathname = '$libdir/hstore'
  relocatable = true
*** a/doc/src/sgml/extend.sgml
--- b/doc/src/sgml/extend.sgml
***************
*** 350,355 ****
--- 350,363 ----
     </para>
  
     <para>
+     When an extension only uses SQL definitions (and does not need to ship
+     compiled binary code, usually from C source), then it can use
+     the <firstterm>template</> facility in order to upload the necessary
+     script to the <productname>PostgreSQL</> server, all using the usual
+     clients and protocol.
+    </para>
+ 
+    <para>
      The kinds of SQL objects that can be members of an extension are shown in
      the description of <xref linkend="sql-alterextension">.  Notably, objects
      that are database-cluster-wide, such as databases, roles, and tablespaces,
***************
*** 423,428 ****
--- 431,451 ----
       </varlistentry>
  
       <varlistentry>
+       <term><varname>default_full_version</varname> (<type>string</type>)</term>
+       <listitem>
+        <para>
+         This option allows an extension author to avoid shiping all versions
+         scripts when shipping an extension. When a version is requested and
+         the matching script does not exist on disk,
+         set <replaceable>default_full_version</replaceable> to the first
+         script you still ship and PostgreSQL will apply the intermediate
+         upgrade script as per the <command>ALTER EXTENSION UPDATE</command>
+         command.
+        </para>
+       </listitem>
+      </varlistentry>
+ 
+      <varlistentry>
        <term><varname>comment</varname> (<type>string</type>)</term>
        <listitem>
         <para>
*** a/doc/src/sgml/ref/allfiles.sgml
--- b/doc/src/sgml/ref/allfiles.sgml
***************
*** 32,37 **** Complete list of usable sgml source files in this directory.
--- 32,38 ----
  <!ENTITY alterSequence      SYSTEM "alter_sequence.sgml">
  <!ENTITY alterTable         SYSTEM "alter_table.sgml">
  <!ENTITY alterTableSpace    SYSTEM "alter_tablespace.sgml">
+ <!ENTITY alterTemplateForExtension SYSTEM "alter_extension_template.sgml">
  <!ENTITY alterTSConfig      SYSTEM "alter_tsconfig.sgml">
  <!ENTITY alterTSDictionary  SYSTEM "alter_tsdictionary.sgml">
  <!ENTITY alterTSParser      SYSTEM "alter_tsparser.sgml">
***************
*** 76,81 **** Complete list of usable sgml source files in this directory.
--- 77,83 ----
  <!ENTITY createTable        SYSTEM "create_table.sgml">
  <!ENTITY createTableAs      SYSTEM "create_table_as.sgml">
  <!ENTITY createTableSpace   SYSTEM "create_tablespace.sgml">
+ <!ENTITY createTemplateForExtension SYSTEM "create_extension_template.sgml">
  <!ENTITY createTrigger      SYSTEM "create_trigger.sgml">
  <!ENTITY createTSConfig     SYSTEM "create_tsconfig.sgml">
  <!ENTITY createTSDictionary SYSTEM "create_tsdictionary.sgml">
***************
*** 116,121 **** Complete list of usable sgml source files in this directory.
--- 118,124 ----
  <!ENTITY dropServer         SYSTEM "drop_server.sgml">
  <!ENTITY dropTable          SYSTEM "drop_table.sgml">
  <!ENTITY dropTableSpace     SYSTEM "drop_tablespace.sgml">
+ <!ENTITY dropTemplateForExtension SYSTEM "drop_extension_template.sgml">
  <!ENTITY dropTrigger        SYSTEM "drop_trigger.sgml">
  <!ENTITY dropTSConfig       SYSTEM "drop_tsconfig.sgml">
  <!ENTITY dropTSDictionary   SYSTEM "drop_tsdictionary.sgml">
*** /dev/null
--- b/doc/src/sgml/ref/alter_extension_template.sgml
***************
*** 0 ****
--- 1,149 ----
+ <!--
+ doc/src/sgml/ref/alter_extension_template.sgml
+ PostgreSQL documentation
+ -->
+ 
+ <refentry id="SQL-ALTEREXTENSIONTEMPLATE">
+  <refmeta>
+   <refentrytitle>ALTER TEMPLATE FOR EXTENSION</refentrytitle>
+   <manvolnum>7</manvolnum>
+   <refmiscinfo>SQL - Language Statements</refmiscinfo>
+  </refmeta>
+ 
+  <refnamediv>
+   <refname>ALTER TEMPLATE FOR EXTENSION</refname>
+   <refpurpose>change the definition of a template for an extension</refpurpose>
+  </refnamediv>
+ 
+  <indexterm zone="sql-alterextensiontemplate">
+   <primary>ALTER TEMPLATE FOR EXTENSION</primary>
+  </indexterm>
+ 
+  <refsynopsisdiv>
+ <synopsis>
+ ALTER TEMPLATE FOR EXTENSION <replaceable>name</replaceable> SET DEFAULT VERSION <replaceable>version</replaceable>
+ ALTER TEMPLATE FOR EXTENSION <replaceable>name</replaceable> SET DEFAULT FULL VERSION <replaceable>full_version</replaceable>
+ ALTER TEMPLATE FOR EXTENSION <replaceable>name</replaceable> VERSION <replaceable>version</replaceable> WITH [ ([ <replaceable class="parameter">control_parameter</replaceable> ] [, ... ]) ] 
+ ALTER TEMPLATE FOR EXTENSION <replaceable>name</replaceable> VERSION <replaceable>version</replaceable> AS <replaceable>script</replaceable>
+ ALTER TEMPLATE FOR EXTENSION <replaceable>name</replaceable> FROM <replaceable>from_version</replaceable> TO <replaceable>to_version</replaceable> AS <replaceable>script</replaceable>
+ ALTER TEMPLATE FOR EXTENSION <replaceable>name</replaceable> OWNER TO <replaceable>new_owner</replaceable>
+ ALTER TEMPLATE FOR EXTENSION <replaceable>name</replaceable> RENAME TO <replaceable>new_name</replaceable>
+ 
+ <phrase>where <replaceable class="parameter">control_parameter</replaceable> is one of:
+ 
+  SCHEMA <replaceable class="parameter">schema_name</replaceable>
+  SUPERUSER
+  NOSUPERUSER
+  RELOCATABLE
+  NORELOCATABLE
+  REQUIRES <replaceable class="parameter">requirements</replaceable>
+ 
+ </synopsis>
+  </refsynopsisdiv>
+  <refsect1>
+   <title>Description</title>
+ 
+   <para>
+    <command>ALTER TEMPLATE FOR EXTENSION</command> changes the definition of
+    an extension template. Currently, the only supported functionality is to
+    change the template's default version.
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Parameters</title>
+ 
+   <variablelist>
+    <varlistentry>
+     <term><replaceable class="parameter">name</replaceable></term>
+     <listitem>
+      <para>
+       The name of an extension that already has templates.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><replaceable class="parameter">version</replaceable></term>
+     <listitem>
+      <para>
+       The version of the extension we want to install by default when using
+       its template.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><replaceable class="parameter">full_version</replaceable></term>
+     <listitem>
+      <para>
+       The version of the extension we want to install from by default when
+       using its template. For example, if you have an extension installation
+       scipt for version <literal>1.0</literal> and an upgrade script
+       for <literal>1.0--1.1</literal>, you can set the
+       default <literal>full_version</literal> to <literal>1.0</literal> so
+       that <productname>PostgreSQL</productname> knows to install
+       version <literal>1.1</literal> by first using
+       the <literal>1.0</literal> creation script then
+       the <literal>1.1</literal> upgrade script.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><replaceable class="parameter">script</replaceable></term>
+     <listitem>
+      <para>
+       The script to run when installing this version of the extension.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><replaceable class="parameter">control_parameters</replaceable></term>
+     <listitem>
+      <para>
+       For details about the control parameters meaning, please refer
+       to <xref linkend="extend-extension">.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><replaceable class="parameter">new_name</replaceable></term>
+     <listitem>
+      <para>
+       The new name of the aggregate function.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><replaceable class="parameter">new_owner</replaceable></term>
+     <listitem>
+      <para>
+       The new owner of the aggregate function.
+      </para>
+     </listitem>
+    </varlistentry>
+  </variablelist>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Compatibility</title>
+ 
+   <para>
+    There is no <command>ALTER TEMPLATE FOR EXTENSION</command> statement in
+    the SQL standard.
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>See Also</title>
+ 
+   <simplelist type="inline">
+    <member><xref linkend="sql-createextensiontemplate"></member>
+    <member><xref linkend="sql-dropextensiontemplate"></member>
+   </simplelist>
+  </refsect1>
+ </refentry>
*** /dev/null
--- b/doc/src/sgml/ref/create_extension_template.sgml
***************
*** 0 ****
--- 1,104 ----
+ <!--
+ doc/src/sgml/ref/create_extension_template.sgml
+ PostgreSQL documentation
+ -->
+ 
+ <refentry id="SQL-CREATEEXTENSIONTEMPLATE">
+  <refmeta>
+   <refentrytitle>CREATE TEMPLATE FOR EXTENSION</refentrytitle>
+   <manvolnum>7</manvolnum>
+   <refmiscinfo>SQL - Language Statements</refmiscinfo>
+  </refmeta>
+ 
+  <refnamediv>
+   <refname>CREATE TEMPLATE FOR EXTENSION</refname>
+   <refpurpose>define a new template for extension</refpurpose>
+  </refnamediv>
+ 
+  <indexterm zone="sql-createtstemplate">
+   <primary>CREATE TEMPLATE FOR EXTENSION</primary>
+  </indexterm>
+ 
+  <refsynopsisdiv>
+ <synopsis>
+ CREATE TEMPLATE FOR EXTENSION <replaceable class="parameter">name</replaceable>
+ [ DEFAULT ] VERSION <replaceable class="parameter">version</replaceable>
+ [ WITH <replaceable class="parameter"> [ (
+   [ <replaceable class="parameter">control_parameter</replaceable> ] [, ... ]
+ ) ] ]
+ AS <replaceable class="parameter">script</replaceable>
+ 
+ CREATE TEMPLATE FOR EXTENSION <replaceable class="parameter">name</replaceable>
+   FROM <replaceable class="parameter">old_version</replaceable> TO <replaceable class="parameter">new_version</replaceable>
+ [ WITH <replaceable class="parameter"> [ (
+   [ <replaceable class="parameter">control_parameter</replaceable> ] [, ... ]
+ ) ] ]
+ AS <replaceable class="parameter">script</replaceable>
+ 
+ 
+ <phrase>where <replaceable class="parameter">control_parameter</replaceable> is one of:
+ 
+  SCHEMA <replaceable class="parameter">schema_name</replaceable>
+  SUPERUSER
+  NOSUPERUSER
+  RELOCATABLE
+  NORELOCATABLE
+  REQUIRES <replaceable class="parameter">requirements</replaceable>
+ 
+ </synopsis>
+  </refsynopsisdiv>
+ 
+  <refsect1>
+   <title>Description</title>
+ 
+   <para>
+    <command>CREATE TEMPLATE FOR EXTENSION</command> creates a new template
+    for creating the extension of the same name. It allows tools and users to
+    upload an extension script and control file without needing to access the
+    file system of the server which is running
+    the <productname>PostgreSQL</productname> service.
+   </para>
+ 
+   <para>
+    Using the <command>CREATE TEMPLATE FOR EXTENSION</command> command you
+    can upload script to be run at <command>CREATE EXTENSION</command> time
+    and at <command>ALTER EXTENSION ... UPDATE</command> time.
+   </para>
+ 
+   <para>
+    Refer to <xref linkend="extend-extension"> for further information.
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Control Parameters</title>
+ 
+   <para>
+    For details about the control parameters meaning, please refer
+    to <xref linkend="extend-extension">.
+   </para>
+ 
+   <para>
+    The arguments can appear in any order, not only the one shown above.
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Compatibility</title>
+ 
+   <para>
+    There is no
+    <command>CREATE TEMPLATE FOR EXTENSION</command> statement in the SQL
+    standard.
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>See Also</title>
+ 
+   <simplelist type="inline">
+    <member><xref linkend="sql-alterextensiontemplate"></member>
+    <member><xref linkend="sql-dropextensiontemplate"></member>
+   </simplelist>
+  </refsect1>
+ </refentry>
*** /dev/null
--- b/doc/src/sgml/ref/drop_extension_template.sgml
***************
*** 0 ****
--- 1,81 ----
+ <!--
+ doc/src/sgml/ref/drop_extension_template.sgml
+ PostgreSQL documentation
+ -->
+ 
+ <refentry id="SQL-DROPTSTEMPLATE">
+  <refmeta>
+   <refentrytitle>DROP TEMPLATE FOR EXTENSION</refentrytitle>
+   <manvolnum>7</manvolnum>
+   <refmiscinfo>SQL - Language Statements</refmiscinfo>
+  </refmeta>
+ 
+  <refnamediv>
+   <refname>DROP TEMPLATE FOR EXTENSION</refname>
+   <refpurpose>remove a template for an extension</refpurpose>
+  </refnamediv>
+ 
+  <indexterm zone="sql-droptstemplate">
+   <primary>DROP TEMPLATE FOR EXTENSION</primary>
+  </indexterm>
+ 
+  <refsynopsisdiv>
+ <synopsis>
+  DROP TEMPLATE FOR EXTENSION <replaceable class="PARAMETER">name</replaceable> VERSION <replaceable class="PARAMETER">version</replaceable> [ CASCADE | RESTRICT ]
+  DROP TEMPLATE FOR EXTENSION <replaceable class="PARAMETER">name</replaceable> FROM <replaceable class="PARAMETER">old_version</replaceable> TO <replaceable class="PARAMETER">new_version</replaceable> [ CASCADE | RESTRICT ]
+ </synopsis>
+  </refsynopsisdiv>
+ 
+  <refsect1>
+   <title>Description</title>
+ 
+   <para>
+    <command>DROP TEMPLATE FOR EXTENSION</command> drops an existing template
+    for named extension
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Parameters</title>
+ 
+   <variablelist>
+    <varlistentry>
+     <term><replaceable class="parameter">name</replaceable></term>
+     <listitem>
+      <para>
+       The name of an existing text search template.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><literal>CASCADE</literal></term>
+     <listitem>
+      <para>
+       Automatically drop objects that depend on the template.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><literal>RESTRICT</literal></term>
+     <listitem>
+      <para>
+       Refuse to drop the template if any objects depend on it. This is the
+       default.
+      </para>
+     </listitem>
+    </varlistentry>
+   </variablelist>
+  </refsect1>
+ 
+  <refsect1>
+   <title>See Also</title>
+ 
+   <simplelist type="inline">
+    <member><xref linkend="sql-alterextensiontemplate"></member>
+    <member><xref linkend="sql-createextensiontemplate"></member>
+   </simplelist>
+  </refsect1>
+ 
+ </refentry>
*** a/doc/src/sgml/reference.sgml
--- b/doc/src/sgml/reference.sgml
***************
*** 60,65 ****
--- 60,66 ----
     &alterServer;
     &alterTable;
     &alterTableSpace;
+    &alterTemplateForExtension;
     &alterTSConfig;
     &alterTSDictionary;
     &alterTSParser;
***************
*** 104,109 ****
--- 105,111 ----
     &createTable;
     &createTableAs;
     &createTableSpace;
+    &createTemplateForExtension;
     &createTSConfig;
     &createTSDictionary;
     &createTSParser;
***************
*** 144,149 ****
--- 146,152 ----
     &dropServer;
     &dropTable;
     &dropTableSpace;
+    &dropTemplateForExtension;
     &dropTSConfig;
     &dropTSDictionary;
     &dropTSParser;
*** a/src/backend/catalog/Makefile
--- b/src/backend/catalog/Makefile
***************
*** 38,43 **** POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
--- 38,44 ----
  	pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \
  	pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \
  	pg_ts_parser.h pg_ts_template.h pg_extension.h \
+ 	 pg_extension_control.h pg_extension_template.h pg_extension_uptmpl.h \
  	pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
  	pg_foreign_table.h \
  	pg_default_acl.h pg_seclabel.h pg_shseclabel.h pg_collation.h pg_range.h \
*** a/src/backend/catalog/aclchk.c
--- b/src/backend/catalog/aclchk.c
***************
*** 32,37 ****
--- 32,40 ----
  #include "catalog/pg_default_acl.h"
  #include "catalog/pg_event_trigger.h"
  #include "catalog/pg_extension.h"
+ #include "catalog/pg_extension_control.h"
+ #include "catalog/pg_extension_template.h"
+ #include "catalog/pg_extension_uptmpl.h"
  #include "catalog/pg_foreign_data_wrapper.h"
  #include "catalog/pg_foreign_server.h"
  #include "catalog/pg_language.h"
***************
*** 5042,5047 **** pg_extension_ownercheck(Oid ext_oid, Oid roleid)
--- 5045,5182 ----
  }
  
  /*
+  * Ownership check for an extension control (specified by OID).
+  */
+ bool
+ pg_extension_control_ownercheck(Oid ext_control_oid, Oid roleid)
+ {
+ 	Relation	pg_extension_control;
+ 	ScanKeyData entry[1];
+ 	SysScanDesc scan;
+ 	HeapTuple	tuple;
+ 	Oid			ownerId;
+ 
+ 	/* Superusers bypass all permission checking. */
+ 	if (superuser_arg(roleid))
+ 		return true;
+ 
+ 	/* There's no syscache for pg_extension_control, so do it the hard way */
+ 	pg_extension_control =
+ 		heap_open(ExtensionControlRelationId, AccessShareLock);
+ 
+ 	ScanKeyInit(&entry[0],
+ 				ObjectIdAttributeNumber,
+ 				BTEqualStrategyNumber, F_OIDEQ,
+ 				ObjectIdGetDatum(ext_control_oid));
+ 
+ 	scan = systable_beginscan(pg_extension_control,
+ 							  ExtensionControlOidIndexId, true,
+ 							  SnapshotNow, 1, entry);
+ 
+ 	tuple = systable_getnext(scan);
+ 	if (!HeapTupleIsValid(tuple))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_UNDEFINED_OBJECT),
+ 				 errmsg("extension control with OID %u does not exist",
+ 						ext_control_oid)));
+ 
+ 	ownerId = ((Form_pg_extension_control) GETSTRUCT(tuple))->ctlowner;
+ 
+ 	systable_endscan(scan);
+ 	heap_close(pg_extension_control, AccessShareLock);
+ 
+ 	return has_privs_of_role(roleid, ownerId);
+ }
+ 
+ /*
+  * Ownership check for an extension template (specified by OID).
+  */
+ bool
+ pg_extension_template_ownercheck(Oid ext_template_oid, Oid roleid)
+ {
+ 	Relation	pg_extension_template;
+ 	ScanKeyData entry[1];
+ 	SysScanDesc scan;
+ 	HeapTuple	tuple;
+ 	Oid			ownerId;
+ 
+ 	/* Superusers bypass all permission checking. */
+ 	if (superuser_arg(roleid))
+ 		return true;
+ 
+ 	/* There's no syscache for pg_extension_template, so do it the hard way */
+ 	pg_extension_template =
+ 		heap_open(ExtensionTemplateRelationId, AccessShareLock);
+ 
+ 	ScanKeyInit(&entry[0],
+ 				ObjectIdAttributeNumber,
+ 				BTEqualStrategyNumber, F_OIDEQ,
+ 				ObjectIdGetDatum(ext_template_oid));
+ 
+ 	scan = systable_beginscan(pg_extension_template,
+ 							  ExtensionTemplateOidIndexId, true,
+ 							  SnapshotNow, 1, entry);
+ 
+ 	tuple = systable_getnext(scan);
+ 	if (!HeapTupleIsValid(tuple))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_UNDEFINED_OBJECT),
+ 				 errmsg("extension template with OID %u does not exist",
+ 						ext_template_oid)));
+ 
+ 	ownerId = ((Form_pg_extension_template) GETSTRUCT(tuple))->tplowner;
+ 
+ 	systable_endscan(scan);
+ 	heap_close(pg_extension_template, AccessShareLock);
+ 
+ 	return has_privs_of_role(roleid, ownerId);
+ }
+ 
+ /*
+  * Ownership check for an extension update template (specified by OID).
+  */
+ bool
+ pg_extension_uptmpl_ownercheck(Oid ext_uptmpl_oid, Oid roleid)
+ {
+ 	Relation	pg_extension_uptmpl;
+ 	ScanKeyData entry[1];
+ 	SysScanDesc scan;
+ 	HeapTuple	tuple;
+ 	Oid			ownerId;
+ 
+ 	/* Superusers bypass all permission checking. */
+ 	if (superuser_arg(roleid))
+ 		return true;
+ 
+ 	/* There's no syscache for pg_extension_uptmpl, so do it the hard way */
+ 	pg_extension_uptmpl =
+ 		heap_open(ExtensionUpTmplRelationId, AccessShareLock);
+ 
+ 	ScanKeyInit(&entry[0],
+ 				ObjectIdAttributeNumber,
+ 				BTEqualStrategyNumber, F_OIDEQ,
+ 				ObjectIdGetDatum(ext_uptmpl_oid));
+ 
+ 	scan = systable_beginscan(pg_extension_uptmpl,
+ 							  ExtensionUpTmplOidIndexId, true,
+ 							  SnapshotNow, 1, entry);
+ 
+ 	tuple = systable_getnext(scan);
+ 	if (!HeapTupleIsValid(tuple))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_UNDEFINED_OBJECT),
+ 				 errmsg("extension uptmpl with OID %u does not exist",
+ 						ext_uptmpl_oid)));
+ 
+ 	ownerId = ((Form_pg_extension_uptmpl) GETSTRUCT(tuple))->uptowner;
+ 
+ 	systable_endscan(scan);
+ 	heap_close(pg_extension_uptmpl, AccessShareLock);
+ 
+ 	return has_privs_of_role(roleid, ownerId);
+ }
+ 
+ /*
   * Check whether specified role has CREATEROLE privilege (or is a superuser)
   *
   * Note: roles do not have owners per se; instead we use this test in
*** a/src/backend/catalog/dependency.c
--- b/src/backend/catalog/dependency.c
***************
*** 37,42 ****
--- 37,45 ----
  #include "catalog/pg_depend.h"
  #include "catalog/pg_event_trigger.h"
  #include "catalog/pg_extension.h"
+ #include "catalog/pg_extension_control.h"
+ #include "catalog/pg_extension_template.h"
+ #include "catalog/pg_extension_uptmpl.h"
  #include "catalog/pg_foreign_data_wrapper.h"
  #include "catalog/pg_foreign_server.h"
  #include "catalog/pg_language.h"
***************
*** 64,69 ****
--- 67,73 ----
  #include "commands/schemacmds.h"
  #include "commands/seclabel.h"
  #include "commands/tablespace.h"
+ #include "commands/template.h"
  #include "commands/trigger.h"
  #include "commands/typecmds.h"
  #include "foreign/foreign.h"
***************
*** 1228,1233 **** doDeletion(const ObjectAddress *object, int flags)
--- 1232,1249 ----
  			RemoveExtensionById(object->objectId);
  			break;
  
+ 		case OCLASS_EXTENSION_CONTROL:
+ 			RemoveExtensionControlById(object->objectId);
+ 			break;
+ 
+ 		case OCLASS_EXTENSION_TEMPLATE:
+ 			RemoveExtensionTemplateById(object->objectId);
+ 			break;
+ 
+ 		case OCLASS_EXTENSION_UPTMPL:
+ 			RemoveExtensionUpTmplById(object->objectId);
+ 			break;
+ 
  		case OCLASS_EVENT_TRIGGER:
  			RemoveEventTriggerById(object->objectId);
  			break;
***************
*** 2289,2294 **** getObjectClass(const ObjectAddress *object)
--- 2305,2319 ----
  		case ExtensionRelationId:
  			return OCLASS_EXTENSION;
  
+ 		case ExtensionControlRelationId:
+ 			return OCLASS_EXTENSION_CONTROL;
+ 
+ 		case ExtensionTemplateRelationId:
+ 			return OCLASS_EXTENSION_TEMPLATE;
+ 
+ 		case ExtensionUpTmplRelationId:
+ 			return OCLASS_EXTENSION_UPTMPL;
+ 
  		case EventTriggerRelationId:
  			return OCLASS_EVENT_TRIGGER;
  	}
***************
*** 2930,2935 **** getObjectDescription(const ObjectAddress *object)
--- 2955,2996 ----
  				break;
  			}
  
+ 		case OCLASS_EXTENSION_CONTROL:
+ 			{
+ 				char	   *extname;
+ 
+ 				extname = get_extension_control_name(object->objectId);
+ 				if (!extname)
+ 					elog(ERROR, "cache lookup failed for control template for extension %u",
+ 						 object->objectId);
+ 				appendStringInfo(&buffer, _("control template for extension %s"), extname);
+ 				break;
+ 			}
+ 
+ 		case OCLASS_EXTENSION_TEMPLATE:
+ 			{
+ 				char	   *extname;
+ 
+ 				extname = get_extension_template_name(object->objectId);
+ 				if (!extname)
+ 					elog(ERROR, "cache lookup failed for template for creating extension %u",
+ 						 object->objectId);
+ 				appendStringInfo(&buffer, _("install template for extension %s"), extname);
+ 				break;
+ 			}
+ 
+ 		case OCLASS_EXTENSION_UPTMPL:
+ 			{
+ 				char	   *extname;
+ 
+ 				extname = get_extension_uptmpl_name(object->objectId);
+ 				if (!extname)
+ 					elog(ERROR, "cache lookup failed for template for updating extension %u",
+ 						 object->objectId);
+ 				appendStringInfo(&buffer, _("update template for extension %s"), extname);
+ 				break;
+ 			}
+ 
          case OCLASS_EVENT_TRIGGER:
  			{
  				HeapTuple	tup;
*** a/src/backend/catalog/objectaddress.c
--- b/src/backend/catalog/objectaddress.c
***************
*** 28,33 ****
--- 28,36 ----
  #include "catalog/pg_conversion.h"
  #include "catalog/pg_database.h"
  #include "catalog/pg_extension.h"
+ #include "catalog/pg_extension_control.h"
+ #include "catalog/pg_extension_template.h"
+ #include "catalog/pg_extension_uptmpl.h"
  #include "catalog/pg_foreign_data_wrapper.h"
  #include "catalog/pg_foreign_server.h"
  #include "catalog/pg_language.h"
***************
*** 52,57 ****
--- 55,61 ----
  #include "commands/extension.h"
  #include "commands/proclang.h"
  #include "commands/tablespace.h"
+ #include "commands/template.h"
  #include "commands/trigger.h"
  #include "foreign/foreign.h"
  #include "libpq/be-fsstubs.h"
***************
*** 159,164 **** static ObjectPropertyType ObjectProperty[] =
--- 163,201 ----
  		ACL_KIND_EXTENSION
  	},
  	{
+ 		ExtensionControlRelationId,
+ 		ExtensionControlOidIndexId,
+ 		-1,
+ 		-1,
+ 		Anum_pg_extension_control_ctlname,
+ 		InvalidAttrNumber,		/* extension doesn't belong to extnamespace */
+ 		Anum_pg_extension_control_ctlowner,
+ 		InvalidAttrNumber,
+ 		ACL_KIND_EXTCONTROL
+ 	},
+ 	{
+ 		ExtensionTemplateRelationId,
+ 		ExtensionTemplateOidIndexId,
+ 		-1,
+ 		-1,
+ 		Anum_pg_extension_template_tplname,
+ 		InvalidAttrNumber,		/* extension doesn't belong to extnamespace */
+ 		Anum_pg_extension_template_tplowner,
+ 		InvalidAttrNumber,
+ 		ACL_KIND_EXTTEMPLATE
+ 	},
+ 	{
+ 		ExtensionUpTmplRelationId,
+ 		ExtensionUpTmplOidIndexId,
+ 		-1,
+ 		-1,
+ 		Anum_pg_extension_uptmpl_uptname,
+ 		InvalidAttrNumber,		/* extension doesn't belong to extnamespace */
+ 		Anum_pg_extension_uptmpl_uptowner,
+ 		InvalidAttrNumber,
+ 		ACL_KIND_EXTUPTMPL
+ 	},
+ 	{
  		ForeignDataWrapperRelationId,
  		ForeignDataWrapperOidIndexId,
  		FOREIGNDATAWRAPPEROID,
***************
*** 394,399 **** static ObjectAddress get_object_address_type(ObjectType objtype,
--- 431,438 ----
  						List *objname, bool missing_ok);
  static ObjectAddress get_object_address_opcf(ObjectType objtype, List *objname,
  						List *objargs, bool missing_ok);
+ static ObjectAddress get_object_address_tmpl(ObjectType objtype,
+ 						List *objname, List *objargs, bool missing_ok);
  static ObjectPropertyType *get_object_property_data(Oid class_id);
  
  /*
***************
*** 475,480 **** get_object_address(ObjectType objtype, List *objname, List *objargs,
--- 514,524 ----
  				address = get_object_address_unqualified(objtype,
  														 objname, missing_ok);
  				break;
+ 			case OBJECT_EXTENSION_TEMPLATE:
+ 			case OBJECT_EXTENSION_UPTMPL:
+ 				address = get_object_address_tmpl(objtype,
+ 												  objname, objargs, missing_ok);
+ 				break;
  			case OBJECT_TYPE:
  			case OBJECT_DOMAIN:
  				address = get_object_address_type(objtype, objname, missing_ok);
***************
*** 721,731 **** get_object_address_unqualified(ObjectType objtype,
  			address.objectId = get_extension_oid(name, missing_ok);
  			address.objectSubId = 0;
  			break;
- 		case OBJECT_TABLESPACE:
- 			address.classId = TableSpaceRelationId;
- 			address.objectId = get_tablespace_oid(name, missing_ok);
- 			address.objectSubId = 0;
- 			break;
  		case OBJECT_ROLE:
  			address.classId = AuthIdRelationId;
  			address.objectId = get_role_oid(name, missing_ok);
--- 765,770 ----
***************
*** 756,761 **** get_object_address_unqualified(ObjectType objtype,
--- 795,805 ----
  			address.objectId = get_event_trigger_oid(name, missing_ok);
  			address.objectSubId = 0;
  			break;
+  		case OBJECT_TABLESPACE:
+  			address.classId = TableSpaceRelationId;
+  			address.objectId = get_tablespace_oid(name, missing_ok);
+  			address.objectSubId = 0;
+  			break;
  		default:
  			elog(ERROR, "unrecognized objtype: %d", (int) objtype);
  			/* placate compiler, which doesn't know elog won't return */
***************
*** 1069,1074 **** get_object_address_opcf(ObjectType objtype,
--- 1113,1194 ----
  }
  
  /*
+  * Find the ObjectAddress for an extension template, control or update
+  * template.
+  */
+ static ObjectAddress
+ get_object_address_tmpl(ObjectType objtype,
+ 						List *objname, List *objargs, bool missing_ok)
+ {
+ 	const char *name;
+ 	ObjectAddress address;
+ 
+ 	/*
+ 	 * The types of names handled by this function are not permitted to be
+ 	 * schema-qualified or catalog-qualified.
+ 	 */
+ 	if (list_length(objname) != 1)
+ 	{
+ 		const char *msg;
+ 
+ 		switch (objtype)
+ 		{
+ 			case OBJECT_EXTENSION_TEMPLATE:
+ 				msg = gettext_noop("extension template name cannot be qualified");
+ 				break;
+ 			case OBJECT_EXTENSION_UPTMPL:
+ 				msg = gettext_noop("extension update template name cannot be qualified");
+ 				break;
+ 			default:
+ 				elog(ERROR, "unrecognized objtype: %d", (int) objtype);
+ 				msg = NULL;		/* placate compiler */
+ 		}
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_SYNTAX_ERROR),
+ 				 errmsg("%s", _(msg))));
+ 	}
+ 
+ 	name = strVal(linitial(objname));
+ 
+ 	switch (objtype)
+ 	{
+ 		case OBJECT_EXTENSION_TEMPLATE:
+ 		{
+ 			const char *version;
+ 
+ 			Assert(list_length(objargs) == 1);
+ 			version = strVal(linitial(objargs));
+ 
+ 			address.classId = ExtensionTemplateRelationId;
+ 			address.objectId = get_template_oid(name, version, missing_ok);
+ 			address.objectSubId = 0;
+ 			break;
+ 		}
+ 		case OBJECT_EXTENSION_UPTMPL:
+ 		{
+ 			const char *from, *to;
+ 
+ 			Assert(list_length(objargs) == 2);
+ 
+ 			from = strVal(linitial(objargs));
+ 			to = strVal(lsecond(objargs));
+ 
+ 			address.classId = ExtensionUpTmplRelationId;
+ 			address.objectId = get_uptmpl_oid(name, from, to, missing_ok);
+ 			address.objectSubId = 0;
+ 			break;
+ 		}
+ 		default:
+ 			elog(ERROR, "unrecognized objtype: %d", (int) objtype);
+ 			/* placate compiler, which doesn't know elog won't return */
+ 			address.classId = InvalidOid;
+ 			address.objectId = InvalidOid;
+ 			address.objectSubId = 0;
+ 	}
+ 	return address;
+ }
+ 
+ /*
   * Check ownership of an object previously identified by get_object_address.
   */
  void
***************
*** 1133,1138 **** check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
--- 1253,1268 ----
  				aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_EXTENSION,
  							   NameListToString(objname));
  			break;
+ 		case OBJECT_EXTENSION_TEMPLATE:
+ 			if (!pg_extension_ownercheck(address.objectId, roleid))
+ 				aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_EXTTEMPLATE,
+ 							   NameListToString(objname));
+ 			break;
+ 		case OBJECT_EXTENSION_UPTMPL:
+ 			if (!pg_extension_ownercheck(address.objectId, roleid))
+ 				aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_EXTUPTMPL,
+ 							   NameListToString(objname));
+ 			break;
  		case OBJECT_FDW:
  			if (!pg_foreign_data_wrapper_ownercheck(address.objectId, roleid))
  				aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FDW,
*** a/src/backend/commands/Makefile
--- b/src/backend/commands/Makefile
***************
*** 19,25 **** OBJS = aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o  \
  	indexcmds.o lockcmds.o matview.o operatorcmds.o opclasscmds.o \
  	portalcmds.o prepare.o proclang.o \
  	schemacmds.o seclabel.o sequence.o tablecmds.o tablespace.o trigger.o \
! 	tsearchcmds.o typecmds.o user.o vacuum.o vacuumlazy.o \
  	variable.o view.o
  
  include $(top_srcdir)/src/backend/common.mk
--- 19,25 ----
  	indexcmds.o lockcmds.o matview.o operatorcmds.o opclasscmds.o \
  	portalcmds.o prepare.o proclang.o \
  	schemacmds.o seclabel.o sequence.o tablecmds.o tablespace.o trigger.o \
! 	template.o tsearchcmds.o typecmds.o user.o vacuum.o vacuumlazy.o \
  	variable.o view.o
  
  include $(top_srcdir)/src/backend/common.mk
*** a/src/backend/commands/alter.c
--- b/src/backend/commands/alter.c
***************
*** 46,51 ****
--- 46,52 ----
  #include "commands/schemacmds.h"
  #include "commands/tablecmds.h"
  #include "commands/tablespace.h"
+ #include "commands/template.h"
  #include "commands/trigger.h"
  #include "commands/typecmds.h"
  #include "commands/user.h"
***************
*** 62,67 ****
--- 63,69 ----
  
  
  static Oid AlterObjectNamespace_internal(Relation rel, Oid objid, Oid nspOid);
+ static HeapTuple get_catalog_object_by_oid(Relation catalog, Oid objectId);
  
  /*
   * Raise an error to the effect that an object of the given name is already
***************
*** 146,156 **** report_namespace_conflict(Oid classId, const char *name, Oid nspOid)
   * objectId: OID of object to be renamed
   * new_name: CString representation of new name
   */
! static void
  AlterObjectRename_internal(Relation rel, Oid objectId, const char *new_name)
  {
  	Oid			classId = RelationGetRelid(rel);
- 	int			oidCacheId = get_object_catcache_oid(classId);
  	int			nameCacheId = get_object_catcache_name(classId);
  	AttrNumber	Anum_name = get_object_attnum_name(classId);
  	AttrNumber	Anum_namespace = get_object_attnum_namespace(classId);
--- 148,157 ----
   * objectId: OID of object to be renamed
   * new_name: CString representation of new name
   */
! void
  AlterObjectRename_internal(Relation rel, Oid objectId, const char *new_name)
  {
  	Oid			classId = RelationGetRelid(rel);
  	int			nameCacheId = get_object_catcache_name(classId);
  	AttrNumber	Anum_name = get_object_attnum_name(classId);
  	AttrNumber	Anum_namespace = get_object_attnum_namespace(classId);
***************
*** 168,175 **** AlterObjectRename_internal(Relation rel, Oid objectId, const char *new_name)
  	bool	   *nulls;
  	bool	   *replaces;
  
! 	oldtup = SearchSysCache1(oidCacheId, ObjectIdGetDatum(objectId));
! 	if (!HeapTupleIsValid(oldtup))
  		elog(ERROR, "cache lookup failed for object %u of catalog \"%s\"",
  			 objectId, RelationGetRelationName(rel));
  
--- 169,176 ----
  	bool	   *nulls;
  	bool	   *replaces;
  
! 	oldtup = get_catalog_object_by_oid(rel, objectId);
! 	if (oldtup == NULL)
  		elog(ERROR, "cache lookup failed for object %u of catalog \"%s\"",
  			 objectId, RelationGetRelationName(rel));
  
***************
*** 286,293 **** AlterObjectRename_internal(Relation rel, Oid objectId, const char *new_name)
  	pfree(nulls);
  	pfree(replaces);
  	heap_freetuple(newtup);
- 
- 	ReleaseSysCache(oldtup);
  }
  
  /*
--- 287,292 ----
***************
*** 337,342 **** ExecRenameStmt(RenameStmt *stmt)
--- 336,344 ----
  		case OBJECT_TYPE:
  			return RenameType(stmt);
  
+ 		case OBJECT_EXTENSION_TEMPLATE:
+ 			return AtlerExtensionTemplateRename(stmt->subname, stmt->newname);
+ 
  		case OBJECT_AGGREGATE:
  		case OBJECT_COLLATION:
  		case OBJECT_CONVERSION:
***************
*** 694,699 **** ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
--- 696,705 ----
  			return AlterEventTriggerOwner(strVal(linitial(stmt->object)),
  										  newowner);
  
+ 		case OBJECT_EXTENSION_TEMPLATE:
+ 			return AtlerExtensionTemplateOwner(strVal(linitial(stmt->object)),
+ 											   newowner);
+ 
  		/* Generic cases */
  		case OBJECT_AGGREGATE:
  		case OBJECT_COLLATION:
*** a/src/backend/commands/event_trigger.c
--- b/src/backend/commands/event_trigger.c
***************
*** 85,90 **** static event_trigger_support_data event_trigger_support[] = {
--- 85,91 ----
  	{ "TEXT SEARCH TEMPLATE", true },
  	{ "TYPE", true },
  	{ "USER MAPPING", true },
+ 	{ "TEMPLATE FOR EXTENSION", true },
  	{ "VIEW", true },
  	{ NULL, false }
  };
*** a/src/backend/commands/extension.c
--- b/src/backend/commands/extension.c
***************
*** 37,48 ****
--- 37,50 ----
  #include "catalog/pg_collation.h"
  #include "catalog/pg_depend.h"
  #include "catalog/pg_extension.h"
+ #include "catalog/pg_extension_control.h"
  #include "catalog/pg_namespace.h"
  #include "catalog/pg_type.h"
  #include "commands/alter.h"
  #include "commands/comment.h"
  #include "commands/extension.h"
  #include "commands/schemacmds.h"
+ #include "commands/template.h"
  #include "funcapi.h"
  #include "mb/pg_wchar.h"
  #include "miscadmin.h"
***************
*** 61,83 **** bool		creating_extension = false;
  Oid			CurrentExtensionObject = InvalidOid;
  
  /*
-  * Internal data structure to hold the results of parsing a control file
-  */
- typedef struct ExtensionControlFile
- {
- 	char	   *name;			/* name of the extension */
- 	char	   *directory;		/* directory for script files */
- 	char	   *default_version;	/* default install target version, if any */
- 	char	   *module_pathname;	/* string to substitute for MODULE_PATHNAME */
- 	char	   *comment;		/* comment, if any */
- 	char	   *schema;			/* target schema (allowed if !relocatable) */
- 	bool		relocatable;	/* is ALTER EXTENSION SET SCHEMA supported? */
- 	bool		superuser;		/* must be superuser to install? */
- 	int			encoding;		/* encoding of the script file, or -1 */
- 	List	   *requires;		/* names of prerequisite extensions */
- } ExtensionControlFile;
- 
- /*
   * Internal data structure for update path information
   */
  typedef struct ExtensionVersionInfo
--- 63,68 ----
***************
*** 96,106 **** static List *find_update_path(List *evi_list,
  				 ExtensionVersionInfo *evi_start,
  				 ExtensionVersionInfo *evi_target,
  				 bool reinitialize);
! static void get_available_versions_for_extension(ExtensionControlFile *pcontrol,
  									 Tuplestorestate *tupstore,
  									 TupleDesc tupdesc);
  static void ApplyExtensionUpdates(Oid extensionOid,
! 					  ExtensionControlFile *pcontrol,
  					  const char *initialVersion,
  					  List *updateVersions);
  
--- 81,91 ----
  				 ExtensionVersionInfo *evi_start,
  				 ExtensionVersionInfo *evi_target,
  				 bool reinitialize);
! static void get_available_versions_for_extension(ExtensionControl *pcontrol,
  									 Tuplestorestate *tupstore,
  									 TupleDesc tupdesc);
  static void ApplyExtensionUpdates(Oid extensionOid,
! 					  ExtensionControl *pcontrol,
  					  const char *initialVersion,
  					  List *updateVersions);
  
***************
*** 232,238 **** get_extension_schema(Oid ext_oid)
  /*
   * Utility functions to check validity of extension and version names
   */
! static void
  check_valid_extension_name(const char *extensionname)
  {
  	int			namelen = strlen(extensionname);
--- 217,223 ----
  /*
   * Utility functions to check validity of extension and version names
   */
! void
  check_valid_extension_name(const char *extensionname)
  {
  	int			namelen = strlen(extensionname);
***************
*** 370,376 **** get_extension_control_filename(const char *extname)
  }
  
  static char *
! get_extension_script_directory(ExtensionControlFile *control)
  {
  	char		sharepath[MAXPGPATH];
  	char	   *result;
--- 355,361 ----
  }
  
  static char *
! get_extension_script_directory(ExtensionControl *control)
  {
  	char		sharepath[MAXPGPATH];
  	char	   *result;
***************
*** 393,399 **** get_extension_script_directory(ExtensionControlFile *control)
  }
  
  static char *
! get_extension_aux_control_filename(ExtensionControlFile *control,
  								   const char *version)
  {
  	char	   *result;
--- 378,384 ----
  }
  
  static char *
! get_extension_aux_control_filename(ExtensionControl *control,
  								   const char *version)
  {
  	char	   *result;
***************
*** 411,417 **** get_extension_aux_control_filename(ExtensionControlFile *control,
  }
  
  static char *
! get_extension_script_filename(ExtensionControlFile *control,
  							  const char *from_version, const char *version)
  {
  	char	   *result;
--- 396,402 ----
  }
  
  static char *
! get_extension_script_filename(ExtensionControl *control,
  							  const char *from_version, const char *version)
  {
  	char	   *result;
***************
*** 432,437 **** get_extension_script_filename(ExtensionControlFile *control,
--- 417,435 ----
  	return result;
  }
  
+ /*
+  * An extension version is said to be "full" when it has a full install script,
+  * so that we know we don't need any update sequences dances either from
+  * "unpackaged" or from "default_major_version".
+  */
+ static bool
+ extension_version_is_full(ExtensionControl *control, const char *version)
+ {
+ 	char *filename = get_extension_script_filename(control, NULL, version);
+ 
+ 	return access(filename, F_OK) == 0
+ 		|| OidIsValid(get_template_oid(control->name, version, true));
+ }
  
  /*
   * Parse contents of primary or auxiliary control file, and fill in
***************
*** 443,449 **** get_extension_script_filename(ExtensionControlFile *control,
   * worry about what encoding it's in; all values are expected to be ASCII.
   */
  static void
! parse_extension_control_file(ExtensionControlFile *control,
  							 const char *version)
  {
  	char	   *filename;
--- 441,447 ----
   * worry about what encoding it's in; all values are expected to be ASCII.
   */
  static void
! parse_extension_control_file(ExtensionControl *control,
  							 const char *version)
  {
  	char	   *filename;
***************
*** 483,489 **** parse_extension_control_file(ExtensionControlFile *control,
  	FreeFile(file);
  
  	/*
! 	 * Convert the ConfigVariable list into ExtensionControlFile entries.
  	 */
  	for (item = head; item != NULL; item = item->next)
  	{
--- 481,487 ----
  	FreeFile(file);
  
  	/*
! 	 * Convert the ConfigVariable list into ExtensionControl entries.
  	 */
  	for (item = head; item != NULL; item = item->next)
  	{
***************
*** 507,512 **** parse_extension_control_file(ExtensionControlFile *control,
--- 505,514 ----
  
  			control->default_version = pstrdup(item->value);
  		}
+ 		else if (strcmp(item->name, "default_full_version") == 0)
+ 		{
+ 			control->default_full_version = pstrdup(item->value);
+ 		}
  		else if (strcmp(item->name, "module_pathname") == 0)
  		{
  			control->module_pathname = pstrdup(item->value);
***************
*** 579,594 **** parse_extension_control_file(ExtensionControlFile *control,
  /*
   * Read the primary control file for the specified extension.
   */
! static ExtensionControlFile *
  read_extension_control_file(const char *extname)
  {
! 	ExtensionControlFile *control;
  
  	/*
  	 * Set up default values.  Pointer fields are initially null.
  	 */
! 	control = (ExtensionControlFile *) palloc0(sizeof(ExtensionControlFile));
  	control->name = pstrdup(extname);
  	control->relocatable = false;
  	control->superuser = true;
  	control->encoding = -1;
--- 581,598 ----
  /*
   * Read the primary control file for the specified extension.
   */
! ExtensionControl *
  read_extension_control_file(const char *extname)
  {
! 	ExtensionControl *control;
  
  	/*
  	 * Set up default values.  Pointer fields are initially null.
  	 */
! 	control = (ExtensionControl *) palloc0(sizeof(ExtensionControl));
! 	control->ctrlOid = InvalidOid;
  	control->name = pstrdup(extname);
+ 	control->is_template = false;
  	control->relocatable = false;
  	control->superuser = true;
  	control->encoding = -1;
***************
*** 604,623 **** read_extension_control_file(const char *extname)
  /*
   * Read the auxiliary control file for the specified extension and version.
   *
!  * Returns a new modified ExtensionControlFile struct; the original struct
   * (reflecting just the primary control file) is not modified.
   */
! static ExtensionControlFile *
! read_extension_aux_control_file(const ExtensionControlFile *pcontrol,
  								const char *version)
  {
! 	ExtensionControlFile *acontrol;
  
  	/*
  	 * Flat-copy the struct.  Pointer fields share values with original.
  	 */
! 	acontrol = (ExtensionControlFile *) palloc(sizeof(ExtensionControlFile));
! 	memcpy(acontrol, pcontrol, sizeof(ExtensionControlFile));
  
  	/*
  	 * Parse the auxiliary control file, overwriting struct fields
--- 608,627 ----
  /*
   * Read the auxiliary control file for the specified extension and version.
   *
!  * Returns a new modified ExtensionControl struct; the original struct
   * (reflecting just the primary control file) is not modified.
   */
! static ExtensionControl *
! read_extension_aux_control_file(const ExtensionControl *pcontrol,
  								const char *version)
  {
! 	ExtensionControl *acontrol;
  
  	/*
  	 * Flat-copy the struct.  Pointer fields share values with original.
  	 */
! 	acontrol = (ExtensionControl *) palloc(sizeof(ExtensionControl));
! 	memcpy(acontrol, pcontrol, sizeof(ExtensionControl));
  
  	/*
  	 * Parse the auxiliary control file, overwriting struct fields
***************
*** 628,637 **** read_extension_aux_control_file(const ExtensionControlFile *pcontrol,
  }
  
  /*
   * Read an SQL script file into a string, and convert to database encoding
   */
  static char *
! read_extension_script_file(const ExtensionControlFile *control,
  						   const char *filename)
  {
  	int			src_encoding;
--- 632,706 ----
  }
  
  /*
+  * Read the control properties for given extension, either from a file on the
+  * file system or if it does not exists there, from a template catalog in
+  * pg_extension_control, if it exists.
+  *
+  * In the file system case, we get the default properties for the extension and
+  * one of them is the default_version property that allows us to know which
+  * version to install. Knowing that we can then read the right auxilliary
+  * control file to override some defaults if needs be.
+  *
+  * When reading from the catalogs, we have in pg_extension_control at most a
+  * row per version, with the whole set of properties we need to apply. So once
+  * we found the current default version to install, we don't need to read and
+  * another set of properties and override them.
+  *
+  * In both cases we return the structure ExtensionControl, which maybe
+  * should get renamed now.
+  */
+ static ExtensionControl *
+ read_extension_control(const char *extname)
+ {
+ 	char *filename;
+ 
+ 	filename = get_extension_control_filename(extname);
+ 
+ 	if (access(filename, F_OK) == -1 && errno == ENOENT)
+ 	{
+ 		/* ENOENT: let's look at the control templates */
+ 		return find_default_pg_extension_control(extname, false);
+ 	}
+ 	else
+ 		/* we let the file specific routines deal with any other error */
+ 		return read_extension_control_file(extname);
+ }
+ 
+ static ExtensionControl *
+ read_extension_aux_control(const ExtensionControl *pcontrol,
+ 						   const char *version)
+ {
+ 	if (pcontrol->is_template)
+ 	{
+ 		/* we might already have read the right version */
+ 		if (strcmp(pcontrol->default_version, version) != 0)
+ 		{
+ 			ExtensionControl *control;
+ 			/*
+ 			 * While read_extension_aux_control() override pcontrol with the
+ 			 * auxilliary control file properties, in the case when we read
+ 			 * from the catalogs, the overriding has been done already at
+ 			 * CREATE TEMPLATE time, so we only need to load a single row from
+ 			 * pg_extension_control at any time.
+ 			 */
+ 			control = find_pg_extension_control(pcontrol->name, version, true);
+ 
+ 			return control ? control : (ExtensionControl *)pcontrol;
+ 		}
+ 		else
+ 			/* pcontrol is the control file for the right version. */
+ 			return (ExtensionControl *)pcontrol;
+ 	}
+ 	else
+ 		/* read ExtensionControl from files */
+ 		return read_extension_aux_control_file(pcontrol, version);
+ }
+ 
+ /*
   * Read an SQL script file into a string, and convert to database encoding
   */
  static char *
! read_extension_script_file(const ExtensionControl *control,
  						   const char *filename)
  {
  	int			src_encoding;
***************
*** 674,681 **** read_extension_script_file(const ExtensionControlFile *control,
  /*
   * Execute given SQL string.
   *
-  * filename is used only to report errors.
-  *
   * Note: it's tempting to just use SPI to execute the string, but that does
   * not work very well.	The really serious problem is that SPI will parse,
   * analyze, and plan the whole string before executing any of it; of course
--- 743,748 ----
***************
*** 685,691 **** read_extension_script_file(const ExtensionControlFile *control,
   * could be very long.
   */
  static void
! execute_sql_string(const char *sql, const char *filename)
  {
  	List	   *raw_parsetree_list;
  	DestReceiver *dest;
--- 752,758 ----
   * could be very long.
   */
  static void
! execute_sql_string(const char *sql)
  {
  	List	   *raw_parsetree_list;
  	DestReceiver *dest;
***************
*** 770,782 **** execute_sql_string(const char *sql, const char *filename)
   * If from_version isn't NULL, it's an update
   */
  static void
! execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
  						 const char *from_version,
  						 const char *version,
  						 List *requiredSchemas,
  						 const char *schemaName, Oid schemaOid)
  {
- 	char	   *filename;
  	int			save_nestlevel;
  	StringInfoData pathbuf;
  	ListCell   *lc;
--- 837,848 ----
   * If from_version isn't NULL, it's an update
   */
  static void
! execute_extension_script(Oid extensionOid, ExtensionControl *control,
  						 const char *from_version,
  						 const char *version,
  						 List *requiredSchemas,
  						 const char *schemaName, Oid schemaOid)
  {
  	int			save_nestlevel;
  	StringInfoData pathbuf;
  	ListCell   *lc;
***************
*** 802,809 **** execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
  					 errhint("Must be superuser to update this extension.")));
  	}
  
- 	filename = get_extension_script_filename(control, from_version, version);
- 
  	/*
  	 * Force client_min_messages and log_min_messages to be at least WARNING,
  	 * so that we won't spam the user with useless NOTICE messages from common
--- 868,873 ----
***************
*** 858,865 **** execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
  	CurrentExtensionObject = extensionOid;
  	PG_TRY();
  	{
! 		char	   *c_sql = read_extension_script_file(control, filename);
! 		Datum		t_sql;
  
  		/* We use various functions that want to operate on text datums */
  		t_sql = CStringGetTextDatum(c_sql);
--- 922,944 ----
  	CurrentExtensionObject = extensionOid;
  	PG_TRY();
  	{
! 		char	*c_sql;
! 		Datum	 t_sql;
! 
! 		if (control->is_template)
! 		{
! 			c_sql = read_extension_template_script(control->name,
! 												   from_version,
! 												   version);
! 		}
! 		else
! 		{
! 			char *filename = get_extension_script_filename(control,
! 														   from_version,
! 														   version);
! 
! 			c_sql = read_extension_script_file(control, filename);
! 		}
  
  		/* We use various functions that want to operate on text datums */
  		t_sql = CStringGetTextDatum(c_sql);
***************
*** 908,914 **** execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
  		/* And now back to C string */
  		c_sql = text_to_cstring(DatumGetTextPP(t_sql));
  
! 		execute_sql_string(c_sql, filename);
  	}
  	PG_CATCH();
  	{
--- 987,993 ----
  		/* And now back to C string */
  		c_sql = text_to_cstring(DatumGetTextPP(t_sql));
  
! 		execute_sql_string(c_sql);
  	}
  	PG_CATCH();
  	{
***************
*** 997,1003 **** get_nearest_unprocessed_vertex(List *evi_list)
   * the versions that can be reached in one step from that version.
   */
  static List *
! get_ext_ver_list(ExtensionControlFile *control)
  {
  	List	   *evi_list = NIL;
  	int			extnamelen = strlen(control->name);
--- 1076,1082 ----
   * the versions that can be reached in one step from that version.
   */
  static List *
! get_ext_ver_list_from_files(ExtensionControl *control)
  {
  	List	   *evi_list = NIL;
  	int			extnamelen = strlen(control->name);
***************
*** 1053,1058 **** get_ext_ver_list(ExtensionControlFile *control)
--- 1132,1189 ----
  }
  
  /*
+  * We scan pg_extension_template for all install scripts of given extension,
+  * then pg_extension_uptmpl for all update scripts of same extension.
+  */
+ static List *
+ get_ext_ver_list_from_catalog(ExtensionControl *control)
+ {
+ 	List		*evi_list = NIL;
+ 	List		*installable, *direct_update_paths;
+ 	ListCell    *lc;
+ 
+ 	/* pg_extension_template contains install scripts */
+ 	installable = list_pg_extension_template_versions(control->name);
+ 
+ 	foreach(lc, installable)
+ 	{
+ 		ExtensionVersionInfo	*evi;
+ 		char					*vername = (char *) lfirst(lc);
+ 
+ 		evi = get_ext_ver_info(vername, &evi_list);
+ 	}
+ 
+ 	/* pg_extension_uptmpl contains upgrade scripts */
+ 	direct_update_paths = list_pg_extension_update_versions(control->name);
+ 
+ 	foreach(lc, direct_update_paths)
+ 	{
+ 		ExtensionVersionInfo	*evi, *evi2;
+ 		char					*vername = (char *) linitial(lfirst(lc));
+ 		char					*vername2 = (char *) lsecond(lfirst(lc));
+ 
+ 		evi = get_ext_ver_info(vername, &evi_list);
+ 		evi2 = get_ext_ver_info(vername2, &evi_list);
+ 		evi->reachable = lappend(evi->reachable, evi2);
+ 	}
+ 	return evi_list;
+ }
+ 
+ /*
+  * We have to implement that function twice. The first implementation deals
+  * with control files and sql scripts on the file system while the second one
+  * deals with the catalogs pg_extension_template and pg_extension_uptmpl.
+  */
+ static List *
+ get_ext_ver_list(ExtensionControl *control)
+ {
+ 	if (control->is_template)
+ 		return get_ext_ver_list_from_catalog(control);
+ 	else
+ 		return get_ext_ver_list_from_files(control);
+ }
+ 
+ /*
   * Given an initial and final version name, identify the sequence of update
   * scripts that have to be applied to perform that update.
   *
***************
*** 1060,1066 **** get_ext_ver_list(ExtensionControlFile *control)
   * version is *not* included).
   */
  static List *
! identify_update_path(ExtensionControlFile *control,
  					 const char *oldVersion, const char *newVersion)
  {
  	List	   *result;
--- 1191,1197 ----
   * version is *not* included).
   */
  static List *
! identify_update_path(ExtensionControl *control,
  					 const char *oldVersion, const char *newVersion)
  {
  	List	   *result;
***************
*** 1185,1197 **** CreateExtension(CreateExtensionStmt *stmt)
  	char	   *versionName;
  	char	   *oldVersionName;
  	Oid			extowner = GetUserId();
! 	ExtensionControlFile *pcontrol;
! 	ExtensionControlFile *control;
  	List	   *updateVersions;
  	List	   *requiredExtensions;
  	List	   *requiredSchemas;
  	Oid			extensionOid;
  	ListCell   *lc;
  
  	/* Check extension name validity before any filesystem access */
  	check_valid_extension_name(stmt->extname);
--- 1316,1329 ----
  	char	   *versionName;
  	char	   *oldVersionName;
  	Oid			extowner = GetUserId();
! 	ExtensionControl *pcontrol;
! 	ExtensionControl *control;
  	List	   *updateVersions;
  	List	   *requiredExtensions;
  	List	   *requiredSchemas;
  	Oid			extensionOid;
  	ListCell   *lc;
+ 	bool        unpackaged = false, target_version_is_full = false;
  
  	/* Check extension name validity before any filesystem access */
  	check_valid_extension_name(stmt->extname);
***************
*** 1233,1239 **** CreateExtension(CreateExtensionStmt *stmt)
  	 * any non-ASCII data, so there is no need to worry about encoding at this
  	 * point.
  	 */
! 	pcontrol = read_extension_control_file(stmt->extname);
  
  	/*
  	 * Read the statement option list
--- 1365,1371 ----
  	 * any non-ASCII data, so there is no need to worry about encoding at this
  	 * point.
  	 */
! 	pcontrol = read_extension_control(stmt->extname);
  
  	/*
  	 * Read the statement option list
***************
*** 1272,1277 **** CreateExtension(CreateExtensionStmt *stmt)
--- 1404,1414 ----
  
  	/*
  	 * Determine the version to install
+ 	 *
+ 	 * Note that in the case when we install an extension from a template, and
+ 	 * when the target version to install is given in the SQL command, we could
+ 	 * arrange the code to only scan pg_extension_control once: there's no need
+ 	 * to read any primary control row in that case. There's no harm doing so.
  	 */
  	if (d_new_version && d_new_version->arg)
  		versionName = strVal(d_new_version->arg);
***************
*** 1287,1328 **** CreateExtension(CreateExtensionStmt *stmt)
  	check_valid_version_name(versionName);
  
  	/*
  	 * Determine the (unpackaged) version to update from, if any, and then
  	 * figure out what sequence of update scripts we need to apply.
  	 */
! 	if (d_old_version && d_old_version->arg)
  	{
! 		oldVersionName = strVal(d_old_version->arg);
! 		check_valid_version_name(oldVersionName);
  
! 		if (strcmp(oldVersionName, versionName) == 0)
! 			ereport(ERROR,
! 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! 					 errmsg("FROM version must be different from installation target version \"%s\"",
! 							versionName)));
  
! 		updateVersions = identify_update_path(pcontrol,
! 											  oldVersionName,
! 											  versionName);
  
! 		if (list_length(updateVersions) == 1)
  		{
! 			/*
! 			 * Simple case where there's just one update script to run. We
! 			 * will not need any follow-on update steps.
! 			 */
! 			Assert(strcmp((char *) linitial(updateVersions), versionName) == 0);
! 			updateVersions = NIL;
  		}
  		else
  		{
! 			/*
! 			 * Multi-step sequence.  We treat this as installing the version
! 			 * that is the target of the first script, followed by successive
! 			 * updates to the later versions.
! 			 */
! 			versionName = (char *) linitial(updateVersions);
! 			updateVersions = list_delete_first(updateVersions);
  		}
  	}
  	else
--- 1424,1511 ----
  	check_valid_version_name(versionName);
  
  	/*
+ 	 * If we have a full script for the target version (or a create template),
+ 	 * we don't need to care about unpackaged or default_major_version, nor
+ 	 * about upgrade sequences.
+ 	 */
+ 	if (extension_version_is_full(pcontrol, versionName))
+ 	{
+ 		target_version_is_full = true;
+ 		oldVersionName = NULL;
+ 		updateVersions = NIL;
+ 	}
+ 	/*
  	 * Determine the (unpackaged) version to update from, if any, and then
  	 * figure out what sequence of update scripts we need to apply.
+ 	 *
+ 	 * When we have a default_full_version and the target is different from it,
+ 	 * apply the same algorithm to find a sequence of updates. If the user did
+ 	 * ask for a target version that happens to be the same as the
+ 	 * default_full_version, just install that one directly.
  	 */
! 	else if ((d_old_version && d_old_version->arg) || pcontrol->default_full_version)
  	{
! 		unpackaged = (d_old_version && d_old_version->arg);
  
! 		if (unpackaged)
! 			oldVersionName = strVal(d_old_version->arg);
! 		else
! 			oldVersionName = pcontrol->default_full_version;
  
! 		check_valid_version_name(oldVersionName);
  
! 		if (strcmp(oldVersionName, versionName) == 0)
  		{
! 			if (unpackaged)
! 			{
! 				ereport(ERROR,
! 						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! 						 errmsg("FROM version must be different from installation target version \"%s\"",
! 								versionName)));
! 			}
! 			else
! 			{
! 				/*
! 				 * CREATE EXTENSION ... VERSION = default_full_version, just
! 				 * pretend we don't have a default_full_version for the
! 				 * remaining of the code here, as that's the behavior we want
! 				 * to see happening.
! 				 */
! 				pcontrol->default_full_version = NULL;
! 				oldVersionName = NULL;
! 				updateVersions = NIL;
! 			}
  		}
  		else
  		{
! 			/* oldVersionName != versionName */
! 			updateVersions = identify_update_path(pcontrol,
! 												  oldVersionName,
! 												  versionName);
! 		}
! 
! 		/* in the create from unpackaged case, reduce the update list */
! 		if (unpackaged)
! 		{
! 			if (list_length(updateVersions) == 1)
! 			{
! 				/*
! 				 * Simple case where there's just one update script to run. We
! 				 * will not need any follow-on update steps.
! 				 */
! 				Assert(strcmp((char *) linitial(updateVersions), versionName) == 0);
! 				updateVersions = NIL;
! 			}
! 			else
! 			{
! 				/*
! 				 * Multi-step sequence.  We treat this as installing the version
! 				 * that is the target of the first script, followed by successive
! 				 * updates to the later versions.
! 				 */
! 				versionName = (char *) linitial(updateVersions);
! 				updateVersions = list_delete_first(updateVersions);
! 			}
  		}
  	}
  	else
***************
*** 1334,1340 **** CreateExtension(CreateExtensionStmt *stmt)
  	/*
  	 * Fetch control parameters for installation target version
  	 */
! 	control = read_extension_aux_control_file(pcontrol, versionName);
  
  	/*
  	 * Determine the target schema to install the extension into
--- 1517,1523 ----
  	/*
  	 * Fetch control parameters for installation target version
  	 */
! 	control = read_extension_aux_control(pcontrol, versionName);
  
  	/*
  	 * Determine the target schema to install the extension into
***************
*** 1448,1454 **** CreateExtension(CreateExtensionStmt *stmt)
  										versionName,
  										PointerGetDatum(NULL),
  										PointerGetDatum(NULL),
! 										requiredExtensions);
  
  	/*
  	 * Apply any control-file comment on extension
--- 1631,1638 ----
  										versionName,
  										PointerGetDatum(NULL),
  										PointerGetDatum(NULL),
! 										requiredExtensions,
! 										control->ctrlOid);
  
  	/*
  	 * Apply any control-file comment on extension
***************
*** 1458,1476 **** CreateExtension(CreateExtensionStmt *stmt)
  
  	/*
  	 * Execute the installation script file
! 	 */
! 	execute_extension_script(extensionOid, control,
! 							 oldVersionName, versionName,
! 							 requiredSchemas,
! 							 schemaName, schemaOid);
! 
! 	/*
  	 * If additional update scripts have to be executed, apply the updates as
  	 * though a series of ALTER EXTENSION UPDATE commands were given
  	 */
! 	ApplyExtensionUpdates(extensionOid, pcontrol,
! 						  versionName, updateVersions);
  
  	return extensionOid;
  }
  
--- 1642,1678 ----
  
  	/*
  	 * Execute the installation script file
! 	 *
  	 * If additional update scripts have to be executed, apply the updates as
  	 * though a series of ALTER EXTENSION UPDATE commands were given
  	 */
! 	if (target_version_is_full)
! 	{
! 		execute_extension_script(extensionOid, control,
! 								 NULL, versionName,
! 								 requiredSchemas,
! 								 schemaName, schemaOid);
! 	}
! 	else if (pcontrol->default_full_version && !unpackaged)
! 	{
! 		execute_extension_script(extensionOid, control,
! 								 NULL, oldVersionName,
! 								 requiredSchemas,
! 								 schemaName, schemaOid);
! 
! 		ApplyExtensionUpdates(extensionOid, pcontrol,
! 							  oldVersionName, updateVersions);
! 	}
! 	else
! 	{
! 		execute_extension_script(extensionOid, control,
! 								 oldVersionName, versionName,
! 								 requiredSchemas,
! 								 schemaName, schemaOid);
  
+ 		ApplyExtensionUpdates(extensionOid, pcontrol,
+ 							  versionName, updateVersions);
+ 	}
  	return extensionOid;
  }
  
***************
*** 1491,1497 **** Oid
  InsertExtensionTuple(const char *extName, Oid extOwner,
  					 Oid schemaOid, bool relocatable, const char *extVersion,
  					 Datum extConfig, Datum extCondition,
! 					 List *requiredExtensions)
  {
  	Oid			extensionOid;
  	Relation	rel;
--- 1693,1699 ----
  InsertExtensionTuple(const char *extName, Oid extOwner,
  					 Oid schemaOid, bool relocatable, const char *extVersion,
  					 Datum extConfig, Datum extCondition,
! 					 List *requiredExtensions, Oid ctrlOid)
  {
  	Oid			extensionOid;
  	Relation	rel;
***************
*** 1561,1566 **** InsertExtensionTuple(const char *extName, Oid extOwner,
--- 1763,1781 ----
  
  		recordDependencyOn(&myself, &otherext, DEPENDENCY_NORMAL);
  	}
+ 
+ 	/* Record dependency on pg_extension_control, if created from a template */
+ 	if (OidIsValid(ctrlOid))
+ 	{
+ 		ObjectAddress pg_extension_control;
+ 
+ 		pg_extension_control.classId = ExtensionControlRelationId;
+ 		pg_extension_control.objectId = ctrlOid;
+ 		pg_extension_control.objectSubId = 0;
+ 
+ 		recordDependencyOn(&myself, &pg_extension_control, DEPENDENCY_NORMAL);
+ 	}
+ 
  	/* Post creation hook for new extension */
  	InvokeObjectPostCreateHook(ExtensionRelationId, extensionOid, 0);
  
***************
*** 1631,1643 **** Datum
  pg_available_extensions(PG_FUNCTION_ARGS)
  {
  	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
! 	TupleDesc	tupdesc;
! 	Tuplestorestate *tupstore;
! 	MemoryContext per_query_ctx;
! 	MemoryContext oldcontext;
! 	char	   *location;
! 	DIR		   *dir;
! 	struct dirent *de;
  
  	/* check to see if caller supports us returning a tuplestore */
  	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
--- 1846,1860 ----
  pg_available_extensions(PG_FUNCTION_ARGS)
  {
  	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
! 	TupleDesc			 tupdesc;
! 	Tuplestorestate		*tupstore;
! 	MemoryContext		 per_query_ctx;
! 	MemoryContext		 oldcontext;
! 	char				*location;
! 	DIR					*dir;
! 	struct dirent		*de;
! 	List				*templates;
! 	ListCell			*lc;
  
  	/* check to see if caller supports us returning a tuplestore */
  	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
***************
*** 1680,1686 **** pg_available_extensions(PG_FUNCTION_ARGS)
  	{
  		while ((de = ReadDir(dir, location)) != NULL)
  		{
! 			ExtensionControlFile *control;
  			char	   *extname;
  			Datum		values[3];
  			bool		nulls[3];
--- 1897,1903 ----
  	{
  		while ((de = ReadDir(dir, location)) != NULL)
  		{
! 			ExtensionControl *control;
  			char	   *extname;
  			Datum		values[3];
  			bool		nulls[3];
***************
*** 1721,1726 **** pg_available_extensions(PG_FUNCTION_ARGS)
--- 1938,1966 ----
  		FreeDir(dir);
  	}
  
+     /* add in the extension we can install from a template */
+ 	templates = pg_extension_default_controls();
+ 
+     foreach(lc, templates)
+ 	{
+ 		char	*name = (char *)linitial(lfirst(lc));
+ 		char	*vers = (char *)lsecond(lfirst(lc));
+ 		Datum	 values[3];
+ 		bool	 nulls[3];
+ 
+ 		memset(values, 0, sizeof(values));
+ 		memset(nulls, 0, sizeof(nulls));
+ 
+ 		/* name */
+ 		values[0] = DirectFunctionCall1(namein, CStringGetDatum(name));
+ 		/* default_version */
+ 		values[1] = CStringGetTextDatum(vers);
+ 		/* comment */
+ 		nulls[2] = true;
+ 
+ 		tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ 	}
+ 
  	/* clean up and return the tuplestore */
  	tuplestore_donestoring(tupstore);
  
***************
*** 1789,1795 **** pg_available_extension_versions(PG_FUNCTION_ARGS)
  	{
  		while ((de = ReadDir(dir, location)) != NULL)
  		{
! 			ExtensionControlFile *control;
  			char	   *extname;
  
  			if (!is_extension_control_filename(de->d_name))
--- 2029,2035 ----
  	{
  		while ((de = ReadDir(dir, location)) != NULL)
  		{
! 			ExtensionControl *control;
  			char	   *extname;
  
  			if (!is_extension_control_filename(de->d_name))
***************
*** 1824,1830 **** pg_available_extension_versions(PG_FUNCTION_ARGS)
   *		read versions of one extension, add rows to tupstore
   */
  static void
! get_available_versions_for_extension(ExtensionControlFile *pcontrol,
  									 Tuplestorestate *tupstore,
  									 TupleDesc tupdesc)
  {
--- 2064,2070 ----
   *		read versions of one extension, add rows to tupstore
   */
  static void
! get_available_versions_for_extension(ExtensionControl *pcontrol,
  									 Tuplestorestate *tupstore,
  									 TupleDesc tupdesc)
  {
***************
*** 1838,1844 **** get_available_versions_for_extension(ExtensionControlFile *pcontrol,
  	/* Note this will fail if script directory doesn't exist */
  	while ((de = ReadDir(dir, location)) != NULL)
  	{
! 		ExtensionControlFile *control;
  		char	   *vername;
  		Datum		values[7];
  		bool		nulls[7];
--- 2078,2084 ----
  	/* Note this will fail if script directory doesn't exist */
  	while ((de = ReadDir(dir, location)) != NULL)
  	{
! 		ExtensionControl *control;
  		char	   *vername;
  		Datum		values[7];
  		bool		nulls[7];
***************
*** 1935,1941 **** pg_extension_update_paths(PG_FUNCTION_ARGS)
  	MemoryContext per_query_ctx;
  	MemoryContext oldcontext;
  	List	   *evi_list;
! 	ExtensionControlFile *control;
  	ListCell   *lc1;
  
  	/* Check extension name validity before any filesystem access */
--- 2175,2181 ----
  	MemoryContext per_query_ctx;
  	MemoryContext oldcontext;
  	List	   *evi_list;
! 	ExtensionControl *control;
  	ListCell   *lc1;
  
  	/* Check extension name validity before any filesystem access */
***************
*** 1968,1974 **** pg_extension_update_paths(PG_FUNCTION_ARGS)
  	MemoryContextSwitchTo(oldcontext);
  
  	/* Read the extension's control file */
! 	control = read_extension_control_file(NameStr(*extname));
  
  	/* Extract the version update graph from the script directory */
  	evi_list = get_ext_ver_list(control);
--- 2208,2214 ----
  	MemoryContextSwitchTo(oldcontext);
  
  	/* Read the extension's control file */
! 	control = read_extension_control(NameStr(*extname));
  
  	/* Extract the version update graph from the script directory */
  	evi_list = get_ext_ver_list(control);
***************
*** 2585,2591 **** ExecAlterExtensionStmt(AlterExtensionStmt *stmt)
  	DefElem    *d_new_version = NULL;
  	char	   *versionName;
  	char	   *oldVersionName;
! 	ExtensionControlFile *control;
  	Oid			extensionOid;
  	Relation	extRel;
  	ScanKeyData key[1];
--- 2825,2831 ----
  	DefElem    *d_new_version = NULL;
  	char	   *versionName;
  	char	   *oldVersionName;
! 	ExtensionControl *control;
  	Oid			extensionOid;
  	Relation	extRel;
  	ScanKeyData key[1];
***************
*** 2651,2657 **** ExecAlterExtensionStmt(AlterExtensionStmt *stmt)
  	 * any non-ASCII data, so there is no need to worry about encoding at this
  	 * point.
  	 */
! 	control = read_extension_control_file(stmt->extname);
  
  	/*
  	 * Read the statement option list
--- 2891,2897 ----
  	 * any non-ASCII data, so there is no need to worry about encoding at this
  	 * point.
  	 */
! 	control = read_extension_control(stmt->extname);
  
  	/*
  	 * Read the statement option list
***************
*** 2726,2732 **** ExecAlterExtensionStmt(AlterExtensionStmt *stmt)
   */
  static void
  ApplyExtensionUpdates(Oid extensionOid,
! 					  ExtensionControlFile *pcontrol,
  					  const char *initialVersion,
  					  List *updateVersions)
  {
--- 2966,2972 ----
   */
  static void
  ApplyExtensionUpdates(Oid extensionOid,
! 					  ExtensionControl *pcontrol,
  					  const char *initialVersion,
  					  List *updateVersions)
  {
***************
*** 2736,2742 **** ApplyExtensionUpdates(Oid extensionOid,
  	foreach(lcv, updateVersions)
  	{
  		char	   *versionName = (char *) lfirst(lcv);
! 		ExtensionControlFile *control;
  		char	   *schemaName;
  		Oid			schemaOid;
  		List	   *requiredExtensions;
--- 2976,2982 ----
  	foreach(lcv, updateVersions)
  	{
  		char	   *versionName = (char *) lfirst(lcv);
! 		ExtensionControl *control;
  		char	   *schemaName;
  		Oid			schemaOid;
  		List	   *requiredExtensions;
***************
*** 2755,2761 **** ApplyExtensionUpdates(Oid extensionOid,
  		/*
  		 * Fetch parameters for specific version (pcontrol is not changed)
  		 */
! 		control = read_extension_aux_control_file(pcontrol, versionName);
  
  		/* Find the pg_extension tuple */
  		extRel = heap_open(ExtensionRelationId, RowExclusiveLock);
--- 2995,3001 ----
  		/*
  		 * Fetch parameters for specific version (pcontrol is not changed)
  		 */
! 		control = read_extension_aux_control(pcontrol, versionName);
  
  		/* Find the pg_extension tuple */
  		extRel = heap_open(ExtensionRelationId, RowExclusiveLock);
*** /dev/null
--- b/src/backend/commands/template.c
***************
*** 0 ****
--- 1,2323 ----
+ /*-------------------------------------------------------------------------
+  *
+  * template.c
+  *	  Commands to manipulate templates
+  *
+  * Extension Templates in PostgreSQL allow creation of Extension from the
+  * protocol only.
+  *
+  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  *
+  * IDENTIFICATION
+  *	  src/backend/commands/template.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/heapam.h"
+ #include "access/htup_details.h"
+ #include "access/sysattr.h"
+ #include "access/xact.h"
+ #include "catalog/dependency.h"
+ #include "catalog/indexing.h"
+ #include "catalog/namespace.h"
+ #include "catalog/objectaccess.h"
+ #include "catalog/pg_depend.h"
+ #include "catalog/pg_extension.h"
+ #include "catalog/pg_extension_control.h"
+ #include "catalog/pg_extension_template.h"
+ #include "catalog/pg_extension_uptmpl.h"
+ #include "catalog/pg_namespace.h"
+ #include "catalog/pg_type.h"
+ #include "commands/alter.h"
+ #include "commands/extension.h"
+ #include "commands/template.h"
+ #include "funcapi.h"
+ #include "miscadmin.h"
+ #include "tcop/utility.h"
+ #include "utils/acl.h"
+ #include "utils/builtins.h"
+ #include "utils/fmgroids.h"
+ #include "utils/lsyscache.h"
+ #include "utils/rel.h"
+ #include "utils/snapmgr.h"
+ #include "utils/tqual.h"
+ 
+ static Oid InsertExtensionControlTuple(Oid owner,
+ 									   ExtensionControl *control,
+ 									   const char *version);
+ 
+ static Oid InsertExtensionTemplateTuple(Oid owner,
+ 										ExtensionControl *control,
+ 										const char *version,
+ 										const char *script);
+ 
+ static Oid InsertExtensionUpTmplTuple(Oid owner,
+ 									  const char *extname,
+ 									  ExtensionControl *control,
+ 									  const char *from,
+ 									  const char *to,
+ 									  const char *script);
+ 
+ static Oid AlterTemplateSetDefault(const char *extname, const char *version);
+ static Oid AlterTemplateSetDefaultFull(const char *extname,
+ 									   const char *version);
+ static Oid AlterTemplateSetControl(const char *extname,
+ 								   const char *version,
+ 								   List *options);
+ 
+ static Oid AlterTemplateSetScript(const char *extname,
+ 								  const char *version, const char *script);
+ static Oid AlterUpTpmlSetScript(const char *extname,
+ 								const char *from,
+ 								const char *to,
+ 								const char *script);
+ 
+ static Oid modify_pg_extension_control_default(const char *extname,
+ 											   const char *version,
+ 											   bool value);
+ 
+ static Oid modify_pg_extension_control_default_full(const char *extname,
+ 													const char *version,
+ 													bool value);
+ 
+ static ExtensionControl *read_pg_extension_control(const char *extname,
+ 												   Relation rel,
+ 												   HeapTuple tuple);
+ 
+ /*
+  * The grammar accumulates control properties into a DefElem list that we have
+  * to process in multiple places.
+  */
+ static void
+ parse_statement_control_defelems(ExtensionControl *control, List *defelems)
+ {
+ 	ListCell	*lc;
+ 	DefElem		*d_schema	   = NULL;
+ 	DefElem		*d_superuser   = NULL;
+ 	DefElem		*d_relocatable = NULL;
+ 	DefElem		*d_requires	   = NULL;
+ 
+ 	/*
+ 	 * Read the statement option list
+ 	 */
+ 	foreach(lc, defelems)
+ 	{
+ 		DefElem    *defel = (DefElem *) lfirst(lc);
+ 
+ 		if (strcmp(defel->defname, "schema") == 0)
+ 		{
+ 			if (d_schema)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						 errmsg("conflicting or redundant options")));
+ 			d_schema = defel;
+ 
+ 			control->schema = strVal(d_schema->arg);
+ 		}
+ 		else if (strcmp(defel->defname, "superuser") == 0)
+ 		{
+ 			if (d_superuser)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						 errmsg("conflicting or redundant options")));
+ 			d_superuser = defel;
+ 
+ 			control->superuser = intVal(d_superuser->arg) != 0;
+ 		}
+ 		else if (strcmp(defel->defname, "relocatable") == 0)
+ 		{
+ 			if (d_relocatable)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						 errmsg("conflicting or redundant options")));
+ 			d_relocatable = defel;
+ 
+ 			control->relocatable = intVal(d_relocatable->arg) != 0;
+ 		}
+ 		else if (strcmp(defel->defname, "requires") == 0)
+ 		{
+ 			if (d_requires)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						 errmsg("conflicting or redundant options")));
+ 			d_requires = defel;
+ 
+ 			if (!SplitIdentifierString(pstrdup(strVal(d_requires->arg)),
+ 									   ',',
+ 									   &control->requires))
+ 			{
+ 				/* syntax error in name list */
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 						 errmsg("parameter \"requires\" must be a list of extension names")));
+ 			}
+ 		}
+ 		else
+ 			elog(ERROR, "unrecognized option: %s", defel->defname);
+ 	}
+ }
+ 
+ /*
+  * CREATE TEMPLATE FOR EXTENSION
+  *
+  * Routing function, the statement can be either about a template for creating
+  * an extension or a template for updating and extension.
+  */
+ Oid
+ CreateTemplate(CreateExtTemplateStmt *stmt)
+ {
+ 	switch (stmt->tmpltype)
+ 	{
+ 		case TEMPLATE_CREATE_EXTENSION:
+ 			return CreateExtensionTemplate(stmt);
+ 
+ 		case TEMPLATE_UPDATE_EXTENSION:
+ 			return CreateExtensionUpdateTemplate(stmt);
+ 	}
+ 	/* keep compiler happy */
+ 	return InvalidOid;
+ }
+ 
+ /*
+  * CREATE TEMPLATE FOR EXTENSION
+  *
+  * Create a template for an extension's given version.
+  */
+ Oid
+ CreateExtensionTemplate(CreateExtTemplateStmt *stmt)
+ {
+ 	ExtensionControl *default_version;
+     Oid			 extTemplateOid;
+ 	Oid			 owner		   = GetUserId();
+ 	ExtensionControl *control;
+ 
+ 	/* Check extension name validity before any filesystem access */
+ 	check_valid_extension_name(stmt->extname);
+ 
+ 	/*
+ 	 * Check for duplicate extension name in the pg_extension catalogs. Any
+ 	 * extension that already is known in the catalogs needs no template for
+ 	 * creating it in the first place.
+ 	 */
+ 	if (get_extension_oid(stmt->extname, true) != InvalidOid)
+ 	{
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_DUPLICATE_OBJECT),
+ 				 errmsg("extension \"%s\" already exists",
+ 						stmt->extname)));
+ 	}
+ 
+ 	/*
+ 	 * Check for duplicate template for given extension and version. The unique
+ 	 * index on pg_extension_template(extname, version) would catch this
+ 	 * anyway, and serves as a backstop in case of race conditions; but this is
+ 	 * a friendlier error message, and besides we need a check to support IF
+ 	 * NOT EXISTS.
+ 	 */
+ 	if (get_template_oid(stmt->extname, stmt->version, true) != InvalidOid)
+ 	{
+ 		if (stmt->if_not_exists)
+ 		{
+ 			ereport(NOTICE,
+ 					(errcode(ERRCODE_DUPLICATE_OBJECT),
+ 					 errmsg("template for extension \"%s\" version \"%s\" already exists, skipping",
+ 							stmt->extname, stmt->version)));
+ 			return InvalidOid;
+ 		}
+ 		else
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_DUPLICATE_OBJECT),
+ 					 errmsg("template for extension \"%s\" version \"%s\" already exists",
+ 							stmt->extname, stmt->version)));
+ 	}
+ 
+ 	/*
+ 	 * Check that no control file of the same extension's name is already
+ 	 * available on disk, as a friendliness service to our users. Between
+ 	 * CREATE TEMPLATE FOR EXTENSION and CREATE EXTENSION time, some new file
+ 	 * might have been added to the file-system and would then be prefered, but
+ 	 * at least we tried to be as nice as we possibly can.
+ 	 */
+ 	PG_TRY();
+ 	{
+ 		control = read_extension_control_file(stmt->extname);
+ 	}
+ 	PG_CATCH();
+ 	{
+ 		/* no control file found is good news for us */
+ 		control = NULL;
+ 	}
+ 	PG_END_TRY();
+ 
+ 	if (control)
+ 	{
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_DUPLICATE_OBJECT),
+ 					 errmsg("extension \"%s\" is already available",
+ 							stmt->extname)));
+ 	}
+ 
+ 	/* Now read the control properties from the statement */
+  	control = (ExtensionControl *) palloc0(sizeof(ExtensionControl));
+ 	control->name = pstrdup(stmt->extname);
+ 	parse_statement_control_defelems(control, stmt->control);
+ 
+ 	/*
+ 	 * Check that there's no other pg_extension_control row already claiming to
+ 	 * be the default for this extension, when the statement claims to be the
+ 	 * default.
+ 	 */
+ 	default_version = find_default_pg_extension_control(control->name, true);
+ 
+ 	if (stmt->default_version)
+ 	{
+ 		if (default_version)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_DUPLICATE_OBJECT),
+ 					 errmsg("extension \"%s\" already has a default control template",
+ 							control->name),
+ 					 errdetail("default version is \"%s\"",
+ 							   default_version->default_version)));
+ 
+ 		/* no pre-existing */
+ 		control->default_version = pstrdup(stmt->version);
+ 	}
+ 	else
+ 	{
+ 		/*
+ 		 * No explicit default has been given in the command, and we didn't
+ 		 * find one in the catalogs (it must be the first time we hear about
+ 		 * that very extension): we maintain our invariant that we must have a
+ 		 * single line per extension in pg_extension_control where ctldefault
+ 		 * is true.
+ 		 */
+ 		if (default_version == NULL)
+ 			control->default_version = pstrdup(stmt->version);
+ 	}
+ 
+ 	/*
+ 	 * In the control structure, find_default_pg_extension_control() has
+ 	 * stuffed the current default full version of the extension, which might
+ 	 * be different from the default version.
+ 	 *
+ 	 * When creating the first template for an extension, we don't have a
+ 	 * default_full_version set yet. To maintain our invariant that we always
+ 	 * have a single version of the extension templates always as the default
+ 	 * full version, if no default_full_version has been found, forcibly set it
+ 	 * now.
+ 	 */
+ 	if (default_version == NULL ||
+ 		default_version->default_full_version == NULL)
+ 	{
+ 		control->default_full_version = stmt->version;
+ 	}
+ 
+ 	extTemplateOid =  InsertExtensionTemplateTuple(owner,
+ 												   control,
+ 												   stmt->version,
+ 												   stmt->script);
+ 
+ 	/* Check that we have a default version target now */
+ 	CommandCounterIncrement();
+ 	find_default_pg_extension_control(stmt->extname, false);
+ 
+ 	return extTemplateOid;
+ }
+ 
+ /*
+  * CREATE TEMPLATE FOR UPDATE OF EXTENSION
+  */
+ Oid
+ CreateExtensionUpdateTemplate(CreateExtTemplateStmt *stmt)
+ {
+ 	Oid			 owner = GetUserId();
+ 	ExtensionControl *control;
+ 
+ 	/* Check extension name validity before any filesystem access */
+ 	check_valid_extension_name(stmt->extname);
+ 
+ 	/*
+ 	 * Check that a template for installing extension already exists in the
+ 	 * catalogs. Do not enforce that we have a complete path upgrade path at
+ 	 * template creation time, that will get checked at CREATE EXTENSION time.
+ 	 */
+ 	(void) can_create_extension_from_template(stmt->extname, false);
+ 
+ 	/*
+ 	 * Check for duplicate template for given extension and versions. The
+ 	 * unique index on pg_extension_uptmpl(uptname, uptfrom, uptto) would catch
+ 	 * this anyway, and serves as a backstop in case of race conditions; but
+ 	 * this is a friendlier error message, and besides we need a check to
+ 	 * support IF NOT EXISTS.
+ 	 */
+ 	if (get_uptmpl_oid(stmt->extname, stmt->from, stmt->to, true) != InvalidOid)
+ 	{
+ 		if (stmt->if_not_exists)
+ 		{
+ 			ereport(NOTICE,
+ 					(errcode(ERRCODE_DUPLICATE_OBJECT),
+ 					 errmsg("template for extension \"%s\" update from version \"%s\" to version \"%s\" already exists, skipping",
+ 							stmt->extname, stmt->from, stmt->to)));
+ 			return InvalidOid;
+ 		}
+ 		else
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_DUPLICATE_OBJECT),
+ 					 errmsg("template for extension \"%s\" update from version \"%s\" to version \"%s\" already exists",
+ 							stmt->extname, stmt->from, stmt->to)));
+ 	}
+ 
+ 	/*
+ 	 * Check that no control file of the same extension's name is already
+ 	 * available on disk, as a friendliness service to our users. Between
+ 	 * CREATE TEMPLATE FOR EXTENSION and CREATE EXTENSION time, some new file
+ 	 * might have been added to the file-system and would then be prefered, but
+ 	 * at least we tried to be as nice as we possibly can.
+ 	 */
+ 	PG_TRY();
+ 	{
+ 		control = read_extension_control_file(stmt->extname);
+ 	}
+ 	PG_CATCH();
+ 	{
+ 		/* no control file found is good news for us */
+ 		control = NULL;
+ 	}
+ 	PG_END_TRY();
+ 
+ 	if (control)
+ 	{
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_DUPLICATE_OBJECT),
+ 					 errmsg("extension \"%s\" is already available",
+ 							stmt->extname)));
+ 	}
+ 
+ 	/* Now read the (optional) control properties from the statement */
+ 	if (stmt->control)
+ 	{
+  	    control = (ExtensionControl *) palloc0(sizeof(ExtensionControl));
+ 	    control->name = pstrdup(stmt->extname);
+ 
+ 		parse_statement_control_defelems(control, stmt->control);
+ 	}
+ 	else
+ 	{
+ 		/*
+ 		 *
+ 		 * To allow for ALTER command to be able to change the control
+ 		 * properties of the given extension for the target version (to) here,
+ 		 * when no control properties have been given on the command line, copy
+ 		 * those of the version we upgrade from (from).
+ 		 */
+ 		control = find_pg_extension_control(stmt->extname, stmt->from, false);
+ 	}
+ 
+ 	return InsertExtensionUpTmplTuple(owner, stmt->extname, control,
+ 									  stmt->from, stmt->to, stmt->script);
+ }
+ 
+ /*
+  * Utility function to build a text[] from the List *requires option.
+  */
+ static Datum
+ construct_control_requires_datum(List *requires)
+ {
+ 	Datum	   *datums;
+ 	int			ndatums;
+ 	ArrayType  *a;
+ 	ListCell   *lc;
+ 
+ 	ndatums = list_length(requires);
+ 	datums = (Datum *) palloc(ndatums * sizeof(Datum));
+ 	ndatums = 0;
+ 	foreach(lc, requires)
+ 	{
+ 		char	   *curreq = (char *) lfirst(lc);
+ 
+ 		datums[ndatums++] =
+ 			DirectFunctionCall1(namein, CStringGetDatum(curreq));
+ 	}
+ 	a = construct_array(datums, ndatums,
+ 						NAMEOID,
+ 						NAMEDATALEN, false, 'c');
+ 
+ 	return PointerGetDatum(a);
+ }
+ 
+ /*
+  * InsertExtensionControlTuple
+  *
+  * Insert the new pg_extension_control row and register its dependency to its
+  * owner. Return the OID assigned to the new row.
+  */
+ static Oid
+ InsertExtensionControlTuple(Oid owner,
+ 							ExtensionControl *control,
+ 							const char *version)
+ {
+ 	Oid			extControlOid;
+ 	Relation	rel;
+ 	Datum		values[Natts_pg_extension_control];
+ 	bool		nulls[Natts_pg_extension_control];
+ 	HeapTuple	tuple;
+ 
+ 	/*
+ 	 * Build and insert the pg_extension_control tuple
+ 	 */
+ 	rel = heap_open(ExtensionControlRelationId, RowExclusiveLock);
+ 
+ 	memset(values, 0, sizeof(values));
+ 	memset(nulls, 0, sizeof(nulls));
+ 
+ 	values[Anum_pg_extension_control_ctlname - 1] =
+ 		DirectFunctionCall1(namein, CStringGetDatum(control->name));
+ 
+ 	values[Anum_pg_extension_control_ctlowner - 1] =
+ 		ObjectIdGetDatum(owner);
+ 
+ 	values[Anum_pg_extension_control_ctlrelocatable - 1] =
+ 		BoolGetDatum(control->relocatable);
+ 
+ 	values[Anum_pg_extension_control_ctlsuperuser - 1] =
+ 		BoolGetDatum(control->superuser);
+ 
+ 	if (control->schema == NULL)
+ 		nulls[Anum_pg_extension_control_ctlnamespace - 1] = true;
+ 	else
+ 		values[Anum_pg_extension_control_ctlnamespace - 1] =
+ 			DirectFunctionCall1(namein, CStringGetDatum((control->schema)));
+ 
+ 	values[Anum_pg_extension_control_ctlversion - 1] =
+ 		CStringGetTextDatum(version);
+ 
+ 	/*
+ 	 * We only register that this pg_extension_control row is the default for
+ 	 * the given extension. Necessary controls must have been made before.
+ 	 */
+ 	if (control->default_version == NULL)
+ 		values[Anum_pg_extension_control_ctldefault - 1] = false;
+ 	else
+ 		values[Anum_pg_extension_control_ctldefault - 1] = true;
+ 
+ 	if (control->default_full_version == NULL)
+ 		values[Anum_pg_extension_control_ctldefaultfull - 1] = false;
+ 	else
+ 		values[Anum_pg_extension_control_ctldefaultfull - 1] = true;
+ 
+ 	if (control->requires == NULL)
+ 		nulls[Anum_pg_extension_control_ctlrequires - 1] = true;
+ 	else
+ 		values[Anum_pg_extension_control_ctlrequires - 1] =
+ 			construct_control_requires_datum(control->requires);
+ 
+ 	tuple = heap_form_tuple(rel->rd_att, values, nulls);
+ 
+ 	extControlOid = simple_heap_insert(rel, tuple);
+ 	CatalogUpdateIndexes(rel, tuple);
+ 
+ 	heap_freetuple(tuple);
+ 	heap_close(rel, RowExclusiveLock);
+ 
+ 	/*
+ 	 * Record dependencies on owner.
+ 	 *
+ 	 * When we create the extension template and control file, the target
+ 	 * extension, its schema and requirements usually do not exist in the
+ 	 * database. Don't even think about registering a dependency from the
+ 	 * template.
+ 	 */
+ 	recordDependencyOnOwner(ExtensionControlRelationId, extControlOid, owner);
+ 
+ 	/* Post creation hook for new extension control */
+ 	InvokeObjectPostCreateHook(ExtensionControlRelationId, extControlOid, 0);
+ ;
+ 	return extControlOid;
+ }
+ 
+ /*
+  * InsertExtensionTemplateTuple
+  *
+  * Insert the new pg_extension_template row and register its dependencies.
+  * Return the OID assigned to the new row.
+  */
+ static Oid
+ InsertExtensionTemplateTuple(Oid owner, ExtensionControl *control,
+ 							 const char *version, const char *script)
+ {
+ 	Oid			extControlOid, extTemplateOid;
+ 	Relation	rel;
+ 	Datum		values[Natts_pg_extension_template];
+ 	bool		nulls[Natts_pg_extension_template];
+ 	HeapTuple	tuple;
+ 	ObjectAddress myself, ctrl;
+ 
+ 	/* First create the companion extension control entry */
+ 	extControlOid = InsertExtensionControlTuple(owner, control, version);
+ 
+ 	/*
+ 	 * Build and insert the pg_extension_template tuple
+ 	 */
+ 	rel = heap_open(ExtensionTemplateRelationId, RowExclusiveLock);
+ 
+ 	memset(values, 0, sizeof(values));
+ 	memset(nulls, 0, sizeof(nulls));
+ 
+ 	values[Anum_pg_extension_template_tplname - 1] =
+ 		DirectFunctionCall1(namein, CStringGetDatum(control->name));
+ 
+ 	values[Anum_pg_extension_template_tplowner - 1] =
+ 		ObjectIdGetDatum(owner);
+ 
+ 	values[Anum_pg_extension_template_tplversion - 1] =
+ 		CStringGetTextDatum(version);
+ 
+ 	values[Anum_pg_extension_template_tplscript - 1] =
+ 		CStringGetTextDatum(script);
+ 
+ 	tuple = heap_form_tuple(rel->rd_att, values, nulls);
+ 
+ 	extTemplateOid = simple_heap_insert(rel, tuple);
+ 	CatalogUpdateIndexes(rel, tuple);
+ 
+ 	heap_freetuple(tuple);
+ 	heap_close(rel, RowExclusiveLock);
+ 
+ 	/*
+ 	 * Record dependencies on owner only.
+ 	 *
+ 	 * When we create the extension template and control file, the target
+ 	 * extension, its schema and requirements usually do not exist in the
+ 	 * database. Don't even think about registering a dependency from the
+ 	 * template.
+ 	 */
+ 	recordDependencyOnOwner(ExtensionTemplateRelationId, extTemplateOid, owner);
+ 
+ 	myself.classId = ExtensionTemplateRelationId;
+ 	myself.objectId = extTemplateOid;
+ 	myself.objectSubId = 0;
+ 
+ 	/* record he dependency between the control row and the template row */
+ 	ctrl.classId = ExtensionControlRelationId;
+ 	ctrl.objectId = extControlOid;
+ 	ctrl.objectSubId = 0;
+ 
+ 	recordDependencyOn(&ctrl, &myself, DEPENDENCY_INTERNAL);
+ 
+ 	/* Post creation hook for new extension control */
+ 	InvokeObjectPostCreateHook(ExtensionTemplateRelationId, extTemplateOid, 0);
+ 
+ 	return extTemplateOid;
+ }
+ 
+ /*
+  * InsertExtensionUpTmplTuple
+  *
+  * Insert the new pg_extension_uptmpl row and register its dependencies.
+  * Return the OID assigned to the new row.
+  */
+ static Oid
+ InsertExtensionUpTmplTuple(Oid owner,
+ 						   const char *extname,
+ 						   ExtensionControl *control,
+ 						   const char *from,
+ 						   const char *to,
+ 						   const char *script)
+ {
+ 	Oid			extControlOid, extUpTmplOid;
+ 	Relation	rel;
+ 	Datum		values[Natts_pg_extension_uptmpl];
+ 	bool		nulls[Natts_pg_extension_uptmpl];
+ 	HeapTuple	tuple;
+ 	ObjectAddress myself, ctrl;
+ 
+ 	/*
+ 	 * First create the companion extension control entry, if any. In the case
+ 	 * of an Update Template the comanion control entry is somilar in scope to
+ 	 * a secondary control file, and is attached to the target version.
+ 	 */
+ 	extControlOid = InsertExtensionControlTuple(owner, control, to);
+ 
+ 	/*
+ 	 * Build and insert the pg_extension_uptmpl tuple
+ 	 */
+ 	rel = heap_open(ExtensionUpTmplRelationId, RowExclusiveLock);
+ 
+ 	memset(values, 0, sizeof(values));
+ 	memset(nulls, 0, sizeof(nulls));
+ 
+ 	values[Anum_pg_extension_uptmpl_uptname - 1] =
+ 		DirectFunctionCall1(namein, CStringGetDatum(extname));
+ 	values[Anum_pg_extension_uptmpl_uptowner - 1] = ObjectIdGetDatum(owner);
+ 	values[Anum_pg_extension_uptmpl_uptfrom - 1] = CStringGetTextDatum(from);
+ 	values[Anum_pg_extension_uptmpl_uptto - 1] = CStringGetTextDatum(to);
+ 	values[Anum_pg_extension_uptmpl_uptscript - 1] = CStringGetTextDatum(script);
+ 
+ 	tuple = heap_form_tuple(rel->rd_att, values, nulls);
+ 
+ 	extUpTmplOid = simple_heap_insert(rel, tuple);
+ 	CatalogUpdateIndexes(rel, tuple);
+ 
+ 	heap_freetuple(tuple);
+ 	heap_close(rel, RowExclusiveLock);
+ 
+ 	/*
+ 	 * Record dependencies on owner only.
+ 	 *
+ 	 * When we create the extension template and control file, the target
+ 	 * extension, its schema and requirements usually do not exist in the
+ 	 * database. Don't even think about registering a dependency from the
+ 	 * template.
+ 	 */
+ 	recordDependencyOnOwner(ExtensionUpTmplRelationId, extUpTmplOid, owner);
+ 
+ 	myself.classId = ExtensionUpTmplRelationId;
+ 	myself.objectId = extUpTmplOid;
+ 	myself.objectSubId = 0;
+ 
+ 	/* record he dependency between the control row and the template row */
+ 	ctrl.classId = ExtensionControlRelationId;
+ 	ctrl.objectId = extControlOid;
+ 	ctrl.objectSubId = 0;
+ 
+ 	recordDependencyOn(&ctrl, &myself, DEPENDENCY_INTERNAL);
+ 
+ 	/* Post creation hook for new extension control */
+ 	InvokeObjectPostCreateHook(ExtensionUpTmplRelationId, extUpTmplOid, 0);
+ 
+ 	return extUpTmplOid;
+ }
+ 
+ /*
+  * Lookup functions
+  */
+ char *
+ get_extension_control_name(Oid ctrlOid)
+ {
+ 	char	   *result;
+ 	Relation	rel;
+ 	SysScanDesc scandesc;
+ 	HeapTuple	tuple;
+ 	ScanKeyData entry[1];
+ 
+ 	rel = heap_open(ExtensionControlRelationId, AccessShareLock);
+ 
+ 	ScanKeyInit(&entry[0],
+ 				ObjectIdAttributeNumber,
+ 				BTEqualStrategyNumber, F_OIDEQ,
+ 				ObjectIdGetDatum(ctrlOid));
+ 
+ 	scandesc = systable_beginscan(rel, ExtensionControlOidIndexId, true,
+ 								  SnapshotNow, 1, entry);
+ 
+ 	tuple = systable_getnext(scandesc);
+ 
+ 	/* We assume that there can be at most one matching tuple */
+ 	if (HeapTupleIsValid(tuple))
+ 		result = pstrdup(
+ 			NameStr(((Form_pg_extension_control) GETSTRUCT(tuple))->ctlname));
+ 	else
+ 		result = NULL;
+ 
+ 	systable_endscan(scandesc);
+ 
+ 	heap_close(rel, AccessShareLock);
+ 
+ 	return result;
+ }
+ 
+ char *
+ get_extension_template_name(Oid tmplOid)
+ {
+ 	char	   *result;
+ 	Relation	rel;
+ 	SysScanDesc scandesc;
+ 	HeapTuple	tuple;
+ 	ScanKeyData entry[1];
+ 
+ 	rel = heap_open(ExtensionTemplateRelationId, AccessShareLock);
+ 
+ 	ScanKeyInit(&entry[0],
+ 				ObjectIdAttributeNumber,
+ 				BTEqualStrategyNumber, F_OIDEQ,
+ 				ObjectIdGetDatum(tmplOid));
+ 
+ 	scandesc = systable_beginscan(rel, ExtensionTemplateOidIndexId, true,
+ 								  SnapshotNow, 1, entry);
+ 
+ 	tuple = systable_getnext(scandesc);
+ 
+ 	/* We assume that there can be at most one matching tuple */
+ 	if (HeapTupleIsValid(tuple))
+ 		result = pstrdup(
+ 			NameStr(((Form_pg_extension_template) GETSTRUCT(tuple))->tplname));
+ 	else
+ 		result = NULL;
+ 
+ 	systable_endscan(scandesc);
+ 
+ 	heap_close(rel, AccessShareLock);
+ 
+ 	return result;
+ }
+ 
+ char *
+ get_extension_uptmpl_name(Oid tmplOid)
+ {
+ 	char	   *result;
+ 	Relation	rel;
+ 	SysScanDesc scandesc;
+ 	HeapTuple	tuple;
+ 	ScanKeyData entry[1];
+ 
+ 	rel = heap_open(ExtensionUpTmplRelationId, AccessShareLock);
+ 
+ 	ScanKeyInit(&entry[0],
+ 				ObjectIdAttributeNumber,
+ 				BTEqualStrategyNumber, F_OIDEQ,
+ 				ObjectIdGetDatum(tmplOid));
+ 
+ 	scandesc = systable_beginscan(rel, ExtensionUpTmplOidIndexId, true,
+ 								  SnapshotNow, 1, entry);
+ 
+ 	tuple = systable_getnext(scandesc);
+ 
+ 	/* We assume that there can be at most one matching tuple */
+ 	if (HeapTupleIsValid(tuple))
+ 		result = pstrdup(
+ 			NameStr(((Form_pg_extension_uptmpl) GETSTRUCT(tuple))->uptname));
+ 	else
+ 		result = NULL;
+ 
+ 	systable_endscan(scandesc);
+ 
+ 	heap_close(rel, AccessShareLock);
+ 
+ 	return result;
+ }
+ 
+ 
+ /*
+  * ALTER TEMPLATE FOR EXTENSION name VERSION version
+  *
+  * This implements high level routing for sub commands.
+  */
+ Oid
+ AlterTemplate(AlterExtTemplateStmt *stmt)
+ {
+ 	switch (stmt->tmpltype)
+ 	{
+ 		case TEMPLATE_CREATE_EXTENSION:
+ 			return AlterExtensionTemplate(stmt);
+ 
+ 		case TEMPLATE_UPDATE_EXTENSION:
+ 			return AlterExtensionUpdateTemplate(stmt);
+ 	}
+ 	/* keep compiler happy */
+ 	return InvalidOid;
+ }
+ 
+ /*
+  * ALTER TEMPLATE FOR EXTENSION routing
+  */
+ Oid
+ AlterExtensionTemplate(AlterExtTemplateStmt *stmt)
+ {
+ 	switch (stmt->cmdtype)
+ 	{
+ 		case AET_SET_DEFAULT:
+ 			return AlterTemplateSetDefault(stmt->extname, stmt->version);
+ 
+ 		case AET_SET_DEFAULT_FULL:
+ 			return AlterTemplateSetDefaultFull(stmt->extname, stmt->version);
+ 
+ 		case AET_SET_SCRIPT:
+ 			return AlterTemplateSetScript(stmt->extname,
+ 										  stmt->version,
+ 										  stmt->script);
+ 
+ 		case AET_UPDATE_CONTROL:
+ 			return AlterTemplateSetControl(stmt->extname,
+ 										   stmt->version,
+ 										   stmt->control);
+ 	}
+ 	/* make compiler happy */
+ 	return InvalidOid;
+ }
+ 
+ /*
+  * ALTER TEMPLATE FOR EXTENSION UPDATE routing
+  */
+ Oid
+ AlterExtensionUpdateTemplate(AlterExtTemplateStmt *stmt)
+ {
+ 	switch (stmt->cmdtype)
+ 	{
+ 		case AET_SET_DEFAULT:
+ 		case AET_SET_DEFAULT_FULL:
+ 			/* shouldn't happen */
+ 			elog(ERROR, "pg_extension_control is associated to a specific version of an extension, not an update script.");
+ 			break;
+ 
+ 		case AET_UPDATE_CONTROL:
+ 			/* shouldn't happen */
+ 			elog(ERROR, "pg_extension_control is associated to a specific version of an extension, not an update script.");
+ 			break;
+ 
+ 		case AET_SET_SCRIPT:
+ 			return AlterUpTpmlSetScript(stmt->extname,
+ 										stmt->from,
+ 										stmt->to,
+ 										stmt->script);
+ 
+ 	}
+ 	/* make compiler happy */
+ 	return InvalidOid;
+ }
+ 
+ /*
+  * ALTER TEMPLATE FOR EXTENSION ... OWNER TO ...
+  *
+  * In fact we are going to change the owner of all the templates objects
+  * related to the extension name given: pg_extension_control entries,
+  * pg_extension_template entries and also pg_extension_uptmpl entries.
+  *
+  * There's no reason to be able to change the owner of only a part of an
+  * extension's template (the control but not the template, or just the upgrade
+  * script).
+  */
+ Oid
+ AtlerExtensionTemplateOwner(const char *extname, Oid newOwnerId)
+ {
+ 	ListCell *lc;
+ 
+ 	/* Alter owner of all pg_extension_control entries for extname */
+ 	foreach(lc, list_pg_extension_control_oids_for(extname))
+ 	{
+ 		Relation		catalog;
+ 		Oid				objectId = lfirst_oid(lc);
+ 
+ 		elog(DEBUG1, "alter owner of pg_extension_control %u", objectId);
+ 
+ 		catalog = heap_open(ExtensionControlRelationId, RowExclusiveLock);
+ 		AlterObjectOwner_internal(catalog, objectId, newOwnerId);
+ 		heap_close(catalog, RowExclusiveLock);
+ 	}
+ 
+ 	/* Alter owner of all pg_extension_template entries for extname */
+ 	foreach(lc, list_pg_extension_template_oids_for(extname))
+ 	{
+ 		Relation		catalog;
+ 		Oid				objectId = lfirst_oid(lc);
+ 
+ 		elog(DEBUG1, "alter owner of pg_extension_template %u", objectId);
+ 
+ 		catalog = heap_open(ExtensionTemplateRelationId, RowExclusiveLock);
+ 		AlterObjectOwner_internal(catalog, objectId, newOwnerId);
+ 		heap_close(catalog, RowExclusiveLock);
+ 	}
+ 
+ 	/* Alter owner of all pg_extension_uptmpl entries for extname */
+ 	foreach(lc, list_pg_extension_uptmpl_oids_for(extname))
+ 	{
+ 		Relation		catalog;
+ 		Oid				objectId = lfirst_oid(lc);
+ 
+ 		elog(DEBUG1, "alter owner of pg_extension_uptmpl %u", objectId);
+ 
+ 		catalog = heap_open(ExtensionUpTmplRelationId, RowExclusiveLock);
+ 		AlterObjectOwner_internal(catalog, objectId, newOwnerId);
+ 		heap_close(catalog, RowExclusiveLock);
+ 	}
+ 
+ 	/* which Oid to return here? */
+ 	return InvalidOid;
+ }
+ 
+ /*
+  * ALTER TEMPLATE FOR EXTENSION ... RENAME TO ...
+  *
+  * There's no reason to be able to change the name of only a part of an
+  * extension's template (the control but not the template, or just the upgrade
+  * script).
+  */
+ Oid
+ AtlerExtensionTemplateRename(const char *extname, const char *newname)
+ {
+ 	ListCell *lc;
+ 
+ 	/* Rename all pg_extension_control entries for extname */
+ 	foreach(lc, list_pg_extension_control_oids_for(extname))
+ 	{
+ 		Relation		catalog;
+ 		Oid				objectId = lfirst_oid(lc);
+ 
+ 		elog(DEBUG1, "rename pg_extension_control %u", objectId);
+ 
+ 		catalog = heap_open(ExtensionControlRelationId, RowExclusiveLock);
+ 		AlterObjectRename_internal(catalog, objectId, newname);
+ 		heap_close(catalog, RowExclusiveLock);
+ 	}
+ 
+ 	/* Rename all pg_extension_template entries for extname */
+ 	foreach(lc, list_pg_extension_template_oids_for(extname))
+ 	{
+ 		Relation		catalog;
+ 		Oid				objectId = lfirst_oid(lc);
+ 
+ 		elog(DEBUG1, "rename pg_extension_template %u", objectId);
+ 
+ 		catalog = heap_open(ExtensionTemplateRelationId, RowExclusiveLock);
+ 		AlterObjectRename_internal(catalog, objectId, newname);
+ 		heap_close(catalog, RowExclusiveLock);
+ 	}
+ 
+ 	/* Rename all pg_extension_uptmpl entries for extname */
+ 	foreach(lc, list_pg_extension_uptmpl_oids_for(extname))
+ 	{
+ 		Relation		catalog;
+ 		Oid				objectId = lfirst_oid(lc);
+ 
+ 		elog(DEBUG1, "rename pg_extension_uptmpl %u", objectId);
+ 
+ 		catalog = heap_open(ExtensionUpTmplRelationId, RowExclusiveLock);
+ 		AlterObjectRename_internal(catalog, objectId, newname);
+ 		heap_close(catalog, RowExclusiveLock);
+ 	}
+ 
+ 	/* which Oid to return here? */
+ 	return InvalidOid;
+ }
+ 
+ /*
+  * ALTER TEMPLATE FOR EXTENSION ... SET DEFAULT VERSION ...
+  *
+  * We refuse to run without a default, so we drop the current one when
+  * assigning a new one.
+  */
+ static Oid
+ AlterTemplateSetDefault(const char *extname, const char *version)
+ {
+ 	/* we need to know who's the default */
+ 	ExtensionControl *current =
+ 		find_default_pg_extension_control(extname, false);
+ 
+ 	if (!pg_extension_control_ownercheck(current->ctrlOid, GetUserId()))
+ 		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_EXTCONTROL,
+ 					   extname);
+ 
+ 	/* silently do nothing if the default is already set as wanted */
+ 	if (strcmp(current->default_version, version) == 0)
+ 		return current->ctrlOid;
+ 
+ 	/* set ctldefault to false on current default extension */
+ 	modify_pg_extension_control_default(current->name,
+ 										current->default_version,
+ 										false);
+ 
+ 	/* set ctldefault to true on new default extension */
+ 	return modify_pg_extension_control_default(extname, version, true);
+ }
+ 
+ /*
+  * Implement flipping the ctldefaultfull bit to given value.
+  */
+ static Oid
+ modify_pg_extension_control_default(const char *extname,
+ 									const char *version,
+ 									bool value)
+ {
+ 	Oid         ctrlOid;
+ 	Relation	rel;
+ 	SysScanDesc scandesc;
+ 	HeapTuple	tuple;
+ 	ScanKeyData entry[2];
+ 	Datum		values[Natts_pg_extension_control];
+ 	bool		nulls[Natts_pg_extension_control];
+ 	bool		repl[Natts_pg_extension_control];
+ 
+ 	rel = heap_open(ExtensionControlRelationId, AccessShareLock);
+ 
+ 	ScanKeyInit(&entry[0],
+ 				Anum_pg_extension_control_ctlname,
+ 				BTEqualStrategyNumber, F_NAMEEQ,
+ 				CStringGetDatum(extname));
+ 
+ 	ScanKeyInit(&entry[1],
+ 				Anum_pg_extension_control_ctlversion,
+ 				BTEqualStrategyNumber, F_TEXTEQ,
+ 				CStringGetTextDatum(version));
+ 
+ 	scandesc = systable_beginscan(rel,
+ 								  ExtensionControlNameVersionIndexId, true,
+ 								  SnapshotNow, 2, entry);
+ 
+ 	tuple = systable_getnext(scandesc);
+ 
+ 	/* We assume that there can be at most one matching tuple */
+ 
+ 	if (!HeapTupleIsValid(tuple))		/* should not happen */
+ 		elog(ERROR,
+ 			 "pg_extension_control for extension \"%s\" version \"%s\" does not exist",
+ 			 extname, version);
+ 
+ 	ctrlOid = HeapTupleGetOid(tuple);
+ 
+ 	/* Modify ctldefault in the pg_extension_control tuple */
+ 	memset(values, 0, sizeof(values));
+ 	memset(nulls, 0, sizeof(nulls));
+ 	memset(repl, 0, sizeof(repl));
+ 
+ 	values[Anum_pg_extension_control_ctldefault - 1] = BoolGetDatum(value);
+ 	repl[Anum_pg_extension_control_ctldefault - 1] = true;
+ 
+ 	tuple = heap_modify_tuple(tuple, RelationGetDescr(rel),
+ 							  values, nulls, repl);
+ 
+ 	simple_heap_update(rel, &tuple->t_self, tuple);
+ 	CatalogUpdateIndexes(rel, tuple);
+ 
+ 	systable_endscan(scandesc);
+ 
+ 	heap_close(rel, AccessShareLock);
+ 
+ 	return ctrlOid;
+ }
+ 
+ /*
+  * ALTER TEMPLATE FOR EXTENSION ... SET DEFAULT FULL VERSION ...
+  */
+ static Oid
+ AlterTemplateSetDefaultFull(const char *extname, const char *version)
+ {
+ 	/* we need to know who's the default */
+ 	ExtensionControl *current =
+ 		find_default_pg_extension_control(extname, false);
+ 
+ 	/* the target version must be an installation script */
+ 	Oid target = get_template_oid(extname, version, false);
+ 
+ 	(void) target;				/* silence compiler */
+ 
+ 	/* only check the owner of one of those, as we maintain them all the same */
+ 	if (!pg_extension_control_ownercheck(current->ctrlOid, GetUserId()))
+ 		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_EXTCONTROL,
+ 					   extname);
+ 
+ 	/* silently do nothing if the default is already set as wanted */
+ 	if (strcmp(current->default_full_version, version) == 0)
+ 		return current->ctrlOid;
+ 
+ 	/* set ctldefault to false on current default extension */
+ 	modify_pg_extension_control_default_full(current->name,
+ 											 current->default_full_version,
+ 											 false);
+ 
+ 	/* set ctldefault to true on new default extension */
+ 	return modify_pg_extension_control_default_full(extname, version, true);
+ }
+ 
+ /*
+  * Implement flipping the ctldefaultfull bit to given value.
+  */
+ static Oid
+ modify_pg_extension_control_default_full(const char *extname,
+ 										 const char *version,
+ 										 bool value)
+ {
+ 	Oid         ctrlOid;
+ 	Relation	rel;
+ 	SysScanDesc scandesc;
+ 	HeapTuple	tuple;
+ 	ScanKeyData entry[2];
+ 	Datum		values[Natts_pg_extension_control];
+ 	bool		nulls[Natts_pg_extension_control];
+ 	bool		repl[Natts_pg_extension_control];
+ 
+ 	rel = heap_open(ExtensionControlRelationId, AccessShareLock);
+ 
+ 	ScanKeyInit(&entry[0],
+ 				Anum_pg_extension_control_ctlname,
+ 				BTEqualStrategyNumber, F_NAMEEQ,
+ 				CStringGetDatum(extname));
+ 
+ 	ScanKeyInit(&entry[1],
+ 				Anum_pg_extension_control_ctlversion,
+ 				BTEqualStrategyNumber, F_TEXTEQ,
+ 				CStringGetTextDatum(version));
+ 
+ 	scandesc = systable_beginscan(rel,
+ 								  ExtensionControlNameVersionIndexId, true,
+ 								  SnapshotNow, 2, entry);
+ 
+ 	tuple = systable_getnext(scandesc);
+ 
+ 	/* We assume that there can be at most one matching tuple */
+ 
+ 	if (!HeapTupleIsValid(tuple))		/* should not happen */
+ 		elog(ERROR,
+ 			 "pg_extension_control for extension \"%s\" version \"%s\" does not exist",
+ 			 extname, version);
+ 
+ 	ctrlOid = HeapTupleGetOid(tuple);
+ 
+ 	/* Modify ctldefault in the pg_extension_control tuple */
+ 	memset(values, 0, sizeof(values));
+ 	memset(nulls, 0, sizeof(nulls));
+ 	memset(repl, 0, sizeof(repl));
+ 
+ 	values[Anum_pg_extension_control_ctldefaultfull - 1] = BoolGetDatum(value);
+ 	repl[Anum_pg_extension_control_ctldefaultfull - 1] = true;
+ 
+ 	tuple = heap_modify_tuple(tuple, RelationGetDescr(rel),
+ 							  values, nulls, repl);
+ 
+ 	simple_heap_update(rel, &tuple->t_self, tuple);
+ 	CatalogUpdateIndexes(rel, tuple);
+ 
+ 	systable_endscan(scandesc);
+ 
+ 	heap_close(rel, AccessShareLock);
+ 
+ 	return ctrlOid;
+ }
+ 
+ /*
+  * ALTER TEMPLATE FOR EXTENSION ... AS $$ ... $$
+  */
+ static Oid
+ AlterTemplateSetScript(const char *extname,
+ 					   const char *version,
+ 					   const char *script)
+ {
+ 	Oid			extTemplateOid;
+ 	Relation	rel;
+ 	SysScanDesc	scandesc;
+ 	HeapTuple	tuple;
+ 	ScanKeyData	entry[2];
+ 	Datum		values[Natts_pg_extension_template];
+ 	bool		nulls[Natts_pg_extension_template];
+ 	bool		repl[Natts_pg_extension_template];
+ 
+ 	rel = heap_open(ExtensionTemplateRelationId, AccessShareLock);
+ 
+ 	ScanKeyInit(&entry[0],
+ 				Anum_pg_extension_template_tplname,
+ 				BTEqualStrategyNumber, F_NAMEEQ,
+ 				CStringGetDatum(extname));
+ 
+ 	ScanKeyInit(&entry[1],
+ 				Anum_pg_extension_template_tplversion,
+ 				BTEqualStrategyNumber, F_TEXTEQ,
+ 				CStringGetTextDatum(version));
+ 
+ 	scandesc = systable_beginscan(rel,
+ 								  ExtensionTemplateNameVersionIndexId, true,
+ 								  SnapshotNow, 2, entry);
+ 
+ 	tuple = systable_getnext(scandesc);
+ 
+ 	if (!HeapTupleIsValid(tuple))		/* should not happen */
+ 		elog(ERROR,
+ 			 "pg_extension_template for extension \"%s\" version \"%s\" does not exist",
+ 			 extname, version);
+ 
+ 	extTemplateOid = HeapTupleGetOid(tuple);
+ 
+ 	/* check privileges */
+ 	if (!pg_extension_template_ownercheck(extTemplateOid, GetUserId()))
+ 		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_EXTTEMPLATE,
+ 					   extname);
+ 
+ 	/* Modify ctldefault in the pg_extension_control tuple */
+ 	memset(values, 0, sizeof(values));
+ 	memset(nulls, 0, sizeof(nulls));
+ 	memset(repl, 0, sizeof(repl));
+ 
+ 	repl[Anum_pg_extension_template_tplscript - 1] = true;
+ 	values[Anum_pg_extension_template_tplscript - 1] =
+ 		CStringGetTextDatum(script);
+ 
+ 	tuple = heap_modify_tuple(tuple, RelationGetDescr(rel),
+ 							  values, nulls, repl);
+ 
+ 	simple_heap_update(rel, &tuple->t_self, tuple);
+ 	CatalogUpdateIndexes(rel, tuple);
+ 
+ 	systable_endscan(scandesc);
+ 
+ 	heap_close(rel, AccessShareLock);
+ 
+ 	return extTemplateOid;
+ }
+ 
+ /*
+  * ALTER TEMPLATE FOR EXTENSION ... FROM ... TO ... AS $$ ... $$
+  */
+ static Oid
+ AlterUpTpmlSetScript(const char *extname,
+ 					 const char *from,
+ 					 const char *to,
+ 					 const char *script)
+ {
+ 	Oid			extUpTmplOid;
+ 	Relation	rel;
+ 	SysScanDesc scandesc;
+ 	HeapTuple	tuple;
+ 	ScanKeyData entry[3];
+ 	Datum		values[Natts_pg_extension_uptmpl];
+ 	bool		nulls[Natts_pg_extension_uptmpl];
+ 	bool		repl[Natts_pg_extension_uptmpl];
+ 
+ 	rel = heap_open(ExtensionUpTmplRelationId, AccessShareLock);
+ 
+ 	ScanKeyInit(&entry[0],
+ 				Anum_pg_extension_uptmpl_uptname,
+ 				BTEqualStrategyNumber, F_NAMEEQ,
+ 				CStringGetDatum(extname));
+ 
+ 	ScanKeyInit(&entry[1],
+ 				Anum_pg_extension_uptmpl_uptfrom,
+ 				BTEqualStrategyNumber, F_TEXTEQ,
+ 				CStringGetTextDatum(from));
+ 
+ 	ScanKeyInit(&entry[2],
+ 				Anum_pg_extension_uptmpl_uptto,
+ 				BTEqualStrategyNumber, F_TEXTEQ,
+ 				CStringGetTextDatum(to));
+ 
+ 	scandesc = systable_beginscan(rel,
+ 								  ExtensionUpTpmlNameFromToIndexId, true,
+ 								  SnapshotNow, 3, entry);
+ 
+ 	tuple = systable_getnext(scandesc);
+ 
+ 	if (!HeapTupleIsValid(tuple))		/* should not happen */
+ 		elog(ERROR,
+ 			 "pg_extension_template for extension \"%s\" from version \"%s\" to version \"%s\" does not exist",
+ 			 extname, from, to);
+ 
+ 	extUpTmplOid = HeapTupleGetOid(tuple);
+ 
+ 	/* check privileges */
+ 	if (!pg_extension_uptmpl_ownercheck(extUpTmplOid, GetUserId()))
+ 		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_EXTUPTMPL,
+ 					   extname);
+ 
+ 	/* Modify ctldefault in the pg_extension_control tuple */
+ 	memset(values, 0, sizeof(values));
+ 	memset(nulls, 0, sizeof(nulls));
+ 	memset(repl, 0, sizeof(repl));
+ 
+ 	repl[Anum_pg_extension_uptmpl_uptscript - 1] = true;
+ 	values[Anum_pg_extension_uptmpl_uptscript - 1] =
+ 		CStringGetTextDatum(script);
+ 
+ 	tuple = heap_modify_tuple(tuple, RelationGetDescr(rel),
+ 							  values, nulls, repl);
+ 
+ 	simple_heap_update(rel, &tuple->t_self, tuple);
+ 	CatalogUpdateIndexes(rel, tuple);
+ 
+ 	systable_endscan(scandesc);
+ 
+ 	heap_close(rel, AccessShareLock);
+ 
+ 	return extUpTmplOid;
+ }
+ 
+ /*
+  * AlterTemplateSetControl
+  */
+ static Oid
+ AlterTemplateSetControl(const char *extname,
+ 						const char *version,
+ 						List *options)
+ {
+ 	Oid         ctrlOid;
+ 	Relation	rel;
+ 	SysScanDesc scandesc;
+ 	HeapTuple	tuple;
+ 	ScanKeyData entry[2];
+ 	Datum		values[Natts_pg_extension_control];
+ 	bool		nulls[Natts_pg_extension_control];
+ 	bool		repl[Natts_pg_extension_control];
+ 
+ 	ExtensionControl *current_control;
+  	ExtensionControl *new_control =
+ 		(ExtensionControl *) palloc0(sizeof(ExtensionControl));
+ 
+ 	/* parse the new control options given in the SQL command */
+ 	new_control->name = pstrdup(extname);
+ 	parse_statement_control_defelems(new_control, options);
+ 
+ 	/* now find the tuple we want to edit */
+ 	rel = heap_open(ExtensionControlRelationId, AccessShareLock);
+ 
+ 	ScanKeyInit(&entry[0],
+ 				Anum_pg_extension_control_ctlname,
+ 				BTEqualStrategyNumber, F_NAMEEQ,
+ 				CStringGetDatum(extname));
+ 
+ 	ScanKeyInit(&entry[1],
+ 				Anum_pg_extension_control_ctlversion,
+ 				BTEqualStrategyNumber, F_TEXTEQ,
+ 				CStringGetTextDatum(version));
+ 
+ 	scandesc = systable_beginscan(rel,
+ 								  ExtensionControlNameVersionIndexId, true,
+ 								  SnapshotNow, 2, entry);
+ 
+ 	tuple = systable_getnext(scandesc);
+ 
+ 	if (!HeapTupleIsValid(tuple))		/* should not happen */
+ 		elog(ERROR,
+ 			 "pg_extension_control for extension \"%s\" version \"%s\" does not exist",
+ 			 extname, version);
+ 
+ 	ctrlOid = HeapTupleGetOid(tuple);
+ 
+ 	/* check privileges */
+ 	if (!pg_extension_control_ownercheck(ctrlOid, GetUserId()))
+ 		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_EXTCONTROL,
+ 					   extname);
+ 
+ 	current_control = read_pg_extension_control(extname, rel, tuple);
+ 
+ 	/* Modify the pg_extension_control tuple */
+ 	memset(values, 0, sizeof(values));
+ 	memset(nulls, 0, sizeof(nulls));
+ 	memset(repl, 0, sizeof(repl));
+ 
+ 	/*
+ 	 * We don't compare with the current value, we directly set what's been
+ 	 * given in the new command, if anything was given
+ 	 */
+ 	if (new_control->schema)
+ 	{
+ 		values[Anum_pg_extension_control_ctlnamespace - 1] =
+ 			DirectFunctionCall1(namein, CStringGetDatum((new_control->schema)));
+ 		repl[Anum_pg_extension_control_ctlnamespace - 1] = true;
+ 	}
+ 	if (new_control->requires)
+ 	{
+ 		values[Anum_pg_extension_control_ctlrequires - 1] =
+ 			construct_control_requires_datum(new_control->requires);
+ 		repl[Anum_pg_extension_control_ctlrequires - 1] = true;
+ 	}
+ 
+ 	/*
+ 	 * We need to compare superuser and relocatable because those are bools,
+ 	 * and we don't have a NULL pointer when they were omited in the command.
+ 	 */
+ 	if (new_control->superuser != current_control->superuser)
+ 	{
+ 		values[Anum_pg_extension_control_ctlsuperuser - 1] =
+ 			BoolGetDatum(new_control->superuser);
+ 		repl[Anum_pg_extension_control_ctlsuperuser - 1] = true;
+ 	}
+ 	if (new_control->relocatable != current_control->relocatable)
+ 	{
+ 		values[Anum_pg_extension_control_ctlrelocatable - 1] =
+ 			BoolGetDatum(new_control->relocatable);
+ 		repl[Anum_pg_extension_control_ctlrelocatable - 1] = true;
+ 	}
+ 
+ 	tuple = heap_modify_tuple(tuple, RelationGetDescr(rel),
+ 							  values, nulls, repl);
+ 
+ 	simple_heap_update(rel, &tuple->t_self, tuple);
+ 	CatalogUpdateIndexes(rel, tuple);
+ 
+ 	systable_endscan(scandesc);
+ 
+ 	heap_close(rel, AccessShareLock);
+ 
+ 	return ctrlOid;
+ }
+ 
+ /*
+  * get_template_oid - given an extension name and version, look up the template
+  * OID
+  *
+  * If missing_ok is false, throw an error if extension name not found.	If
+  * true, just return InvalidOid.
+  */
+ Oid
+ get_template_oid(const char *extname, const char *version, bool missing_ok)
+ {
+ 	Oid			result;
+ 	Relation	rel;
+ 	SysScanDesc scandesc;
+ 	HeapTuple	tuple;
+ 	ScanKeyData entry[2];
+ 
+ 	rel = heap_open(ExtensionTemplateRelationId, AccessShareLock);
+ 
+ 	ScanKeyInit(&entry[0],
+ 				Anum_pg_extension_template_tplname,
+ 				BTEqualStrategyNumber, F_NAMEEQ,
+ 				CStringGetDatum(extname));
+ 
+ 	ScanKeyInit(&entry[1],
+ 				Anum_pg_extension_template_tplversion,
+ 				BTEqualStrategyNumber, F_TEXTEQ,
+ 				CStringGetTextDatum(version));
+ 
+ 	scandesc = systable_beginscan(rel,
+ 								  ExtensionTemplateNameVersionIndexId, true,
+ 								  SnapshotNow, 2, entry);
+ 
+ 	tuple = systable_getnext(scandesc);
+ 
+ 	/* We assume that there can be at most one matching tuple */
+ 	if (HeapTupleIsValid(tuple))
+ 		result = HeapTupleGetOid(tuple);
+ 	else
+ 		result = InvalidOid;
+ 
+ 	systable_endscan(scandesc);
+ 
+ 	heap_close(rel, AccessShareLock);
+ 
+ 	if (!OidIsValid(result) && !missing_ok)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_UNDEFINED_OBJECT),
+ 				 errmsg("template for extension \"%s\" version \"%s\" does not exist",
+ 						extname, version)));
+ 
+ 	return result;
+ }
+ 
+ /*
+  * Check that the given extension name has a create template.
+  */
+ bool
+ can_create_extension_from_template(const char *extname, bool missing_ok)
+ {
+ 	bool		result;
+ 	Relation	rel;
+ 	SysScanDesc scandesc;
+ 	HeapTuple	tuple;
+ 	ScanKeyData entry[1];
+ 
+ 	rel = heap_open(ExtensionTemplateRelationId, AccessShareLock);
+ 
+ 	ScanKeyInit(&entry[0],
+ 				Anum_pg_extension_template_tplname,
+ 				BTEqualStrategyNumber, F_NAMEEQ,
+ 				CStringGetDatum(extname));
+ 
+ 	scandesc = systable_beginscan(rel,
+ 								  ExtensionTemplateNameVersionIndexId, true,
+ 								  SnapshotNow, 1, entry);
+ 
+ 	tuple = systable_getnext(scandesc);
+ 
+ 	/* We only are interested into knowing if we found at least one tuple */
+ 	result = HeapTupleIsValid(tuple);
+ 
+ 	systable_endscan(scandesc);
+ 
+ 	heap_close(rel, AccessShareLock);
+ 
+ 	if (!result && !missing_ok)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_UNDEFINED_OBJECT),
+ 				 errmsg("no template for extension \"%s\"", extname)));
+ 
+ 	return result;
+ }
+ 
+ /*
+  * get_uptmpl_oid - given an extension name, from version and to version, look
+  * up the uptmpl OID
+  *
+  * If missing_ok is false, throw an error if extension name not found.	If
+  * true, just return InvalidOid.
+  */
+ Oid
+ get_uptmpl_oid(const char *extname, const char *from, const char *to,
+ 			   bool missing_ok)
+ {
+ 	Oid			result;
+ 	Relation	rel;
+ 	SysScanDesc scandesc;
+ 	HeapTuple	tuple;
+ 	ScanKeyData entry[3];
+ 
+ 	rel = heap_open(ExtensionUpTmplRelationId, AccessShareLock);
+ 
+ 	ScanKeyInit(&entry[0],
+ 				Anum_pg_extension_uptmpl_uptname,
+ 				BTEqualStrategyNumber, F_NAMEEQ,
+ 				CStringGetDatum(extname));
+ 
+ 	ScanKeyInit(&entry[1],
+ 				Anum_pg_extension_uptmpl_uptfrom,
+ 				BTEqualStrategyNumber, F_TEXTEQ,
+ 				CStringGetTextDatum(from));
+ 
+ 	ScanKeyInit(&entry[2],
+ 				Anum_pg_extension_uptmpl_uptto,
+ 				BTEqualStrategyNumber, F_TEXTEQ,
+ 				CStringGetTextDatum(to));
+ 
+ 	scandesc = systable_beginscan(rel,
+ 								  ExtensionUpTpmlNameFromToIndexId, true,
+ 								  SnapshotNow, 3, entry);
+ 
+ 	tuple = systable_getnext(scandesc);
+ 
+ 	/* We assume that there can be at most one matching tuple */
+ 	if (HeapTupleIsValid(tuple))
+ 		result = HeapTupleGetOid(tuple);
+ 	else
+ 		result = InvalidOid;
+ 
+ 	systable_endscan(scandesc);
+ 
+ 	heap_close(rel, AccessShareLock);
+ 
+ 	if (!OidIsValid(result) && !missing_ok)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_UNDEFINED_OBJECT),
+ 				 errmsg("template for extension \"%s\" update from version \"%s\" to version \"%s\"does not exist",
+ 						extname, from, to)));
+ 
+ 	return result;
+ }
+ 
+ /*
+  * Remove Extension Control by OID
+  */
+ void
+ RemoveExtensionControlById(Oid extControlOid)
+ {
+ 	Relation	rel;
+ 	SysScanDesc scandesc;
+ 	HeapTuple	tuple;
+ 	ScanKeyData entry[1];
+ 
+ 	rel = heap_open(ExtensionControlRelationId, RowExclusiveLock);
+ 
+ 	ScanKeyInit(&entry[0],
+ 				ObjectIdAttributeNumber,
+ 				BTEqualStrategyNumber, F_OIDEQ,
+ 				ObjectIdGetDatum(extControlOid));
+ 	scandesc = systable_beginscan(rel, ExtensionControlOidIndexId, true,
+ 								  SnapshotNow, 1, entry);
+ 
+ 	tuple = systable_getnext(scandesc);
+ 
+ 	/* We assume that there can be at most one matching tuple */
+ 	if (HeapTupleIsValid(tuple))
+ 		simple_heap_delete(rel, &tuple->t_self);
+ 
+ 	systable_endscan(scandesc);
+ 
+ 	heap_close(rel, RowExclusiveLock);
+ }
+ 
+ /*
+  * Remove Extension Control by OID
+  */
+ void
+ RemoveExtensionTemplateById(Oid extTemplateOid)
+ {
+ 	Relation	rel;
+ 	SysScanDesc scandesc;
+ 	HeapTuple	tuple;
+ 	ScanKeyData entry[1];
+ 
+ 	rel = heap_open(ExtensionTemplateRelationId, RowExclusiveLock);
+ 
+ 	ScanKeyInit(&entry[0],
+ 				ObjectIdAttributeNumber,
+ 				BTEqualStrategyNumber, F_OIDEQ,
+ 				ObjectIdGetDatum(extTemplateOid));
+ 	scandesc = systable_beginscan(rel, ExtensionTemplateOidIndexId, true,
+ 								  SnapshotNow, 1, entry);
+ 
+ 	tuple = systable_getnext(scandesc);
+ 
+ 	/* We assume that there can be at most one matching tuple */
+ 	if (HeapTupleIsValid(tuple))
+ 		simple_heap_delete(rel, &tuple->t_self);
+ 
+ 	systable_endscan(scandesc);
+ 
+ 	heap_close(rel, RowExclusiveLock);
+ }
+ 
+ /*
+  * Remove Extension Control by OID
+  */
+ void
+ RemoveExtensionUpTmplById(Oid extUpTmplOid)
+ {
+ 	Relation	rel;
+ 	SysScanDesc scandesc;
+ 	HeapTuple	tuple;
+ 	ScanKeyData entry[1];
+ 
+ 	rel = heap_open(ExtensionUpTmplRelationId, RowExclusiveLock);
+ 
+ 	ScanKeyInit(&entry[0],
+ 				ObjectIdAttributeNumber,
+ 				BTEqualStrategyNumber, F_OIDEQ,
+ 				ObjectIdGetDatum(extUpTmplOid));
+ 	scandesc = systable_beginscan(rel, ExtensionUpTmplOidIndexId, true,
+ 								  SnapshotNow, 1, entry);
+ 
+ 	tuple = systable_getnext(scandesc);
+ 
+ 	/* We assume that there can be at most one matching tuple */
+ 	if (HeapTupleIsValid(tuple))
+ 		simple_heap_delete(rel, &tuple->t_self);
+ 
+ 	systable_endscan(scandesc);
+ 
+ 	heap_close(rel, RowExclusiveLock);
+ }
+ 
+ /*
+  * Internal tool to get the version string of a pg_extension_control tuple.
+  */
+ static char *
+ extract_ctlversion(Relation rel, HeapTuple tuple)
+ {
+ 	bool	isnull;
+ 	Datum	dvers;
+ 
+ 	dvers = heap_getattr(tuple, Anum_pg_extension_control_ctlversion,
+ 						 RelationGetDescr(rel), &isnull);
+ 	if (isnull)
+ 		elog(ERROR, "invalid null extension version");
+ 
+ 	return text_to_cstring(DatumGetTextPP(dvers));
+ }
+ 
+ /*
+  * read_pg_extension_control
+  *
+  * Read a pg_extension_control row and fill in an ExtensionControl
+  * structure with the right elements in there.
+  */
+ static ExtensionControl *
+ read_pg_extension_control(const char *extname, Relation rel, HeapTuple tuple)
+ {
+ 	Datum dreqs;
+ 	bool isnull;
+ 	Form_pg_extension_control ctrl =
+ 		(Form_pg_extension_control) GETSTRUCT(tuple);
+ 
+ 	ExtensionControl *control =
+ 		(ExtensionControl *) palloc0(sizeof(ExtensionControl));
+ 
+ 	/* Those fields are not null */
+ 	control->ctrlOid = HeapTupleGetOid(tuple);
+ 	control->name = pstrdup(extname);
+ 	control->is_template = true;
+ 	control->relocatable = ctrl->ctlrelocatable;
+ 	control->superuser = ctrl->ctlsuperuser;
+ 	control->schema = pstrdup(NameStr(ctrl->ctlnamespace));
+ 
+ 	/* set control->default_version */
+ 	if (ctrl->ctldefault)
+ 		control->default_version = extract_ctlversion(rel, tuple);
+ 
+ 	/* set control->default_full_version */
+ 	if (ctrl->ctldefaultfull)
+ 	{
+ 		/* avoid extracting it again if we just did so */
+ 		if (control->default_version)
+ 			control->default_full_version = control->default_version;
+ 		else
+ 			control->default_full_version = extract_ctlversion(rel, tuple);
+ 	}
+ 
+ 	/* now see about the dependencies array */
+ 	dreqs = heap_getattr(tuple, Anum_pg_extension_control_ctlrequires,
+ 						 RelationGetDescr(rel), &isnull);
+ 
+ 	if (!isnull)
+ 	{
+ 		ArrayType  *arr = DatumGetArrayTypeP(dreqs);
+ 		Datum	   *elems;
+ 		int			i;
+ 		int			nelems;
+ 
+ 		if (ARR_NDIM(arr) != 1 || ARR_HASNULL(arr) || ARR_ELEMTYPE(arr) != TEXTOID)
+ 			elog(ERROR, "expected 1-D text array");
+ 		deconstruct_array(arr, TEXTOID, -1, false, 'i', &elems, NULL, &nelems);
+ 
+ 		for (i = 0; i < nelems; ++i)
+ 			control->requires = lappend(control->requires,
+ 										TextDatumGetCString(elems[i]));
+ 
+ 		pfree(elems);
+ 	}
+ 	return control;
+ }
+ 
+ /*
+  * Find the pg_extension_control row for given extname and version, if any, and
+  * return a filled in ExtensionControl structure.
+  *
+  * In case we don't have any pg_extension_control row for given extname and
+  * version, return NULL.
+  */
+ ExtensionControl *
+ find_pg_extension_control(const char *extname,
+ 						  const char *version,
+ 						  bool missing_ok)
+ {
+ 	ExtensionControl *control = NULL;
+ 	Relation	rel;
+ 	SysScanDesc scandesc;
+ 	HeapTuple	tuple;
+ 	ScanKeyData entry[2];
+ 
+ 	rel = heap_open(ExtensionControlRelationId, AccessShareLock);
+ 
+ 	ScanKeyInit(&entry[0],
+ 				Anum_pg_extension_control_ctlname,
+ 				BTEqualStrategyNumber, F_NAMEEQ,
+ 				CStringGetDatum(extname));
+ 
+ 	ScanKeyInit(&entry[1],
+ 				Anum_pg_extension_control_ctlversion,
+ 				BTEqualStrategyNumber, F_TEXTEQ,
+ 				CStringGetTextDatum(version));
+ 
+ 	scandesc = systable_beginscan(rel,
+ 								  ExtensionControlNameVersionIndexId, true,
+ 								  SnapshotNow, 2, entry);
+ 
+ 	tuple = systable_getnext(scandesc);
+ 
+ 	/* We assume that there can be at most one matching tuple */
+ 	if (HeapTupleIsValid(tuple))
+ 		control = read_pg_extension_control(extname, rel, tuple);
+ 
+ 	systable_endscan(scandesc);
+ 
+ 	heap_close(rel, AccessShareLock);
+ 
+ 	if (control == NULL && !missing_ok)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_UNDEFINED_OBJECT),
+ 				 errmsg("extension \"%s\" has no control template for version \"%s\"",
+ 						extname, version)));
+ 
+ 	return control;
+ }
+ 
+ /*
+  * Find the default extension's control properties, and its OID, for internal
+  * use (such as checking ACLs).
+  *
+  * In a single scan of the pg_extension_control catalog, also find out the
+  * default full version of that extension, needed in extension.c when doing
+  * multi steps CREATE EXTENSION.
+  */
+ ExtensionControl *
+ find_default_pg_extension_control(const char *extname, bool missing_ok)
+ {
+ 	ExtensionControl *control = NULL;
+ 	char *default_full_version = NULL;
+ 	Relation	rel;
+ 	SysScanDesc scandesc;
+ 	HeapTuple	tuple;
+ 	ScanKeyData entry[1];
+ 
+ 	rel = heap_open(ExtensionControlRelationId, AccessShareLock);
+ 
+ 	ScanKeyInit(&entry[0],
+ 				Anum_pg_extension_control_ctlname,
+ 				BTEqualStrategyNumber, F_NAMEEQ,
+ 				CStringGetDatum(extname));
+ 
+ 	scandesc = systable_beginscan(rel,
+ 								  ExtensionControlNameVersionIndexId, true,
+ 								  SnapshotNow, 1, entry);
+ 
+ 	/* find all the control tuples for extname */
+ 	while (HeapTupleIsValid(tuple = systable_getnext(scandesc)))
+ 	{
+ 		bool	isnull;
+ 		Datum	tmpdatum;
+ 		bool	ctldefault;
+ 		bool	ctldefaultfull;
+ 
+ 		tmpdatum = fastgetattr(tuple,
+ 							   Anum_pg_extension_control_ctldefault,
+ 							   RelationGetDescr(rel), &isnull);
+ 		if (isnull)
+ 			elog(ERROR, "invalid null ctldefault");
+ 		ctldefault = DatumGetBool(tmpdatum);
+ 
+ 		/* only one of these is the default */
+ 		if (ctldefault)
+ 		{
+ 			if (!control)
+ 				control = read_pg_extension_control(extname, rel, tuple);
+ 			else
+ 				/* should not happen */
+ 				elog(ERROR,
+ 					 "extension \"%s\" has more than one default control template",
+ 					 extname);
+ 		}
+ 
+ 		tmpdatum = fastgetattr(tuple,
+ 							   Anum_pg_extension_control_ctldefaultfull,
+ 							   RelationGetDescr(rel), &isnull);
+ 		if (isnull)
+ 			elog(ERROR, "invalid null ctldefaultfull");
+ 		ctldefaultfull = DatumGetBool(tmpdatum);
+ 
+ 		/* the default version and default full version might be different */
+ 		if (ctldefaultfull)
+ 			default_full_version = extract_ctlversion(rel, tuple);
+ 	}
+ 	systable_endscan(scandesc);
+ 
+ 	heap_close(rel, AccessShareLock);
+ 
+ 	/* we really need a single default version. */
+ 	if (!control && !missing_ok)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_UNDEFINED_OBJECT),
+ 				 errmsg("extension \"%s\" has no default control template",
+ 						extname)));
+ 
+ 	/* don't forget to add in the default full version */
+ 	if (control != NULL && default_full_version)
+ 		control->default_full_version = default_full_version;
+ 
+ 	return control;
+ }
+ 
+ /*
+  * read_pg_extension_template_script
+  *
+  * Return the script from the pg_extension_template catalogs.
+  */
+ char *
+ read_pg_extension_template_script(const char *extname, const char *version)
+ {
+ 	char		*script;
+ 	Relation	 rel;
+ 	SysScanDesc	 scandesc;
+ 	HeapTuple	 tuple;
+ 	ScanKeyData	 entry[2];
+ 
+ 	rel = heap_open(ExtensionTemplateRelationId, AccessShareLock);
+ 
+ 	ScanKeyInit(&entry[0],
+ 				Anum_pg_extension_template_tplname,
+ 				BTEqualStrategyNumber, F_NAMEEQ,
+ 				CStringGetDatum(extname));
+ 
+ 	ScanKeyInit(&entry[1],
+ 				Anum_pg_extension_template_tplversion,
+ 				BTEqualStrategyNumber, F_TEXTEQ,
+ 				CStringGetTextDatum(version));
+ 
+ 	scandesc = systable_beginscan(rel,
+ 								  ExtensionTemplateNameVersionIndexId, true,
+ 								  SnapshotNow, 2, entry);
+ 
+ 	tuple = systable_getnext(scandesc);
+ 
+ 	/* We assume that there can be at most one matching tuple */
+ 	if (HeapTupleIsValid(tuple))
+ 	{
+ 		bool	isnull;
+ 		Datum	dscript;
+ 
+ 		dscript = heap_getattr(tuple, Anum_pg_extension_template_tplscript,
+ 							   RelationGetDescr(rel), &isnull);
+ 
+ 		script = isnull? NULL : text_to_cstring(DatumGetTextPP(dscript));
+ 	}
+ 	else
+ 		/* can't happen */
+ 		elog(ERROR,
+ 			 "Missing Extension Template entry for extension \"%s\" version \"%s\"",
+ 			 extname, version);
+ 
+ 	systable_endscan(scandesc);
+ 
+ 	heap_close(rel, AccessShareLock);
+ 
+ 	return script;
+ }
+ 
+ /*
+  * read_pg_extension_uptmpl_script
+  *
+  * Return the script from the pg_extension_uptmpl catalogs.
+  */
+ char *
+ read_pg_extension_uptmpl_script(const char *extname,
+ 								const char *from_version, const char *version)
+ {
+ 	char		*script;
+ 	Relation	 rel;
+ 	SysScanDesc	 scandesc;
+ 	HeapTuple	 tuple;
+ 	ScanKeyData	 entry[3];
+ 
+ 	rel = heap_open(ExtensionUpTmplRelationId, AccessShareLock);
+ 
+ 	ScanKeyInit(&entry[0],
+ 				Anum_pg_extension_uptmpl_uptname,
+ 				BTEqualStrategyNumber, F_NAMEEQ,
+ 				CStringGetDatum(extname));
+ 
+ 	ScanKeyInit(&entry[1],
+ 				Anum_pg_extension_uptmpl_uptfrom,
+ 				BTEqualStrategyNumber, F_TEXTEQ,
+ 				CStringGetTextDatum(from_version));
+ 
+ 	ScanKeyInit(&entry[2],
+ 				Anum_pg_extension_uptmpl_uptto,
+ 				BTEqualStrategyNumber, F_TEXTEQ,
+ 				CStringGetTextDatum(version));
+ 
+ 	scandesc = systable_beginscan(rel,
+ 								  ExtensionUpTpmlNameFromToIndexId, true,
+ 								  SnapshotNow, 3, entry);
+ 
+ 	tuple = systable_getnext(scandesc);
+ 
+ 	/* We assume that there can be at most one matching tuple */
+ 	if (HeapTupleIsValid(tuple))
+ 	{
+ 		bool	isnull;
+ 		Datum	dscript;
+ 
+ 		dscript = heap_getattr(tuple, Anum_pg_extension_uptmpl_uptscript,
+ 							   RelationGetDescr(rel), &isnull);
+ 
+ 		script = isnull? NULL : text_to_cstring(DatumGetTextPP(dscript));
+ 	}
+ 	else
+ 		/* can't happen */
+ 		elog(ERROR, "Extension Template Control entry for \"%s\" has no template for update from version \"%s\" to version \"%s\"",
+ 			 extname, from_version, version);
+ 
+ 	systable_endscan(scandesc);
+ 
+ 	heap_close(rel, AccessShareLock);
+ 
+ 	return script;
+ }
+ 
+ /*
+  * read_extension_template_script
+  *
+  * Given an extension's name and a version, return the extension's script from
+  * the pg_extension_template or the pg_extension_uptmpl catalog. The former is
+  * used when from_version is NULL.
+  */
+ char *
+ read_extension_template_script(const char *extname,
+ 							   const char *from_version, const char *version)
+ {
+ 	if (from_version)
+ 		return read_pg_extension_uptmpl_script(extname, from_version, version);
+ 	else
+ 		return read_pg_extension_template_script(extname, version);
+ }
+ 
+ /*
+  * Returns a list of cstring containing all known versions that you can install
+  * for a given extension.
+  */
+ List *
+ list_pg_extension_template_versions(const char *extname)
+ {
+ 	List		*versions = NIL;
+ 	Relation	 rel;
+ 	SysScanDesc	 scandesc;
+ 	HeapTuple	 tuple;
+ 	ScanKeyData	 entry[1];
+ 
+ 	rel = heap_open(ExtensionTemplateRelationId, AccessShareLock);
+ 
+ 	ScanKeyInit(&entry[0],
+ 				Anum_pg_extension_template_tplname,
+ 				BTEqualStrategyNumber, F_NAMEEQ,
+ 				CStringGetDatum(extname));
+ 
+ 	scandesc = systable_beginscan(rel,
+ 								  ExtensionTemplateNameVersionIndexId, true,
+ 								  SnapshotNow, 1, entry);
+ 
+ 	while (HeapTupleIsValid(tuple = systable_getnext(scandesc)))
+ 	{
+ 		bool	isnull;
+ 		Datum dvers =
+ 			heap_getattr(tuple, Anum_pg_extension_template_tplversion,
+ 						 RelationGetDescr(rel), &isnull);
+ 
+ 		char *version = isnull? NULL : text_to_cstring(DatumGetTextPP(dvers));
+ 
+ 		versions = lappend(versions, version);
+ 	}
+ 
+ 	systable_endscan(scandesc);
+ 
+ 	heap_close(rel, AccessShareLock);
+ 
+ 	return versions;
+ }
+ 
+ /*
+  * Returns a list of lists of source and target versions for which we have a
+  * direct upgrade path for.
+  */
+ List *
+ list_pg_extension_update_versions(const char *extname)
+ {
+ 	List		*versions = NIL;
+ 	Relation	 rel;
+ 	SysScanDesc	 scandesc;
+ 	HeapTuple	 tuple;
+ 	ScanKeyData	 entry[1];
+ 
+ 	rel = heap_open(ExtensionUpTmplRelationId, AccessShareLock);
+ 
+ 	ScanKeyInit(&entry[0],
+ 				Anum_pg_extension_uptmpl_uptname,
+ 				BTEqualStrategyNumber, F_NAMEEQ,
+ 				CStringGetDatum(extname));
+ 
+ 	scandesc = systable_beginscan(rel,
+ 								  ExtensionUpTpmlNameFromToIndexId, true,
+ 								  SnapshotNow, 1, entry);
+ 
+ 	while (HeapTupleIsValid(tuple = systable_getnext(scandesc)))
+ 	{
+ 		bool	 isnull;
+ 		Datum	 dfrom, dto;
+ 		char	*from, *to;
+ 
+ 		/* neither from nor to are allowed to be null... */
+ 		dfrom = heap_getattr(tuple, Anum_pg_extension_uptmpl_uptfrom,
+ 							 RelationGetDescr(rel), &isnull);
+ 
+ 		from = isnull ? NULL : text_to_cstring(DatumGetTextPP(dfrom));
+ 
+ 		dto = heap_getattr(tuple, Anum_pg_extension_uptmpl_uptto,
+ 						   RelationGetDescr(rel), &isnull);
+ 
+ 		to = isnull ? NULL : text_to_cstring(DatumGetTextPP(dto));
+ 
+ 		versions = lappend(versions, list_make2(from, to));
+ 	}
+ 
+ 	systable_endscan(scandesc);
+ 
+ 	heap_close(rel, AccessShareLock);
+ 
+ 	return versions;
+ }
+ 
+ /*
+  * pg_extension_controls
+  *
+  * List all extensions for which we have a default control entry. Returns a
+  * sorted list of name, version of such extensions.
+  */
+ List *
+ pg_extension_default_controls(void)
+ {
+ 	List		*extensions = NIL;
+ 	Relation	 rel;
+ 	SysScanDesc	 scandesc;
+ 	HeapTuple	 tuple;
+ 
+ 	rel = heap_open(ExtensionControlRelationId, AccessShareLock);
+ 
+ 	scandesc = systable_beginscan(rel,
+ 								  ExtensionControlNameVersionIndexId, true,
+ 								  SnapshotNow, 0, NULL);
+ 
+ 	/* find all the control tuples for extname */
+ 	while (HeapTupleIsValid(tuple = systable_getnext(scandesc)))
+ 	{
+ 		Form_pg_extension_control ctrl =
+ 			(Form_pg_extension_control) GETSTRUCT(tuple);
+ 
+ 		bool isnull;
+ 		bool ctldefault =
+ 			DatumGetBool(
+ 				fastgetattr(tuple, Anum_pg_extension_control_ctldefault,
+ 							RelationGetDescr(rel), &isnull));
+ 
+ 		/* only of those is the default */
+ 		if (ctldefault)
+ 		{
+ 			Datum dvers =
+ 				heap_getattr(tuple, Anum_pg_extension_control_ctlversion,
+ 							 RelationGetDescr(rel), &isnull);
+ 
+ 			char *version = isnull? NULL:text_to_cstring(DatumGetTextPP(dvers));
+ 
+ 			extensions = lappend(extensions,
+ 								 list_make2(pstrdup(NameStr(ctrl->ctlname)),
+ 											version));
+ 		}
+ 	}
+ 
+ 	systable_endscan(scandesc);
+ 
+ 	heap_close(rel, AccessShareLock);
+ 
+ 	return extensions;
+ }
+ 
+ /*
+  * pg_extension_templates
+  *
+  * Return a list of list of (name, version) of extensions available to install
+  * from templates, in alphabetical order.
+  */
+ List *
+ pg_extension_templates(void)
+ {
+ 	List		*templates = NIL;
+ 	Relation	 rel;
+ 	SysScanDesc	 scandesc;
+ 	HeapTuple	 tuple;
+ 	ScanKeyData	 entry[1];
+ 
+ 	rel = heap_open(ExtensionTemplateRelationId, AccessShareLock);
+ 
+ 	scandesc = systable_beginscan(rel,
+ 								  ExtensionTemplateNameVersionIndexId, true,
+ 								  SnapshotNow, 0, entry);
+ 
+ 	while (HeapTupleIsValid(tuple = systable_getnext(scandesc)))
+ 	{
+ 		Form_pg_extension_template tmpl =
+ 			(Form_pg_extension_template) GETSTRUCT(tuple);
+ 
+ 		bool	isnull;
+ 
+ 		Datum dvers =
+ 			heap_getattr(tuple, Anum_pg_extension_template_tplversion,
+ 						 RelationGetDescr(rel), &isnull);
+ 
+ 		char *version = isnull? NULL : text_to_cstring(DatumGetTextPP(dvers));
+ 
+ 		templates = lappend(templates,
+ 							list_make2(pstrdup(NameStr(tmpl->tplname)),
+ 									   version));
+ 	}
+ 
+ 	systable_endscan(scandesc);
+ 
+ 	heap_close(rel, AccessShareLock);
+ 
+ 	return templates;
+ }
+ 
+ List *
+ list_pg_extension_control_oids_for(const char *extname)
+ {
+ 	List       *oids = NIL;
+ 	Relation	rel;
+ 	SysScanDesc scandesc;
+ 	HeapTuple	tuple;
+ 	ScanKeyData entry[1];
+ 
+ 	rel = heap_open(ExtensionControlRelationId, AccessShareLock);
+ 
+ 	ScanKeyInit(&entry[0],
+ 				Anum_pg_extension_control_ctlname,
+ 				BTEqualStrategyNumber, F_NAMEEQ,
+ 				CStringGetDatum(extname));
+ 
+ 	scandesc = systable_beginscan(rel,
+ 								  ExtensionControlNameVersionIndexId, true,
+ 								  SnapshotNow, 1, entry);
+ 
+ 	/* find all the control tuples for extname */
+ 	while (HeapTupleIsValid(tuple = systable_getnext(scandesc)))
+ 		oids = lappend_oid(oids, HeapTupleGetOid(tuple));
+ 
+ 	systable_endscan(scandesc);
+ 
+ 	heap_close(rel, AccessShareLock);
+ 
+ 	return oids;
+ }
+ 
+ List *
+ list_pg_extension_template_oids_for(const char *extname)
+ {
+ 	List        *oids = NIL;
+ 	Relation	 rel;
+ 	SysScanDesc	 scandesc;
+ 	HeapTuple	 tuple;
+ 	ScanKeyData	 entry[1];
+ 
+ 	rel = heap_open(ExtensionTemplateRelationId, AccessShareLock);
+ 
+ 	ScanKeyInit(&entry[0],
+ 				Anum_pg_extension_template_tplname,
+ 				BTEqualStrategyNumber, F_NAMEEQ,
+ 				CStringGetDatum(extname));
+ 
+ 	scandesc = systable_beginscan(rel,
+ 								  ExtensionTemplateNameVersionIndexId, true,
+ 								  SnapshotNow, 1, entry);
+ 
+ 	while (HeapTupleIsValid(tuple = systable_getnext(scandesc)))
+ 		oids = lappend_oid(oids, HeapTupleGetOid(tuple));
+ 
+ 	systable_endscan(scandesc);
+ 
+ 	heap_close(rel, AccessShareLock);
+ 
+ 	return oids;
+ }
+ 
+ List *
+ list_pg_extension_uptmpl_oids_for(const char *extname)
+ {
+ 	List        *oids = NIL;
+ 	Relation	 rel;
+ 	SysScanDesc	 scandesc;
+ 	HeapTuple	 tuple;
+ 	ScanKeyData	 entry[1];
+ 
+ 	rel = heap_open(ExtensionUpTmplRelationId, AccessShareLock);
+ 
+ 	ScanKeyInit(&entry[0],
+ 				Anum_pg_extension_uptmpl_uptname,
+ 				BTEqualStrategyNumber, F_NAMEEQ,
+ 				CStringGetDatum(extname));
+ 
+ 	scandesc = systable_beginscan(rel,
+ 								  ExtensionUpTpmlNameFromToIndexId, true,
+ 								  SnapshotNow, 1, entry);
+ 
+ 	while (HeapTupleIsValid(tuple = systable_getnext(scandesc)))
+ 		oids = lappend_oid(oids, HeapTupleGetOid(tuple));
+ 
+ 	systable_endscan(scandesc);
+ 
+ 	heap_close(rel, AccessShareLock);
+ 
+ 	return oids;
+ }
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 228,234 **** static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
  		AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
  		AlterExtensionStmt AlterExtensionContentsStmt AlterForeignTableStmt
  		AlterCompositeTypeStmt AlterUserStmt AlterUserMappingStmt AlterUserSetStmt
! 		AlterRoleStmt AlterRoleSetStmt
  		AlterDefaultPrivilegesStmt DefACLAction
  		AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt
  		ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt
--- 228,234 ----
  		AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
  		AlterExtensionStmt AlterExtensionContentsStmt AlterForeignTableStmt
  		AlterCompositeTypeStmt AlterUserStmt AlterUserMappingStmt AlterUserSetStmt
! 		AlterRoleStmt AlterRoleSetStmt AlterExtTemplateStmt
  		AlterDefaultPrivilegesStmt DefACLAction
  		AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt
  		ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt
***************
*** 237,247 **** static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
  		CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt
  		CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt
  		CreateAssertStmt CreateTrigStmt CreateEventTrigStmt
! 		CreateUserStmt CreateUserMappingStmt CreateRoleStmt
  		CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt
  		DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt
  		DropAssertStmt DropTrigStmt DropRuleStmt DropCastStmt DropRoleStmt
! 		DropUserStmt DropdbStmt DropTableSpaceStmt DropFdwStmt
  		DropForeignServerStmt DropUserMappingStmt ExplainStmt FetchStmt
  		GrantStmt GrantRoleStmt IndexStmt InsertStmt ListenStmt LoadStmt
  		LockStmt NotifyStmt ExplainableStmt PreparableStmt
--- 237,247 ----
  		CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt
  		CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt
  		CreateAssertStmt CreateTrigStmt CreateEventTrigStmt
! 		CreateUserStmt CreateUserMappingStmt CreateRoleStmt CreateExtTemplateStmt
  		CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt
  		DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt
  		DropAssertStmt DropTrigStmt DropRuleStmt DropCastStmt DropRoleStmt
! 		DropTemplateStmt DropUserStmt DropdbStmt DropTableSpaceStmt DropFdwStmt
  		DropForeignServerStmt DropUserMappingStmt ExplainStmt FetchStmt
  		GrantStmt GrantRoleStmt IndexStmt InsertStmt ListenStmt LoadStmt
  		LockStmt NotifyStmt ExplainableStmt PreparableStmt
***************
*** 275,280 **** static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
--- 275,283 ----
  				transaction_mode_item
  				create_extension_opt_item alter_extension_opt_item
  
+ %type <list>    create_template_control create_template_control_plist
+ %type <defelt>  create_template_control_item
+ 
  %type <ival>	opt_lock lock_type cast_context
  %type <ival>	vacuum_option_list vacuum_option_elem
  %type <boolean>	opt_force opt_or_replace
***************
*** 731,736 **** stmt :
--- 734,740 ----
  			| AlterCompositeTypeStmt
  			| AlterRoleSetStmt
  			| AlterRoleStmt
+ 			| AlterExtTemplateStmt
  			| AlterTSConfigurationStmt
  			| AlterTSDictionaryStmt
  			| AlterUserMappingStmt
***************
*** 769,774 **** stmt :
--- 773,779 ----
  			| CreateUserStmt
  			| CreateUserMappingStmt
  			| CreatedbStmt
+ 			| CreateExtTemplateStmt
  			| DeallocateStmt
  			| DeclareCursorStmt
  			| DefineStmt
***************
*** 792,797 **** stmt :
--- 797,803 ----
  			| DropUserStmt
  			| DropUserMappingStmt
  			| DropdbStmt
+ 			| DropTemplateStmt
  			| ExecuteStmt
  			| ExplainStmt
  			| FetchStmt
***************
*** 3563,3568 **** DropTableSpaceStmt: DROP TABLESPACE name
--- 3569,3800 ----
  /*****************************************************************************
   *
   *		QUERY:
+  *             CREATE TEMPLATE FOR EXTENSION name
+  *
+  *****************************************************************************/
+ 
+ CreateExtTemplateStmt:
+             CREATE TEMPLATE FOR EXTENSION name VERSION_P ColId_or_Sconst
+             WITH create_template_control AS Sconst
+ 				{
+ 					CreateExtTemplateStmt *n = makeNode(CreateExtTemplateStmt);
+ 					n->tmpltype = TEMPLATE_CREATE_EXTENSION;
+ 					n->extname = $5;
+ 					n->version = $7;
+ 					n->control = $9;
+ 					n->script = $11;
+ 					n->if_not_exists = false;
+ 					n->default_version = false;
+ 					$$ = (Node *) n;
+ 				}
+             | CREATE TEMPLATE FOR EXTENSION name
+ 			  DEFAULT VERSION_P ColId_or_Sconst
+               WITH create_template_control AS Sconst
+ 				{
+ 					CreateExtTemplateStmt *n = makeNode(CreateExtTemplateStmt);
+ 					n->tmpltype = TEMPLATE_CREATE_EXTENSION;
+ 					n->extname = $5;
+ 					n->version = $8;
+ 					n->control = $10;
+ 					n->script = $12;
+ 					n->if_not_exists = false;
+ 					n->default_version = true;
+ 					$$ = (Node *) n;
+ 				}
+             | CREATE TEMPLATE FOR EXTENSION name
+               FROM ColId_or_Sconst TO ColId_or_Sconst AS Sconst
+ 				{
+ 					CreateExtTemplateStmt *n = makeNode(CreateExtTemplateStmt);
+ 					n->tmpltype = TEMPLATE_UPDATE_EXTENSION;
+ 					n->extname = $5;
+ 					n->from = $7;
+ 					n->to = $9;
+ 					n->control = NIL;
+ 					n->script = $11;
+ 					n->if_not_exists = false;
+ 					$$ = (Node *) n;
+ 				}
+             | CREATE TEMPLATE FOR EXTENSION name
+               FROM ColId_or_Sconst TO ColId_or_Sconst
+               WITH create_template_control AS Sconst
+ 				{
+ 					CreateExtTemplateStmt *n = makeNode(CreateExtTemplateStmt);
+ 					n->tmpltype = TEMPLATE_UPDATE_EXTENSION;
+ 					n->extname = $5;
+ 					n->from = $7;
+ 					n->to = $9;
+ 					n->control = $11;
+ 					n->script = $13;
+ 					n->if_not_exists = false;
+ 					$$ = (Node *) n;
+ 				}
+ 		;
+ 
+ /*****************************************************************************
+  *
+  *		QUERY:
+  *             ALTER TEMPLATE FOR EXTENSION name
+  *
+  * We only allow a single subcommand per command here.
+  *
+  *****************************************************************************/
+ AlterExtTemplateStmt:
+ 			ALTER TEMPLATE FOR EXTENSION name
+ 			SET DEFAULT VERSION_P ColId_or_Sconst
+ 				{
+ 					AlterExtTemplateStmt *n = makeNode(AlterExtTemplateStmt);
+ 					n->tmpltype = TEMPLATE_CREATE_EXTENSION;
+ 					n->cmdtype = AET_SET_DEFAULT;
+ 					n->extname = $5;
+ 					n->version = $9;
+ 					n->missing_ok = false;
+ 					$$ = (Node *) n;
+ 				}
+ 			| ALTER TEMPLATE FOR EXTENSION name
+ 			  SET DEFAULT FULL VERSION_P ColId_or_Sconst
+ 				{
+ 					AlterExtTemplateStmt *n = makeNode(AlterExtTemplateStmt);
+ 					n->tmpltype = TEMPLATE_CREATE_EXTENSION;
+ 					n->cmdtype = AET_SET_DEFAULT_FULL;
+ 					n->extname = $5;
+ 					n->version = $10;
+ 					n->missing_ok = false;
+ 					$$ = (Node *) n;
+ 				}
+ 			| ALTER TEMPLATE FOR EXTENSION name VERSION_P ColId_or_Sconst
+ 			  AS Sconst
+ 				{
+ 					AlterExtTemplateStmt *n = makeNode(AlterExtTemplateStmt);
+ 					n->tmpltype = TEMPLATE_CREATE_EXTENSION;
+ 					n->cmdtype = AET_SET_SCRIPT;
+ 					n->extname = $5;
+ 					n->version = $7;
+ 					n->script = $9;
+ 					n->missing_ok = false;
+ 					$$ = (Node *) n;
+ 				}
+ 			| ALTER TEMPLATE FOR EXTENSION name VERSION_P ColId_or_Sconst
+ 			  WITH create_template_control
+ 				{
+ 					AlterExtTemplateStmt *n = makeNode(AlterExtTemplateStmt);
+ 					n->tmpltype = TEMPLATE_CREATE_EXTENSION;
+ 					n->cmdtype = AET_UPDATE_CONTROL;
+ 					n->extname = $5;
+ 					n->version = $7;
+ 					n->control = $9;
+ 					n->missing_ok = false;
+ 					$$ = (Node *) n;
+ 				}
+ 			| ALTER TEMPLATE FOR EXTENSION name
+ 			  FROM ColId_or_Sconst TO ColId_or_Sconst
+ 			  AS Sconst
+ 				{
+ 					AlterExtTemplateStmt *n = makeNode(AlterExtTemplateStmt);
+ 					n->tmpltype = TEMPLATE_UPDATE_EXTENSION;
+ 					n->cmdtype = AET_SET_SCRIPT;
+ 					n->extname = $5;
+ 					n->from = $7;
+ 					n->to = $9;
+ 					n->script = $11;
+ 					n->missing_ok = false;
+ 					$$ = (Node *) n;
+ 				}
+ 		;
+ 
+ /*****************************************************************************
+  *
+  *		QUERY:
+  *             DROP TEMPLATE FOR EXTENSION name
+  *
+  *****************************************************************************/
+ DropTemplateStmt:
+ 			DROP TEMPLATE FOR EXTENSION name
+ 			VERSION_P ColId_or_Sconst opt_drop_behavior
+ 				{
+ 					DropStmt *n = makeNode(DropStmt);
+ 					n->removeType = OBJECT_EXTENSION_TEMPLATE;
+ 					n->objects = list_make1(list_make1(makeString($5)));
+ 					n->arguments = list_make1(list_make1(makeString($7)));
+ 					n->behavior = $8;
+ 					n->missing_ok = false;
+ 					n->concurrent = false;
+ 					$$ = (Node *)n;
+ 				}
+ 			| DROP TEMPLATE FOR EXTENSION name
+ 			FROM ColId_or_Sconst TO ColId_or_Sconst opt_drop_behavior
+ 				{
+ 					DropStmt *n = makeNode(DropStmt);
+ 					n->removeType = OBJECT_EXTENSION_UPTMPL;
+ 					n->objects = list_make1(list_make1(makeString($5)));
+ 					n->arguments = list_make1(list_make2(makeString($7),
+ 														 makeString($9)));
+ 					n->behavior = $10;
+ 					n->missing_ok = false;
+ 					n->concurrent = false;
+ 					$$ = (Node *)n;
+ 				}
+ 		;
+ 
+ create_template_control:
+ 			'(' create_template_control_plist ')'		{ $$ = $2; }
+ 		;
+ 
+ 
+ create_template_control_plist:
+ 			create_template_control_item
+ 				{ $$ = list_make1($1); }
+ 			| create_template_control_plist ',' create_template_control_item
+ 				{ $$ = lappend($1, $3); }
+ 			| /* EMPTY */
+ 				{ $$ = NIL; }
+ 		;
+ 
+ create_template_control_item:
+ 			SCHEMA name
+ 				{
+ 					$$ = makeDefElem("schema", (Node *)makeString($2));
+ 				}
+ 			| IDENT
+ 				{
+ 					/*
+ 					 * We handle identifiers that aren't parser keywords with
+ 					 * the following special-case codes, to avoid bloating the
+ 					 * size of the main parser.
+ 					 */
+ 					if (strcmp($1, "superuser") == 0)
+ 						$$ = makeDefElem("superuser", (Node *)makeInteger(TRUE));
+ 					else if (strcmp($1, "nosuperuser") == 0)
+ 						$$ = makeDefElem("superuser", (Node *)makeInteger(FALSE));
+ 					else if (strcmp($1, "relocatable") == 0)
+ 						$$ = makeDefElem("relocatable", (Node *)makeInteger(TRUE));
+ 					else if (strcmp($1, "norelocatable") == 0)
+ 						$$ = makeDefElem("relocatable", (Node *)makeInteger(FALSE));
+ 					else if (strcmp($1, "requires") == 0)
+ 						ereport(ERROR,
+ 								(errcode(ERRCODE_SYNTAX_ERROR),
+ 								 errmsg("template option \"%s\" takes an argument", $1),
+ 									 parser_errposition(@1)));
+ 					else
+ 						ereport(ERROR,
+ 								(errcode(ERRCODE_SYNTAX_ERROR),
+ 								 errmsg("unrecognized template option \"%s\"", $1),
+ 									 parser_errposition(@1)));
+ 				}
+ 			| IDENT Sconst
+ 				{
+ 					if (strcmp($1, "requires") == 0)
+ 						$$ = makeDefElem("requires", (Node *)makeString($2));
+ 					else
+ 						ereport(ERROR,
+ 								(errcode(ERRCODE_SYNTAX_ERROR),
+ 								 errmsg("unrecognized template option \"%s\"", $1),
+ 									 parser_errposition(@1)));
+ 				}
+ 		;
+ 
+ /*****************************************************************************
+  *
+  *		QUERY:
   *             CREATE EXTENSION extension
   *             [ WITH ] [ SCHEMA schema ] [ VERSION version ] [ FROM oldversion ]
   *
***************
*** 7309,7314 **** RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name
--- 7541,7555 ----
  					n->missing_ok = false;
  					$$ = (Node *)n;
  				}
+ 			| ALTER TEMPLATE FOR EXTENSION name RENAME TO name
+ 				{
+ 					RenameStmt *n = makeNode(RenameStmt);
+ 					n->renameType = OBJECT_EXTENSION_TEMPLATE;
+ 					n->subname = $5;
+ 					n->newname = $8;
+ 					n->missing_ok = false;
+ 					$$ = (Node *)n;
+ 				}
  		;
  
  opt_column: COLUMN									{ $$ = COLUMN; }
***************
*** 7712,7717 **** AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleId
--- 7953,7966 ----
  					n->newowner = $7;
  					$$ = (Node *)n;
  				}
+ 			| ALTER TEMPLATE FOR EXTENSION name OWNER TO RoleId
+ 				{
+ 					AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
+ 					n->objectType = OBJECT_EXTENSION_TEMPLATE;
+ 					n->object = list_make1(makeString($5));
+ 					n->newowner = $8;
+ 					$$ = (Node *)n;
+ 				}
  		;
  
  
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
***************
*** 47,52 ****
--- 47,53 ----
  #include "commands/sequence.h"
  #include "commands/tablecmds.h"
  #include "commands/tablespace.h"
+ #include "commands/template.h"
  #include "commands/trigger.h"
  #include "commands/typecmds.h"
  #include "commands/user.h"
***************
*** 226,231 **** check_xact_readonly(Node *parsetree)
--- 227,234 ----
  		case T_CreateExtensionStmt:
  		case T_AlterExtensionStmt:
  		case T_AlterExtensionContentsStmt:
+ 		case T_CreateExtTemplateStmt:
+ 		case T_AlterExtTemplateStmt:
  		case T_CreateFdwStmt:
  		case T_AlterFdwStmt:
  		case T_CreateForeignServerStmt:
***************
*** 654,659 **** standard_ProcessUtility(Node *parsetree,
--- 657,674 ----
  				ExecAlterExtensionContentsStmt((AlterExtensionContentsStmt *) parsetree));
  			break;
  
+ 		case T_CreateExtTemplateStmt:
+ 			InvokeDDLCommandEventTriggers(
+ 				parsetree,
+ 				CreateTemplate((CreateExtTemplateStmt *) parsetree));
+ 			break;
+ 
+ 		case T_AlterExtTemplateStmt:
+ 			InvokeDDLCommandEventTriggers(
+ 				parsetree,
+ 				AlterTemplate((AlterExtTemplateStmt *) parsetree));
+ 			break;
+ 
  		case T_CreateFdwStmt:
  			InvokeDDLCommandEventTriggers(
  				parsetree,
***************
*** 1670,1675 **** AlterObjectTypeCommandTag(ObjectType objtype)
--- 1685,1693 ----
  		case OBJECT_MATVIEW:
  			tag = "ALTER MATERIALIZED VIEW";
  			break;
+ 		case OBJECT_EXTENSION_TEMPLATE:
+ 			tag = "ALTER TEMPLATE FOR EXTENSION";
+ 			break;
  		default:
  			tag = "???";
  			break;
***************
*** 1823,1828 **** CreateCommandTag(Node *parsetree)
--- 1841,1854 ----
  			tag = "ALTER EXTENSION";
  			break;
  
+ 		case T_CreateExtTemplateStmt:
+ 			tag = "CREATE TEMPLATE FOR EXTENSION";
+ 			break;
+ 
+ 		case T_AlterExtTemplateStmt:
+ 			tag = "ALTER TEMPLATE FOR EXTENSION";
+ 			break;
+ 
  		case T_CreateFdwStmt:
  			tag = "CREATE FOREIGN DATA WRAPPER";
  			break;
***************
*** 1906,1911 **** CreateCommandTag(Node *parsetree)
--- 1932,1943 ----
  				case OBJECT_EXTENSION:
  					tag = "DROP EXTENSION";
  					break;
+ 				case OBJECT_EXTENSION_TEMPLATE:
+ 					tag = "DROP TEMPLATE FOR EXTENSION";
+ 					break;
+ 				case OBJECT_EXTENSION_UPTMPL:
+ 					tag = "DROP TEMPLATE FOR EXTENSION";
+ 					break;
  				case OBJECT_FUNCTION:
  					tag = "DROP FUNCTION";
  					break;
***************
*** 2502,2507 **** GetCommandLogLevel(Node *parsetree)
--- 2534,2544 ----
  			lev = LOGSTMT_DDL;
  			break;
  
+ 		case T_CreateExtTemplateStmt:
+ 		case T_AlterExtTemplateStmt:
+ 			lev = LOGSTMT_DDL;
+ 			break;
+ 
  		case T_CreateFdwStmt:
  		case T_AlterFdwStmt:
  		case T_CreateForeignServerStmt:
*** a/src/bin/pg_dump/common.c
--- b/src/bin/pg_dump/common.c
***************
*** 83,88 **** getSchemaData(Archive *fout, int *numTablesPtr)
--- 83,89 ----
  	InhInfo    *inhinfo;
  	CollInfo   *collinfo;
  	int			numExtensions;
+ 	int			numExtensionTemplates;
  	int			numAggregates;
  	int			numInherits;
  	int			numRules;
***************
*** 120,125 **** getSchemaData(Archive *fout, int *numTablesPtr)
--- 121,130 ----
  	getOwnedSeqs(fout, tblinfo, numTables);
  
  	if (g_verbose)
+ 		write_msg(NULL, "reading extension templates\n");
+ 	getExtensionTemplates(fout, &numExtensionTemplates);
+ 
+ 	if (g_verbose)
  		write_msg(NULL, "reading extensions\n");
  	extinfo = getExtensions(fout, &numExtensions);
  
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
***************
*** 168,173 **** static int	collectSecLabels(Archive *fout, SecLabelItem **items);
--- 168,174 ----
  static void dumpDumpableObject(Archive *fout, DumpableObject *dobj);
  static void dumpNamespace(Archive *fout, NamespaceInfo *nspinfo);
  static void dumpExtension(Archive *fout, ExtensionInfo *extinfo);
+ static void dumpExtensionTemplate(Archive *fout, ExtensionTemplateInfo *extinfo);
  static void dumpType(Archive *fout, TypeInfo *tyinfo);
  static void dumpBaseType(Archive *fout, TypeInfo *tyinfo);
  static void dumpEnumType(Archive *fout, TypeInfo *tyinfo);
***************
*** 2911,2916 **** findNamespace(Archive *fout, Oid nsoid, Oid objoid)
--- 2912,3038 ----
   *
   *	numExtensions is set to the number of extensions read in
   */
+ ExtensionTemplateInfo *
+ getExtensionTemplates(Archive *fout, int *numExtensionTemplates)
+ {
+ 	PGresult   *res;
+ 	int			ntups;
+ 	int			i;
+ 	PQExpBuffer query;
+ 	ExtensionTemplateInfo *exttmplinfo;
+ 	int			i_tableoid;
+ 	int			i_oid;
+ 	int			i_extname;
+ 	int			i_namespace;
+ 	int			i_isdefault;
+ 	int			i_relocatable;
+ 	int			i_superuser;
+ 	int			i_requires;
+ 	int			i_install;
+ 	int			i_version;
+ 	int			i_from;
+ 	int			i_to;
+ 	int			i_script;
+ 
+ 	/*
+ 	 * Before 9.3, there are no extension templates.
+ 	 */
+ 	if (fout->remoteVersion < 90300)
+ 	{
+ 		*numExtensionTemplates = 0;
+ 		return NULL;
+ 	}
+ 
+ 	query = createPQExpBuffer();
+ 
+ 	/* Make sure we are in proper schema */
+ 	selectSourceSchema(fout, "pg_catalog");
+ 
+ 	/*
+ 	 * The main catalog object for an extension template is the
+ 	 * pg_extension_control entry, from which thanks to pg_depend we fetch
+ 	 * either the extension creation script or its upgrade script.
+ 	 */
+ 	appendPQExpBuffer(query, "select c.tableoid, c.oid, "
+ 					  "c.ctlname, c.ctldefault, c.ctlrelocatable, "
+ 					  "c.ctlsuperuser, c.ctlnamespace, "
+ 					  "array_to_string(c.ctlrequires, ',') as requires, "
+ 					  "true as install, t.tplversion as version, "
+ 					  "null as uptfrom, null as uptto, t.tplscript as script "
+ 					  "from pg_extension_control c "
+ 					  "join pg_depend d "
+ 					  "on d.classid = 'pg_catalog.pg_extension_control'::regclass "
+ 					  "and d.objid = c.oid "
+ 					  "and d.refclassid = 'pg_catalog.pg_extension_template'::regclass "
+ 					  "join pg_extension_template t on t.oid = d.refobjid "
+ 					  "union all  "
+ 					  "select c.tableoid, c.oid, "
+ 					  "c.ctlname, c.ctldefault, c.ctlrelocatable, "
+ 					  "c.ctlsuperuser, c.ctlnamespace, "
+ 					  "array_to_string(c.ctlrequires, ',') as requires, "
+ 					  "false as install, null as version, "
+ 					  "u.uptfrom, u.uptto, u.uptscript as script "
+ 					  "from pg_extension_control c "
+ 					  "join pg_depend d on d.classid = 'pg_catalog.pg_extension_control'::regclass "
+ 					  "and d.objid = c.oid "
+ 					  "and d.refclassid = 'pg_catalog.pg_extension_uptmpl'::regclass "
+ 					  "join pg_extension_uptmpl u on u.oid = d.refobjid ");
+ 
+ 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+ 
+ 	ntups = PQntuples(res);
+ 
+ 	exttmplinfo = (ExtensionTemplateInfo *)
+ 		pg_malloc(ntups * sizeof(ExtensionTemplateInfo));
+ 
+ 	i_tableoid = PQfnumber(res, "tableoid");
+ 	i_oid = PQfnumber(res, "oid");
+ 	i_extname = PQfnumber(res, "ctlname");
+ 	i_isdefault = PQfnumber(res, "ctldefault");
+ 	i_relocatable = PQfnumber(res, "ctlrelocatable");
+ 	i_namespace = PQfnumber(res, "ctlnamespace");
+ 	i_requires = PQfnumber(res, "requires");
+ 	i_superuser = PQfnumber(res, "ctlsuperuser");
+ 	i_install = PQfnumber(res, "install");
+ 	i_version = PQfnumber(res, "version");
+ 	i_from = PQfnumber(res, "uptfrom");
+ 	i_to = PQfnumber(res, "uptto");
+ 	i_script = PQfnumber(res, "script");
+ 
+ 	for (i = 0; i < ntups; i++)
+ 	{
+ 		exttmplinfo[i].dobj.objType = DO_EXTENSION_TEMPLATE;
+ 		exttmplinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
+ 		exttmplinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ 		AssignDumpId(&exttmplinfo[i].dobj);
+ 		exttmplinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_extname));
+ 		exttmplinfo[i].namespace = pg_strdup(PQgetvalue(res, i, i_namespace));
+ 		exttmplinfo[i].isdefault = *(PQgetvalue(res, i, i_isdefault)) == 't';
+ 		exttmplinfo[i].relocatable = *(PQgetvalue(res, i, i_relocatable)) == 't';
+ 		exttmplinfo[i].superuser = *(PQgetvalue(res, i, i_superuser)) == 't';
+ 		exttmplinfo[i].install = *(PQgetvalue(res, i, i_install)) == 't';
+ 		exttmplinfo[i].requires = pg_strdup(PQgetvalue(res, i, i_requires));
+ 		exttmplinfo[i].version = pg_strdup(PQgetvalue(res, i, i_version));
+ 		exttmplinfo[i].from = pg_strdup(PQgetvalue(res, i, i_from));
+ 		exttmplinfo[i].to = pg_strdup(PQgetvalue(res, i, i_to));
+ 		exttmplinfo[i].script = pg_strdup(PQgetvalue(res, i, i_script));
+ 	}
+ 
+ 	PQclear(res);
+ 	destroyPQExpBuffer(query);
+ 
+ 	*numExtensionTemplates = ntups;
+ 
+ 	return exttmplinfo;
+ }
+ 
+ /*
+  * getExtensions:
+  *	  read all extensions in the system catalogs and return them in the
+  * ExtensionInfo* structure
+  *
+  *	numExtensions is set to the number of extensions read in
+  */
  ExtensionInfo *
  getExtensions(Archive *fout, int *numExtensions)
  {
***************
*** 7507,7512 **** dumpDumpableObject(Archive *fout, DumpableObject *dobj)
--- 7629,7637 ----
  		case DO_NAMESPACE:
  			dumpNamespace(fout, (NamespaceInfo *) dobj);
  			break;
+ 		case DO_EXTENSION_TEMPLATE:
+ 			dumpExtensionTemplate(fout, (ExtensionTemplateInfo *) dobj);
+ 			break;
  		case DO_EXTENSION:
  			dumpExtension(fout, (ExtensionInfo *) dobj);
  			break;
***************
*** 7682,7687 **** dumpNamespace(Archive *fout, NamespaceInfo *nspinfo)
--- 7807,7917 ----
  }
  
  /*
+  * dumpExtensionTemplate
+  *	  writes out to fout the queries to recreate an extension
+  */
+ static void
+ dumpExtensionTemplate(Archive *fout, ExtensionTemplateInfo *exttmplinfo)
+ {
+ 	PQExpBuffer q;
+ 	PQExpBuffer delq;
+ 	PQExpBuffer labelq;
+ 	char	   *qextname,
+ 			   *qversion,
+ 			   *qfrom,
+ 			   *qto;
+ 	bool 		upgrade; /* install or upgrade script? */
+ 
+ 	/* Skip if not to be dumped */
+ 	if (!exttmplinfo->dobj.dump || dataOnly)
+ 		return;
+ 
+ 	q = createPQExpBuffer();
+ 	delq = createPQExpBuffer();
+ 	labelq = createPQExpBuffer();
+ 
+ 	qextname = pg_strdup(fmtId(exttmplinfo->dobj.name));
+ 	upgrade = !exttmplinfo->install;
+ 
+ 	if (upgrade)
+ 	{
+ 		qfrom = pg_strdup(fmtId(exttmplinfo->from));
+ 		qto = pg_strdup(fmtId(exttmplinfo->to));
+ 		qversion = NULL;
+ 
+ 		appendPQExpBuffer(delq, "DROP TEMPLATE FOR EXTENSION %s FROM %s TO %s;\n",
+ 						  qextname, qfrom, qto);
+ 	}
+ 	else
+ 	{
+ 		qversion = pg_strdup(fmtId(exttmplinfo->version));
+ 
+ 		appendPQExpBuffer(delq, "DROP TEMPLATE FOR EXTENSION %s VERSION %s;\n",
+ 						  qextname, qversion);
+ 	}
+ 
+ 	appendPQExpBuffer(q, "CREATE TEMPLATE FOR EXTENSION %s", qextname);
+ 
+ 	if (upgrade)
+ 		appendPQExpBuffer(q, " FROM %s TO %s", qfrom, qto);
+ 	else
+ 	{
+ 		if (exttmplinfo->isdefault)
+ 			appendPQExpBuffer(q, " DEFAULT");
+ 
+ 		appendPQExpBuffer(q, " VERSION %s", qversion);
+ 	}
+ 
+ 	/* control options  */
+ 	appendPQExpBuffer(q, "\n  WITH (schema %s, %ssuperuser, %srelocatable",
+ 					  fmtId(exttmplinfo->namespace),
+ 					  exttmplinfo->superuser ? "" : "no",
+ 					  exttmplinfo->relocatable ? "" : "no");
+ 
+ 	if (exttmplinfo->requires && strlen(exttmplinfo->requires))
+ 		appendPQExpBuffer(q, ", requires '%s'", exttmplinfo->requires);
+ 	appendPQExpBuffer(q, ")\n");
+ 
+ 	/* extension script (either install or upgrade script) */
+ 	appendPQExpBuffer(q, " AS\n$$%s$$;\n", exttmplinfo->script);
+ 
+ 	/*
+ 	 * When the default version is not a create script, we need an extra ALTER
+ 	 * statement here.
+ 	 */
+ 	if (!upgrade && exttmplinfo->isdefault)
+ 	{
+ 		appendPQExpBuffer(q, "\nALTER TEMPLATE FOR EXTENSION %s ", qextname);
+ 		appendPQExpBuffer(q, "SET DEFAULT VERSION %s;\n", qversion);
+ 	}
+ 
+ 	appendPQExpBuffer(labelq, "TEMPLATE FOR EXTENSION %s", qextname);
+ 
+ 	ArchiveEntry(fout, exttmplinfo->dobj.catId, exttmplinfo->dobj.dumpId,
+ 				 exttmplinfo->dobj.name,
+ 				 NULL, NULL,
+ 				 "",
+ 				 false, "EXTENSION TEMPLATE", SECTION_PRE_DATA,
+ 				 q->data, delq->data, NULL,
+ 				 NULL, 0,
+ 				 NULL, NULL);
+ 
+ 	/* Dump Extension Comments and Security Labels */
+ 	dumpComment(fout, labelq->data,
+ 				NULL, "",
+ 				exttmplinfo->dobj.catId, 0, exttmplinfo->dobj.dumpId);
+ 	dumpSecLabel(fout, labelq->data,
+ 				 NULL, "",
+ 				 exttmplinfo->dobj.catId, 0, exttmplinfo->dobj.dumpId);
+ 
+ 	free(qextname);
+ 
+ 	destroyPQExpBuffer(q);
+ 	destroyPQExpBuffer(delq);
+ 	destroyPQExpBuffer(labelq);
+ }
+ 
+ /*
   * dumpExtension
   *	  writes out to fout the queries to recreate an extension
   */
***************
*** 14689,14694 **** addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
--- 14919,14925 ----
  		{
  			case DO_NAMESPACE:
  			case DO_EXTENSION:
+ 			case DO_EXTENSION_TEMPLATE:
  			case DO_TYPE:
  			case DO_SHELL_TYPE:
  			case DO_FUNC:
*** a/src/bin/pg_dump/pg_dump.h
--- b/src/bin/pg_dump/pg_dump.h
***************
*** 79,84 **** typedef enum
--- 79,85 ----
  	/* When modifying this enum, update priority tables in pg_dump_sort.c! */
  	DO_NAMESPACE,
  	DO_EXTENSION,
+ 	DO_EXTENSION_TEMPLATE,
  	DO_TYPE,
  	DO_SHELL_TYPE,
  	DO_FUNC,
***************
*** 145,150 **** typedef struct _extensionInfo
--- 146,166 ----
  	char	   *extcondition;
  } ExtensionInfo;
  
+ typedef struct _extensionTemplateInfo
+ {
+ 	DumpableObject dobj;
+ 	char	   *namespace;		/* schema containing extension's objects */
+ 	bool		isdefault;		/* from pg_extension_control */
+ 	bool		relocatable;	/* from pg_extension_control */
+ 	bool		superuser;		/* from pg_extension_control */
+ 	char	   *requires;		/* from pg_extension_control */
+ 	bool	    install;		/* true if install template (not upgrade) */
+ 	char	   *version;		/* from pg_extension_template */
+ 	char	   *from;			/* from pg_extension_uptmpl */
+ 	char	   *to;				/* from pg_extension_uptmpl */
+ 	char	   *script;			/* both from template or uptmpl */
+ } ExtensionTemplateInfo;
+ 
  typedef struct _typeInfo
  {
  	DumpableObject dobj;
***************
*** 538,543 **** extern void sortDumpableObjectsByTypeOid(DumpableObject **objs, int numObjs);
--- 554,561 ----
   */
  extern NamespaceInfo *getNamespaces(Archive *fout, int *numNamespaces);
  extern ExtensionInfo *getExtensions(Archive *fout, int *numExtensions);
+ extern ExtensionTemplateInfo *getExtensionTemplates(Archive *fout,
+ 												   int *numExtensionTemplates);
  extern TypeInfo *getTypes(Archive *fout, int *numTypes);
  extern FuncInfo *getFuncs(Archive *fout, int *numFuncs);
  extern AggInfo *getAggregates(Archive *fout, int *numAggregates);
*** a/src/bin/pg_dump/pg_dump_sort.c
--- b/src/bin/pg_dump/pg_dump_sort.c
***************
*** 37,42 **** static const int oldObjectTypePriority[] =
--- 37,43 ----
  {
  	1,							/* DO_NAMESPACE */
  	1,							/* DO_EXTENSION */
+ 	1,							/* DO_EXTENSION_TEMPLATE */
  	2,							/* DO_TYPE */
  	2,							/* DO_SHELL_TYPE */
  	2,							/* DO_FUNC */
***************
*** 85,90 **** static const int newObjectTypePriority[] =
--- 86,92 ----
  {
  	1,							/* DO_NAMESPACE */
  	4,							/* DO_EXTENSION */
+ 	4,							/* DO_EXTENSION_TEMPLATE */
  	5,							/* DO_TYPE */
  	5,							/* DO_SHELL_TYPE */
  	6,							/* DO_FUNC */
***************
*** 1087,1092 **** describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
--- 1089,1099 ----
  					 "SCHEMA %s  (ID %d OID %u)",
  					 obj->name, obj->dumpId, obj->catId.oid);
  			return;
+ 		case DO_EXTENSION_TEMPLATE:
+ 			snprintf(buf, bufsize,
+ 					 "EXTENSION TEMPLATE %s  (ID %d OID %u)",
+ 					 obj->name, obj->dumpId, obj->catId.oid);
+ 			return;
  		case DO_EXTENSION:
  			snprintf(buf, bufsize,
  					 "EXTENSION %s  (ID %d OID %u)",
*** a/src/include/catalog/dependency.h
--- b/src/include/catalog/dependency.h
***************
*** 146,151 **** typedef enum ObjectClass
--- 146,154 ----
  	OCLASS_USER_MAPPING,		/* pg_user_mapping */
  	OCLASS_DEFACL,				/* pg_default_acl */
  	OCLASS_EXTENSION,			/* pg_extension */
+ 	OCLASS_EXTENSION_CONTROL,	/* pg_extension_control */
+ 	OCLASS_EXTENSION_TEMPLATE,	/* pg_extension_template */
+ 	OCLASS_EXTENSION_UPTMPL,	/* pg_extension_uptmpl */
  	OCLASS_EVENT_TRIGGER,		/* pg_event_trigger */
  	MAX_OCLASS					/* MUST BE LAST */
  } ObjectClass;
*** a/src/include/catalog/indexing.h
--- b/src/include/catalog/indexing.h
***************
*** 307,312 **** DECLARE_UNIQUE_INDEX(pg_extension_oid_index, 3080, on pg_extension using btree(o
--- 307,329 ----
  
  DECLARE_UNIQUE_INDEX(pg_extension_name_index, 3081, on pg_extension using btree(extname name_ops));
  #define ExtensionNameIndexId 3081
+ DECLARE_UNIQUE_INDEX(pg_extension_template_oid_index, 3180, on pg_extension_template using btree(oid oid_ops));
+ #define ExtensionTemplateOidIndexId 3180
+ 
+ DECLARE_UNIQUE_INDEX(pg_extension_template_name_version_index, 3181, on pg_extension_template using btree(tplname name_ops, tplversion text_ops));
+ #define ExtensionTemplateNameVersionIndexId 3181
+ 
+ DECLARE_UNIQUE_INDEX(pg_extension_uptmpl_oid_index, 3280, on pg_extension_uptmpl using btree(oid oid_ops));
+ #define ExtensionUpTmplOidIndexId 3280
+ 
+ DECLARE_UNIQUE_INDEX(pg_extension_uptmpl_name_from_to_index, 3281, on pg_extension_uptmpl using btree(uptname name_ops, uptfrom text_ops, uptto text_ops));
+ #define ExtensionUpTpmlNameFromToIndexId 3281
+ 
+ DECLARE_UNIQUE_INDEX(pg_extension_control_oid_index, 3380, on pg_extension_control using btree(oid oid_ops));
+ #define ExtensionControlOidIndexId 3380
+ 
+ DECLARE_UNIQUE_INDEX(pg_extension_control_name_version_index, 3381, on pg_extension_control using btree(ctlname name_ops, ctlversion text_ops));
+ #define ExtensionControlNameVersionIndexId 3381
  
  DECLARE_UNIQUE_INDEX(pg_range_rngtypid_index, 3542, on pg_range using btree(rngtypid oid_ops));
  #define RangeTypidIndexId					3542
*** /dev/null
--- b/src/include/catalog/pg_extension_control.h
***************
*** 0 ****
--- 1,75 ----
+ /*-------------------------------------------------------------------------
+  *
+  * pg_extension_control.h
+  *	  definition of the system "extension_control" relation
+  *    (pg_extension_control) along with the relation's initial contents.
+  *
+  *
+  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/include/catalog/pg_extension_control.h
+  *
+  * NOTES
+  *	  the genbki.pl script reads this file and generates .bki
+  *	  information from the DATA() statements.
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef PG_EXTENSION_CONTROL_H
+ #define PG_EXTENSION_CONTROL_H
+ 
+ #include "catalog/genbki.h"
+ 
+ /* ----------------
+  *		pg_extension_install_control definition.  cpp turns this into
+  *		typedef struct FormData_pg_extension_control
+  * ----------------
+  */
+ #define ExtensionControlRelationId 3379
+ 
+ CATALOG(pg_extension_control,3379)
+ {
+ 	NameData	ctlname;		/* extension name */
+ 	Oid			ctlowner;		/* control owner */
+ 	bool		ctldefault;		/* is this version the default? */
+ 	bool		ctldefaultfull;	/* is this version the default full version? */
+ 	bool		ctlrelocatable;	/* extension is relocatable? */
+ 	bool		ctlsuperuser;	/* extension is superuser only? */
+ 	NameData	ctlnamespace;	/* namespace of contained objects */
+ 
+ #ifdef CATALOG_VARLEN			/* variable-length fields start here */
+ 	text		ctlversion;		/* version to install with this control */
+ 	text 		ctlrequires[1];	/* extension dependency list */
+ #endif
+ } FormData_pg_extension_control;
+ 
+ /* ----------------
+  *		Form_pg_extension_control corresponds to a pointer to a tuple with the
+  *		format of pg_extension_control relation.
+  * ----------------
+  */
+ typedef FormData_pg_extension_control *Form_pg_extension_control;
+ 
+ /* ----------------
+  *		compiler constants for pg_extension_control
+  * ----------------
+  */
+ 
+ #define Natts_pg_extension_control					9
+ #define Anum_pg_extension_control_ctlname			1
+ #define Anum_pg_extension_control_ctlowner			2
+ #define Anum_pg_extension_control_ctldefault		3
+ #define Anum_pg_extension_control_ctldefaultfull	4
+ #define Anum_pg_extension_control_ctlrelocatable	5
+ #define Anum_pg_extension_control_ctlsuperuser		6
+ #define Anum_pg_extension_control_ctlnamespace		7
+ #define Anum_pg_extension_control_ctlversion		8
+ #define Anum_pg_extension_control_ctlrequires		9
+ 
+ /* ----------------
+  *		pg_extension_control has no initial contents
+  * ----------------
+  */
+ 
+ #endif   /* PG_EXTENSION_CONTROL_H */
*** /dev/null
--- b/src/include/catalog/pg_extension_template.h
***************
*** 0 ****
--- 1,65 ----
+ /*-------------------------------------------------------------------------
+  *
+  * pg_extension_template.h
+  *	  definition of the system "extension_template" relation
+  *    (pg_extension_template) along with the relation's initial contents.
+  *
+  *
+  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/include/catalog/pg_extension_template.h
+  *
+  * NOTES
+  *	  the genbki.pl script reads this file and generates .bki
+  *	  information from the DATA() statements.
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef PG_EXTENSION_TEMPLATE_H
+ #define PG_EXTENSION_TEMPLATE_H
+ 
+ #include "catalog/genbki.h"
+ 
+ /* ----------------
+  *		pg_extension_install_template definition.  cpp turns this into
+  *		typedef struct FormData_pg_extension_template
+  * ----------------
+  */
+ #define ExtensionTemplateRelationId 3179
+ 
+ CATALOG(pg_extension_template,3179)
+ {
+ 	NameData	tplname;		/* extension name */
+ 	Oid			tplowner;		/* template owner */
+ 
+ #ifdef CATALOG_VARLEN			/* variable-length fields start here */
+ 	text		tplversion;		/* version to install with this template */
+ 	text		tplscript;		/* extension's install script */
+ #endif
+ } FormData_pg_extension_template;
+ 
+ /* ----------------
+  *		Form_pg_extension_template corresponds to a pointer to a tuple with the
+  *		format of pg_extension_template relation.
+  * ----------------
+  */
+ typedef FormData_pg_extension_template *Form_pg_extension_template;
+ 
+ /* ----------------
+  *		compiler constants for pg_extension_template
+  * ----------------
+  */
+ 
+ #define Natts_pg_extension_template				4
+ #define Anum_pg_extension_template_tplname		1
+ #define Anum_pg_extension_template_tplowner		2
+ #define Anum_pg_extension_template_tplversion	3
+ #define Anum_pg_extension_template_tplscript	4
+ 
+ /* ----------------
+  *		pg_extension_template has no initial contents
+  * ----------------
+  */
+ 
+ #endif   /* PG_EXTENSION_TEMPLATE_H */
*** /dev/null
--- b/src/include/catalog/pg_extension_uptmpl.h
***************
*** 0 ****
--- 1,67 ----
+ /*-------------------------------------------------------------------------
+  *
+  * pg_extension_uptmpl.h
+  *	  definition of the system "extension_uptmpl" relation
+  *    (pg_extension_uptmpl) along with the relation's initial contents.
+  *
+  *
+  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/include/catalog/pg_extension_uptmpl.h
+  *
+  * NOTES
+  *	  the genbki.pl script reads this file and generates .bki
+  *	  information from the DATA() statements.
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef PG_EXTENSION_UPTMPL_H
+ #define PG_EXTENSION_UPTMPL_H
+ 
+ #include "catalog/genbki.h"
+ 
+ /* ----------------
+  *		pg_extension_install_uptmpl definition.  cpp turns this into
+  *		typedef struct FormData_pg_extension_uptmpl
+  * ----------------
+  */
+ #define ExtensionUpTmplRelationId 3279
+ 
+ CATALOG(pg_extension_uptmpl,3279)
+ {
+ 	NameData	uptname;		/* extension name */
+ 	Oid			uptowner;		/* template owner */
+ 
+ #ifdef CATALOG_VARLEN			/* variable-length fields start here */
+ 	text		uptfrom;		/* version this template updates from */
+ 	text		uptto;			/* version this template updated to */
+ 	text		uptscript;		/* extension's update script */
+ #endif
+ } FormData_pg_extension_uptmpl;
+ 
+ /* ----------------
+  *		Form_pg_extension_uptmpl corresponds to a pointer to a tuple with the
+  *		format of pg_extension_uptmpl relation.
+  * ----------------
+  */
+ typedef FormData_pg_extension_uptmpl *Form_pg_extension_uptmpl;
+ 
+ /* ----------------
+  *		compiler constants for pg_extension_uptmpl
+  * ----------------
+  */
+ 
+ #define Natts_pg_extension_uptmpl			5
+ #define Anum_pg_extension_uptmpl_uptname	1
+ #define Anum_pg_extension_uptmpl_uptowner	2
+ #define Anum_pg_extension_uptmpl_uptfrom	3
+ #define Anum_pg_extension_uptmpl_uptto		4
+ #define Anum_pg_extension_uptmpl_uptscript	5
+ 
+ /* ----------------
+  *		pg_extension_uptmpl has no initial contents
+  * ----------------
+  */
+ 
+ #endif   /* PG_EXTENSION_UPTMPL_H */
*** a/src/include/commands/alter.h
--- b/src/include/commands/alter.h
***************
*** 19,24 ****
--- 19,26 ----
  #include "utils/relcache.h"
  
  extern Oid ExecRenameStmt(RenameStmt *stmt);
+ extern void AlterObjectRename_internal(Relation rel,
+ 									   Oid objectId, const char *new_name);
  
  extern Oid ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt);
  extern Oid AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid,
*** a/src/include/commands/extension.h
--- b/src/include/commands/extension.h
***************
*** 26,40 ****
  extern bool creating_extension;
  extern Oid	CurrentExtensionObject;
  
  
  extern Oid CreateExtension(CreateExtensionStmt *stmt);
  
  extern void RemoveExtensionById(Oid extId);
  
  extern Oid InsertExtensionTuple(const char *extName, Oid extOwner,
! 					 Oid schemaOid, bool relocatable, const char *extVersion,
! 					 Datum extConfig, Datum extCondition,
! 					 List *requiredExtensions);
  
  extern Oid ExecAlterExtensionStmt(AlterExtensionStmt *stmt);
  
--- 26,65 ----
  extern bool creating_extension;
  extern Oid	CurrentExtensionObject;
  
+ /*
+  * Internal data structure to hold the extension control information, that we
+  * get either from parsing a control file or from the pg_extension_control
+  * catalog when working from Extension Templates.
+  */
+ typedef struct ExtensionControl
+ {
+ 	Oid         ctrlOid;		 /* pg_control_extension oid, or invalidoid */
+ 	char	   *name;			/* name of the extension */
+ 	char	   *directory;		/* directory for script files */
+ 	char	   *default_version;	/* default install target version, if any */
+ 	char	   *default_full_version;	/* default install source version, if any */
+ 	char	   *module_pathname;	/* string to substitute for module_pathname */
+ 	char	   *comment;		/* comment, if any */
+ 	char	   *schema;			/* target schema (allowed if !relocatable) */
+ 	bool		relocatable;	/* is alter extension set schema supported? */
+ 	bool		superuser;		/* must be superuser to install? */
+ 	int			encoding;		/* encoding of the script file, or -1 */
+ 	List	   *requires;		/* names of prerequisite extensions */
+ 	bool		is_template;	/* true if we're using catalog templates */
+ } ExtensionControl;
+ 
+ extern ExtensionControl *read_extension_control_file(const char *extname);
+ extern void check_valid_extension_name(const char *extensionname);
  
  extern Oid CreateExtension(CreateExtensionStmt *stmt);
  
  extern void RemoveExtensionById(Oid extId);
  
  extern Oid InsertExtensionTuple(const char *extName, Oid extOwner,
! 								Oid schemaOid, bool relocatable,
! 								const char *extVersion,
! 								Datum extConfig, Datum extCondition,
! 								List *requiredExtensions, Oid ctrlOid);
  
  extern Oid ExecAlterExtensionStmt(AlterExtensionStmt *stmt);
  
*** /dev/null
--- b/src/include/commands/template.h
***************
*** 0 ****
--- 1,72 ----
+ /*-------------------------------------------------------------------------
+  *
+  * template.h
+  *		Template management commands (create/drop template).
+  *
+  *
+  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/include/commands/template.h
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef TEMPLATE_H
+ #define TEMPLATE_H
+ 
+ #include "nodes/parsenodes.h"
+ 
+ extern Oid CreateTemplate(CreateExtTemplateStmt *stmt);
+ extern Oid CreateExtensionTemplate(CreateExtTemplateStmt *stmt);
+ extern Oid CreateExtensionUpdateTemplate(CreateExtTemplateStmt *stmt);
+ 
+ extern char *get_extension_control_name(Oid ctrlOid);
+ extern char *get_extension_template_name(Oid tmplOid);
+ extern char *get_extension_uptmpl_name(Oid tmplOid);
+ 
+ extern Oid AtlerExtensionTemplateOwner(const char *extname, Oid newowner);
+ extern Oid AtlerExtensionTemplateRename(const char *extname,
+ 										const char *newname);
+ 
+ extern Oid AlterTemplate(AlterExtTemplateStmt *stmt);
+ extern Oid AlterExtensionTemplate(AlterExtTemplateStmt *stmt);
+ extern Oid AlterExtensionUpdateTemplate(AlterExtTemplateStmt *stmt);
+ 
+ extern Oid get_template_oid(const char *extname, const char *version,
+ 							bool missing_ok);
+ extern bool can_create_extension_from_template(const char *extname,
+ 											   bool missing_ok);
+ extern Oid get_uptmpl_oid(const char *extname,
+ 						  const char *from, const char *to,
+ 						  bool missing_ok);
+ 
+ extern void RemoveExtensionControlById(Oid extControlOid);
+ extern void RemoveExtensionTemplateById(Oid extTemplateOid);
+ extern void RemoveExtensionUpTmplById(Oid extUpTmplOid);
+ 
+ extern ExtensionControl *find_pg_extension_control(const char *extname,
+ 												   const char *version,
+ 												   bool missing_ok);
+ 
+ extern ExtensionControl *find_default_pg_extension_control(const char *extname,
+ 														   bool missing_ok);
+ 
+ extern char *read_pg_extension_template_script(const char *extname,
+ 											   const char *version);
+ extern char *read_pg_extension_uptmpl_script(const char *extname,
+ 											 const char *from_version,
+ 											 const char *version);
+ extern char *read_extension_template_script(const char *extname,
+ 											const char *from_version,
+ 											const char *version);
+ 
+ extern List *list_pg_extension_template_versions(const char *extname);
+ extern List *list_pg_extension_update_versions(const char *extname);
+ extern List *pg_extension_default_controls(void);
+ extern List *pg_extension_templates(void);
+ 
+ extern List *list_pg_extension_control_oids_for(const char *extname);
+ extern List *list_pg_extension_template_oids_for(const char *extname);
+ extern List *list_pg_extension_uptmpl_oids_for(const char *extname);
+ 
+ #endif   /* TEMPLATE_H */
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
***************
*** 362,367 **** typedef enum NodeTag
--- 362,369 ----
  	T_CreateEventTrigStmt,
  	T_AlterEventTrigStmt,
  	T_RefreshMatViewStmt,
+ 	T_CreateExtTemplateStmt,
+ 	T_AlterExtTemplateStmt,
  
  	/*
  	 * TAGS FOR PARSE TREE NODES (parsenodes.h)
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
***************
*** 1129,1134 **** typedef enum ObjectType
--- 1129,1136 ----
  	OBJECT_DOMAIN,
  	OBJECT_EVENT_TRIGGER,
  	OBJECT_EXTENSION,
+ 	OBJECT_EXTENSION_TEMPLATE,
+ 	OBJECT_EXTENSION_UPTMPL,
  	OBJECT_FDW,
  	OBJECT_FOREIGN_SERVER,
  	OBJECT_FOREIGN_TABLE,
***************
*** 1638,1643 **** typedef struct AlterExtensionContentsStmt
--- 1640,1688 ----
  	List	   *objargs;		/* Arguments if needed (eg, for functions) */
  } AlterExtensionContentsStmt;
  
+ typedef enum ExtTemplateType
+ {
+ 	TEMPLATE_CREATE_EXTENSION,
+ 	TEMPLATE_UPDATE_EXTENSION
+ } ExtTemplateType;
+ 
+ typedef struct CreateExtTemplateStmt
+ {
+ 	NodeTag		type;
+ 	ExtTemplateType tmpltype;
+ 	char	   *extname;		/* Extension's name */
+ 	char	   *version;		/* Version to create from the template */
+ 	char	   *from;			/*   In case of an update template, we update */
+ 	char	   *to;				/*   from version to version */
+ 	List	   *control;		/* List of DefElem nodes */
+ 	char	   *script;			/* Extension's install script */
+ 	bool		if_not_exists;	/* just do nothing if it already exists? */
+ 	bool        default_version; /* default version of this extension */
+ } CreateExtTemplateStmt;
+ 
+ typedef enum AlterExtTemplateType
+ {
+ 	AET_SET_DEFAULT,
+ 	AET_SET_DEFAULT_FULL,
+ 	AET_SET_SCRIPT,
+ 	AET_UPDATE_CONTROL,
+ } AlterExtTemplateType;
+ 
+ typedef struct AlterExtTemplateStmt
+ {
+ 	NodeTag		type;
+ 	ExtTemplateType tmpltype;
+ 	AlterExtTemplateType cmdtype; /* Type of command */
+ 	char	   *extname;				/* Extension's name */
+ 	char       *version;				/* Extension's version */
+ 	char	   *from;			/*   In case of an update template, we update */
+ 	char	   *to;				/*   from version to version */
+ 	List	   *control;		/* List of DefElem nodes */
+ 	char	   *script;			/* Extension's install script */
+ 	bool		missing_ok;		/* skip error if missing? */
+ 
+ } AlterExtTemplateStmt;
+ 
  /* ----------------------
   *		Create/Alter FOREIGN DATA WRAPPER Statements
   * ----------------------
*** a/src/include/utils/acl.h
--- b/src/include/utils/acl.h
***************
*** 197,202 **** typedef enum AclObjectKind
--- 197,205 ----
  	ACL_KIND_FOREIGN_SERVER,	/* pg_foreign_server */
  	ACL_KIND_EVENT_TRIGGER,		/* pg_event_trigger */
  	ACL_KIND_EXTENSION,			/* pg_extension */
+ 	ACL_KIND_EXTCONTROL,		/* pg_extension_control */
+ 	ACL_KIND_EXTTEMPLATE,		/* pg_extension_template */
+ 	ACL_KIND_EXTUPTMPL,			/* pg_extension_uptmpl */
  	MAX_ACL_KIND				/* MUST BE LAST */
  } AclObjectKind;
  
***************
*** 325,330 **** extern bool pg_foreign_data_wrapper_ownercheck(Oid srv_oid, Oid roleid);
--- 328,336 ----
  extern bool pg_foreign_server_ownercheck(Oid srv_oid, Oid roleid);
  extern bool pg_event_trigger_ownercheck(Oid et_oid, Oid roleid);
  extern bool pg_extension_ownercheck(Oid ext_oid, Oid roleid);
+ extern bool pg_extension_control_ownercheck(Oid ext_oid, Oid roleid);
+ extern bool pg_extension_template_ownercheck(Oid ext_template_oid, Oid roleid);
+ extern bool pg_extension_uptmpl_ownercheck(Oid ext_uptmpl_oid, Oid roleid);
  extern bool has_createrole_privilege(Oid roleid);
  
  #endif   /* ACL_H */
*** /dev/null
--- b/src/test/regress/expected/extension.out
***************
*** 0 ****
--- 1,214 ----
+ -- first create some templates
+ CREATE TEMPLATE
+    FOR EXTENSION pair DEFAULT VERSION '1.0'
+   WITH (superuser, norelocatable, schema public)
+ AS $$
+   CREATE TYPE pair AS ( k text, v text );
+   
+   CREATE OR REPLACE FUNCTION pair(anyelement, text)
+   RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair';
+   
+   CREATE OR REPLACE FUNCTION pair(text, anyelement)
+   RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair';
+   
+   CREATE OR REPLACE FUNCTION pair(anyelement, anyelement)
+   RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair';
+   
+   CREATE OR REPLACE FUNCTION pair(text, text)
+   RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair;';      
+ $$;
+ -- we want to test alter extension update
+ CREATE TEMPLATE
+    FOR EXTENSION pair FROM '1.0' TO '1.1'
+   WITH (superuser, norelocatable, schema public)
+ AS $$
+   CREATE OPERATOR ~> (LEFTARG = text,
+                       RIGHTARG = anyelement,
+                       PROCEDURE = pair);
+                       
+   CREATE OPERATOR ~> (LEFTARG = anyelement,
+                       RIGHTARG = text,
+                       PROCEDURE = pair);
+ 
+   CREATE OPERATOR ~> (LEFTARG = anyelement,
+                       RIGHTARG = anyelement,
+                       PROCEDURE = pair);
+                       
+   CREATE OPERATOR ~> (LEFTARG = text,
+                       RIGHTARG = text,
+                       PROCEDURE = pair);           
+ $$;
+ -- and we want to test update with a cycle
+ CREATE TEMPLATE
+    FOR EXTENSION pair FROM '1.1' TO '1.2'
+ AS
+  $$
+   COMMENT ON EXTENSION pair IS 'Simple Key Value Text Type';
+ $$;
+ -- test some ALTER commands
+ -- ok
+ ALTER TEMPLATE FOR EXTENSION pair VERSION '1.0' WITH (relocatable);
+ -- we don't have a version 1.3 known yet
+ ALTER TEMPLATE FOR EXTENSION pair VERSION '1.3' WITH (relocatable);
+ ERROR:  pg_extension_control for extension "pair" version "1.3" does not exist
+ -- you can't set the default on an upgrade script, only an extension's version
+ ALTER TEMPLATE FOR EXTENSION pair FROM '1.0' TO '1.1' SET DEFAULT;
+ ERROR:  syntax error at or near "SET"
+ LINE 1: ...R TEMPLATE FOR EXTENSION pair FROM '1.0' TO '1.1' SET DEFAUL...
+                                                              ^
+ -- you can't set control properties on an upgrade script, only an
+ -- extension's version
+ ALTER TEMPLATE FOR EXTENSION pair FROM '1.0' TO '1.1' WITH (relocatable);
+ ERROR:  syntax error at or near "WITH ("
+ LINE 1: ...R TEMPLATE FOR EXTENSION pair FROM '1.0' TO '1.1' WITH (relo...
+                                                              ^
+ -- try to set the default full version to an unknown extension version
+ ALTER TEMPLATE FOR EXTENSION pair SET DEFAULT FULL VERSION '1.1';
+ ERROR:  template for extension "pair" version "1.1" does not exist
+ -- now set it to the current one already, should silently do nothing
+ ALTER TEMPLATE FOR EXTENSION pair SET DEFAULT FULL VERSION '1.0';
+ -- you can actually change the script used to update, though
+ ALTER TEMPLATE FOR EXTENSION pair FROM '1.1' TO '1.2'
+ AS $$
+   COMMENT ON EXTENSION pair IS 'A Toy Key Value Text Type';
+ $$;
+ CREATE EXTENSION pair;
+ \dx pair
+      List of installed extensions
+  Name | Version | Schema | Description 
+ ------+---------+--------+-------------
+  pair | 1.0     | public | 
+ (1 row)
+ 
+ \dx+ pair
+      Objects in extension "pair"
+           Object Description          
+ --------------------------------------
+  function pair(anyelement,anyelement)
+  function pair(anyelement,text)
+  function pair(text,anyelement)
+  function pair(text,text)
+  type pair
+ (5 rows)
+ 
+ ALTER EXTENSION pair UPDATE TO '1.2';
+ \dx+ pair
+      Objects in extension "pair"
+           Object Description          
+ --------------------------------------
+  function pair(anyelement,anyelement)
+  function pair(anyelement,text)
+  function pair(text,anyelement)
+  function pair(text,text)
+  operator ~>(anyelement,anyelement)
+  operator ~>(anyelement,text)
+  operator ~>(text,anyelement)
+  operator ~>(text,text)
+  type pair
+ (9 rows)
+ 
+ DROP EXTENSION pair;
+ -- test with another full version that's not the default
+ CREATE TEMPLATE
+    FOR EXTENSION pair VERSION '1.3'
+   WITH (superuser, norelocatable, schema public)
+ AS $$
+   CREATE TYPE pair AS ( k text, v text );
+   
+   CREATE OR REPLACE FUNCTION pair(anyelement, text)
+   RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair';
+   
+   CREATE OR REPLACE FUNCTION pair(text, anyelement)
+   RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair';
+   
+   CREATE OR REPLACE FUNCTION pair(anyelement, anyelement)
+   RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair';
+   
+   CREATE OR REPLACE FUNCTION pair(text, text)
+   RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair;';      
+ 
+   CREATE OPERATOR ~> (LEFTARG = text,
+                       RIGHTARG = anyelement,
+                       PROCEDURE = pair);
+                       
+   CREATE OPERATOR ~> (LEFTARG = anyelement,
+                       RIGHTARG = text,
+                       PROCEDURE = pair);
+ 
+   CREATE OPERATOR ~> (LEFTARG = anyelement,
+                       RIGHTARG = anyelement,
+                       PROCEDURE = pair);
+                       
+   CREATE OPERATOR ~> (LEFTARG = text,
+                       RIGHTARG = text,
+                       PROCEDURE = pair);           
+ 
+   COMMENT ON EXTENSION pair IS 'Simple Key Value Text Type';
+ $$;
+ -- that's ok
+ ALTER TEMPLATE FOR EXTENSION pair SET DEFAULT VERSION '1.1';
+ -- that will install 1.0 then run the 1.0 -- 1.1 update script
+ CREATE EXTENSION pair;
+ \dx pair
+      List of installed extensions
+  Name | Version | Schema | Description 
+ ------+---------+--------+-------------
+  pair | 1.1     | public | 
+ (1 row)
+ 
+ DROP EXTENSION pair;
+ -- now that should install from the extension from the 1.3 template, even if
+ -- we have a default_major_version pointing to 1.0, because we actually have
+ -- a 1.3 create script.
+ CREATE EXTENSION pair VERSION '1.3';
+ \dx pair
+              List of installed extensions
+  Name | Version | Schema |        Description         
+ ------+---------+--------+----------------------------
+  pair | 1.3     | public | Simple Key Value Text Type
+ (1 row)
+ 
+ DROP EXTENSION pair;
+ -- and now let's ask for 1.3 by default while still leaving the
+ -- default_major_version at 1.0, so that it's possible to directly install
+ -- 1.2 if needed.
+ ALTER TEMPLATE FOR EXTENSION pair SET DEFAULT VERSION '1.3';
+ CREATE EXTENSION pair;
+ \dx pair
+              List of installed extensions
+  Name | Version | Schema |        Description         
+ ------+---------+--------+----------------------------
+  pair | 1.3     | public | Simple Key Value Text Type
+ (1 row)
+ 
+ DROP EXTENSION pair;
+ CREATE EXTENSION pair VERSION '1.2';
+ \dx pair
+             List of installed extensions
+  Name | Version | Schema |        Description        
+ ------+---------+--------+---------------------------
+  pair | 1.2     | public | A Toy Key Value Text Type
+ (1 row)
+ 
+ DROP EXTENSION pair;
+ -- test owner change
+ CREATE ROLE regression_bob;
+ ALTER TEMPLATE FOR EXTENSION pair OWNER TO regression_bob;
+ select ctlname, rolname
+   from pg_extension_control c join pg_roles r on r.oid = c.ctlowner;
+  ctlname |    rolname     
+ ---------+----------------
+  pair    | regression_bob
+  pair    | regression_bob
+  pair    | regression_bob
+  pair    | regression_bob
+ (4 rows)
+ 
+ -- test renaming
+ ALTER TEMPLATE FOR EXTENSION pair RENAME TO keyval;
+ -- cleanup
+ DROP TEMPLATE FOR EXTENSION keyval FROM '1.1' TO '1.2';
+ DROP TEMPLATE FOR EXTENSION keyval FROM '1.0' TO '1.1';
+ DROP TEMPLATE FOR EXTENSION keyval VERSION '1.0';
+ DROP TEMPLATE FOR EXTENSION keyval VERSION '1.3';
+ DROP ROLE regression_bob;
*** a/src/test/regress/expected/sanity_check.out
--- b/src/test/regress/expected/sanity_check.out
***************
*** 104,109 **** SELECT relname, relhasindex
--- 104,112 ----
   pg_enum                 | t
   pg_event_trigger        | t
   pg_extension            | t
+  pg_extension_control    | t
+  pg_extension_template   | t
+  pg_extension_uptmpl     | t
   pg_foreign_data_wrapper | t
   pg_foreign_server       | t
   pg_foreign_table        | t
***************
*** 166,172 **** SELECT relname, relhasindex
   timetz_tbl              | f
   tinterval_tbl           | f
   varchar_tbl             | f
! (155 rows)
  
  --
  -- another sanity check: every system catalog that has OIDs should have
--- 169,175 ----
   timetz_tbl              | f
   tinterval_tbl           | f
   varchar_tbl             | f
! (158 rows)
  
  --
  -- another sanity check: every system catalog that has OIDs should have
*** /dev/null
--- b/src/test/regress/sql/extension.sql
***************
*** 0 ****
--- 1,171 ----
+ -- first create some templates
+ CREATE TEMPLATE
+    FOR EXTENSION pair DEFAULT VERSION '1.0'
+   WITH (superuser, norelocatable, schema public)
+ AS $$
+   CREATE TYPE pair AS ( k text, v text );
+   
+   CREATE OR REPLACE FUNCTION pair(anyelement, text)
+   RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair';
+   
+   CREATE OR REPLACE FUNCTION pair(text, anyelement)
+   RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair';
+   
+   CREATE OR REPLACE FUNCTION pair(anyelement, anyelement)
+   RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair';
+   
+   CREATE OR REPLACE FUNCTION pair(text, text)
+   RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair;';      
+ $$;
+ 
+ -- we want to test alter extension update
+ CREATE TEMPLATE
+    FOR EXTENSION pair FROM '1.0' TO '1.1'
+   WITH (superuser, norelocatable, schema public)
+ AS $$
+   CREATE OPERATOR ~> (LEFTARG = text,
+                       RIGHTARG = anyelement,
+                       PROCEDURE = pair);
+                       
+   CREATE OPERATOR ~> (LEFTARG = anyelement,
+                       RIGHTARG = text,
+                       PROCEDURE = pair);
+ 
+   CREATE OPERATOR ~> (LEFTARG = anyelement,
+                       RIGHTARG = anyelement,
+                       PROCEDURE = pair);
+                       
+   CREATE OPERATOR ~> (LEFTARG = text,
+                       RIGHTARG = text,
+                       PROCEDURE = pair);           
+ $$;
+ 
+ -- and we want to test update with a cycle
+ CREATE TEMPLATE
+    FOR EXTENSION pair FROM '1.1' TO '1.2'
+ AS
+  $$
+   COMMENT ON EXTENSION pair IS 'Simple Key Value Text Type';
+ $$;
+ 
+ -- test some ALTER commands
+ 
+ -- ok
+ ALTER TEMPLATE FOR EXTENSION pair VERSION '1.0' WITH (relocatable);
+ 
+ -- we don't have a version 1.3 known yet
+ ALTER TEMPLATE FOR EXTENSION pair VERSION '1.3' WITH (relocatable);
+ 
+ -- you can't set the default on an upgrade script, only an extension's version
+ ALTER TEMPLATE FOR EXTENSION pair FROM '1.0' TO '1.1' SET DEFAULT;
+ 
+ -- you can't set control properties on an upgrade script, only an
+ -- extension's version
+ ALTER TEMPLATE FOR EXTENSION pair FROM '1.0' TO '1.1' WITH (relocatable);
+ 
+ -- try to set the default full version to an unknown extension version
+ ALTER TEMPLATE FOR EXTENSION pair SET DEFAULT FULL VERSION '1.1';
+ 
+ -- now set it to the current one already, should silently do nothing
+ ALTER TEMPLATE FOR EXTENSION pair SET DEFAULT FULL VERSION '1.0';
+ 
+ -- you can actually change the script used to update, though
+ ALTER TEMPLATE FOR EXTENSION pair FROM '1.1' TO '1.2'
+ AS $$
+   COMMENT ON EXTENSION pair IS 'A Toy Key Value Text Type';
+ $$;
+ 
+ CREATE EXTENSION pair;
+ 
+ \dx pair
+ \dx+ pair
+ 
+ ALTER EXTENSION pair UPDATE TO '1.2';
+ 
+ \dx+ pair
+ 
+ DROP EXTENSION pair;
+ 
+ -- test with another full version that's not the default
+ CREATE TEMPLATE
+    FOR EXTENSION pair VERSION '1.3'
+   WITH (superuser, norelocatable, schema public)
+ AS $$
+   CREATE TYPE pair AS ( k text, v text );
+   
+   CREATE OR REPLACE FUNCTION pair(anyelement, text)
+   RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair';
+   
+   CREATE OR REPLACE FUNCTION pair(text, anyelement)
+   RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair';
+   
+   CREATE OR REPLACE FUNCTION pair(anyelement, anyelement)
+   RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair';
+   
+   CREATE OR REPLACE FUNCTION pair(text, text)
+   RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair;';      
+ 
+   CREATE OPERATOR ~> (LEFTARG = text,
+                       RIGHTARG = anyelement,
+                       PROCEDURE = pair);
+                       
+   CREATE OPERATOR ~> (LEFTARG = anyelement,
+                       RIGHTARG = text,
+                       PROCEDURE = pair);
+ 
+   CREATE OPERATOR ~> (LEFTARG = anyelement,
+                       RIGHTARG = anyelement,
+                       PROCEDURE = pair);
+                       
+   CREATE OPERATOR ~> (LEFTARG = text,
+                       RIGHTARG = text,
+                       PROCEDURE = pair);           
+ 
+   COMMENT ON EXTENSION pair IS 'Simple Key Value Text Type';
+ $$;
+ 
+ -- that's ok
+ ALTER TEMPLATE FOR EXTENSION pair SET DEFAULT VERSION '1.1';
+ 
+ -- that will install 1.0 then run the 1.0 -- 1.1 update script
+ CREATE EXTENSION pair;
+ \dx pair
+ DROP EXTENSION pair;
+ 
+ -- now that should install from the extension from the 1.3 template, even if
+ -- we have a default_major_version pointing to 1.0, because we actually have
+ -- a 1.3 create script.
+ CREATE EXTENSION pair VERSION '1.3';
+ \dx pair
+ DROP EXTENSION pair;
+ 
+ -- and now let's ask for 1.3 by default while still leaving the
+ -- default_major_version at 1.0, so that it's possible to directly install
+ -- 1.2 if needed.
+ ALTER TEMPLATE FOR EXTENSION pair SET DEFAULT VERSION '1.3';
+ 
+ CREATE EXTENSION pair;
+ \dx pair
+ DROP EXTENSION pair;
+ 
+ CREATE EXTENSION pair VERSION '1.2';
+ \dx pair
+ DROP EXTENSION pair;
+ 
+ -- test owner change
+ CREATE ROLE regression_bob;
+ 
+ ALTER TEMPLATE FOR EXTENSION pair OWNER TO regression_bob;
+ 
+ select ctlname, rolname
+   from pg_extension_control c join pg_roles r on r.oid = c.ctlowner;
+ 
+ -- test renaming
+ ALTER TEMPLATE FOR EXTENSION pair RENAME TO keyval;
+ 
+ -- cleanup
+ DROP TEMPLATE FOR EXTENSION keyval FROM '1.1' TO '1.2';
+ DROP TEMPLATE FOR EXTENSION keyval FROM '1.0' TO '1.1';
+ DROP TEMPLATE FOR EXTENSION keyval VERSION '1.0';
+ DROP TEMPLATE FOR EXTENSION keyval VERSION '1.3';
+ DROP ROLE regression_bob;
