*** a/contrib/citext/Makefile
--- b/contrib/citext/Makefile
***************
*** 1,6 ****
--- 1,7 ----
  # contrib/citext/Makefile
  
  MODULES = citext
+ DATA = citext.upgrade.sql
  DATA_built = citext.sql
  REGRESS = citext
  
*** a/contrib/citext/citext.control
--- b/contrib/citext/citext.control
***************
*** 2,4 ****
--- 2,5 ----
  comment = 'case-insensitive character string type'
  version = '9.1devel'
  relocatable = true
+ upgrade_from_null = 'null => citext.upgrade.sql'
*** /dev/null
--- b/contrib/citext/citext.upgrade.sql
***************
*** 0 ****
--- 1,74 ----
+ /* contrib/citext/citext.upgrade.sql */
+ 
+ --
+ --	Upgrade the citext extension from before there was extension support
+ --
+ 
+ alter type @extschema@.citext set extension citext;
+ alter function @extschema@.citextin(cstring) set extension citext;
+ alter function @extschema@.citextout(@extschema@.citext) set extension citext;
+ alter function @extschema@.citextrecv(internal) set extension citext;
+ alter function @extschema@.citextsend(@extschema@.citext) set extension citext;
+ alter type @extschema@._citext set extension citext;
+ alter function @extschema@.citext(character) set extension citext;
+ alter function @extschema@.citext(boolean) set extension citext;
+ alter function @extschema@.citext(inet) set extension citext;
+ alter function @extschema@.citext_eq(@extschema@.citext,@extschema@.citext) set extension citext;
+ alter function @extschema@.citext_ne(@extschema@.citext,@extschema@.citext) set extension citext;
+ alter function @extschema@.citext_lt(@extschema@.citext,@extschema@.citext) set extension citext;
+ alter function @extschema@.citext_le(@extschema@.citext,@extschema@.citext) set extension citext;
+ alter function @extschema@.citext_gt(@extschema@.citext,@extschema@.citext) set extension citext;
+ alter function @extschema@.citext_ge(@extschema@.citext,@extschema@.citext) set extension citext;
+ alter operator @extschema@.=(@extschema@.citext,@extschema@.citext) set extension citext;
+ alter operator @extschema@.<>(@extschema@.citext,@extschema@.citext) set extension citext;
+ alter operator @extschema@.<(@extschema@.citext,@extschema@.citext) set extension citext;
+ alter operator @extschema@.<=(@extschema@.citext,@extschema@.citext) set extension citext;
+ alter operator @extschema@.>=(@extschema@.citext,@extschema@.citext) set extension citext;
+ alter operator @extschema@.>(@extschema@.citext,@extschema@.citext) set extension citext;
+ alter function @extschema@.citext_cmp(@extschema@.citext,@extschema@.citext) set extension citext;
+ alter function @extschema@.citext_hash(@extschema@.citext) set extension citext;
+ alter operator family @extschema@.citext_ops using btree set extension citext;
+ alter operator class @extschema@.citext_ops using btree set extension citext;
+ alter operator family @extschema@.citext_ops using hash set extension citext;
+ alter operator class @extschema@.citext_ops using hash set extension citext;
+ alter function @extschema@.citext_smaller(@extschema@.citext,@extschema@.citext) set extension citext;
+ alter function @extschema@.citext_larger(@extschema@.citext,@extschema@.citext) set extension citext;
+ alter function @extschema@.min(@extschema@.citext) set extension citext;
+ alter function @extschema@.max(@extschema@.citext) set extension citext;
+ alter function @extschema@.texticlike(@extschema@.citext,@extschema@.citext) set extension citext;
+ alter function @extschema@.texticnlike(@extschema@.citext,@extschema@.citext) set extension citext;
+ alter function @extschema@.texticregexeq(@extschema@.citext,@extschema@.citext) set extension citext;
+ alter function @extschema@.texticregexne(@extschema@.citext,@extschema@.citext) set extension citext;
+ alter operator @extschema@.~(@extschema@.citext,@extschema@.citext) set extension citext;
+ alter operator @extschema@.~*(@extschema@.citext,@extschema@.citext) set extension citext;
+ alter operator @extschema@.!~(@extschema@.citext,@extschema@.citext) set extension citext;
+ alter operator @extschema@.!~*(@extschema@.citext,@extschema@.citext) set extension citext;
+ alter operator @extschema@.~~(@extschema@.citext,@extschema@.citext) set extension citext;
+ alter operator @extschema@.~~*(@extschema@.citext,@extschema@.citext) set extension citext;
+ alter operator @extschema@.!~~(@extschema@.citext,@extschema@.citext) set extension citext;
+ alter operator @extschema@.!~~*(@extschema@.citext,@extschema@.citext) set extension citext;
+ alter function @extschema@.texticlike(@extschema@.citext,text) set extension citext;
+ alter function @extschema@.texticnlike(@extschema@.citext,text) set extension citext;
+ alter function @extschema@.texticregexeq(@extschema@.citext,text) set extension citext;
+ alter function @extschema@.texticregexne(@extschema@.citext,text) set extension citext;
+ alter operator @extschema@.~(@extschema@.citext,text) set extension citext;
+ alter operator @extschema@.~*(@extschema@.citext,text) set extension citext;
+ alter operator @extschema@.!~(@extschema@.citext,text) set extension citext;
+ alter operator @extschema@.!~*(@extschema@.citext,text) set extension citext;
+ alter operator @extschema@.~~(@extschema@.citext,text) set extension citext;
+ alter operator @extschema@.~~*(@extschema@.citext,text) set extension citext;
+ alter operator @extschema@.!~~(@extschema@.citext,text) set extension citext;
+ alter operator @extschema@.!~~*(@extschema@.citext,text) set extension citext;
+ alter function @extschema@.regexp_matches(@extschema@.citext,@extschema@.citext) set extension citext;
+ alter function @extschema@.regexp_matches(@extschema@.citext,@extschema@.citext,text) set extension citext;
+ alter function @extschema@.regexp_replace(@extschema@.citext,@extschema@.citext,text) set extension citext;
+ alter function @extschema@.regexp_replace(@extschema@.citext,@extschema@.citext,text,text) set extension citext;
+ alter function @extschema@.regexp_split_to_array(@extschema@.citext,@extschema@.citext) set extension citext;
+ alter function @extschema@.regexp_split_to_array(@extschema@.citext,@extschema@.citext,text) set extension citext;
+ alter function @extschema@.regexp_split_to_table(@extschema@.citext,@extschema@.citext) set extension citext;
+ alter function @extschema@.regexp_split_to_table(@extschema@.citext,@extschema@.citext,text) set extension citext;
+ alter function @extschema@.strpos(@extschema@.citext,@extschema@.citext) set extension citext;
+ alter function @extschema@.replace(@extschema@.citext,@extschema@.citext,@extschema@.citext) set extension citext;
+ alter function @extschema@.split_part(@extschema@.citext,@extschema@.citext,integer) set extension citext;
+ alter function @extschema@.translate(@extschema@.citext,@extschema@.citext,text) set extension citext;
+ 
*** a/contrib/lo/Makefile
--- b/contrib/lo/Makefile
***************
*** 1,6 ****
--- 1,7 ----
  # contrib/lo/Makefile
  
  MODULES = lo
+ DATA = lo.upgrade.sql
  DATA_built = lo.sql
  
  EXTENSION = $(MODULES)
*** a/contrib/lo/lo.control
--- b/contrib/lo/lo.control
***************
*** 2,4 ****
--- 2,5 ----
  comment = 'managing Large Objects'
  version = '9.1devel'
  relocatable = true
+ upgrade_from_null = 'null => lo.upgrade.sql'
*** /dev/null
--- b/contrib/lo/lo.upgrade.sql
***************
*** 0 ****
--- 1,9 ----
+ /* contrib/lo/lo.upgrade.sql */
+ 
+ --
+ --	Upgrade the lo extension from before there was extension support
+ --
+ 
+ alter domain @extschema@.lo set extension lo;
+ alter function @extschema@.lo_oid(lo) set extension lo;
+ alter function @extschema@.lo_manage() set extension lo;
*** a/contrib/pg_stat_statements/Makefile
--- b/contrib/pg_stat_statements/Makefile
***************
*** 1,6 ****
--- 1,7 ----
  # contrib/pg_stat_statements/Makefile
  
  MODULE_big = pg_stat_statements
+ DATA = pg_stat_statements.upgrade.sql
  DATA_built = pg_stat_statements.sql
  OBJS = pg_stat_statements.o
  
*** a/contrib/pg_stat_statements/pg_stat_statements.control
--- b/contrib/pg_stat_statements/pg_stat_statements.control
***************
*** 2,4 ****
--- 2,5 ----
  comment = 'tracking execution statistics of all SQL statements executed'
  version = '9.1devel'
  relocatable = true
+ upgrade_from_null = 'null => pg_stat_statements.upgrade.sql'
*** /dev/null
--- b/contrib/pg_stat_statements/pg_stat_statements.upgrade.sql
***************
*** 0 ****
--- 1,6 ----
+ /* contrib/pg_stat_statements/pg_stat_statements.upgrade.sql */
+ 
+ alter function @extschema@.pg_stat_statements_reset() set extension pg_stat_statements;
+ alter function @extschema@.pg_stat_statements() set extension pg_stat_statements;
+ alter view @extschema@.pg_stat_statements set extension pg_stat_statements;
+ alter type @extschema@.pg_stat_statements set extension pg_stat_statements;
*** a/contrib/unaccent/Makefile
--- b/contrib/unaccent/Makefile
***************
*** 3,8 ****
--- 3,9 ----
  MODULE_big = unaccent
  OBJS = unaccent.o
  
+ DATA = unaccent.upgrade.sql
  DATA_built = unaccent.sql
  DATA_TSEARCH = unaccent.rules
  REGRESS = unaccent
*** a/contrib/unaccent/unaccent.control
--- b/contrib/unaccent/unaccent.control
***************
*** 2,4 ****
--- 2,5 ----
  comment = 'text search dictionary that removes accents'
  version = '9.1devel'
  relocatable = true
+ upgrade_from_null = 'null => unaccent.upgrade.sql'
*** /dev/null
--- b/contrib/unaccent/unaccent.upgrade.sql
***************
*** 0 ****
--- 1,8 ----
+ /* contrib/unaccent/unaccent.upgrade.sql */
+ 
+ alter function @extschema@.unaccent(regdictionary,text) set extension unaccent;
+ alter function @extschema@.unaccent(text) set extension unaccent;
+ alter function @extschema@.unaccent_init(internal) set extension unaccent;
+ alter function @extschema@.unaccent_lexize(internal,internal,internal,internal) set extension unaccent;
+ alter text search template unaccent set extension unaccent;
+ alter text search dictionary unaccent set extension unaccent;
*** a/src/backend/commands/alter.c
--- b/src/backend/commands/alter.c
***************
*** 244,249 **** ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt)
--- 244,318 ----
  }
  
  /*
+  * Executes an ALTER OBJECT / SET EXTENSION statement.  Based on the object
+  * type, the function appropriate to that type is executed.
+  */
+ void
+ ExecAlterObjectExtensionStmt(AlterObjectExtensionStmt *stmt)
+ {
+ 	switch (stmt->objectType)
+ 	{
+ 		case OBJECT_AGGREGATE:
+ 			AlterFunctionExtension(stmt->object, stmt->objarg, true,
+ 								   stmt->extname);
+ 			break;
+ 
+ 		case OBJECT_CONVERSION:
+ 			AlterConversionExtension(stmt->object, stmt->extname);
+ 			break;
+ 
+ 		case OBJECT_FUNCTION:
+ 			AlterFunctionExtension(stmt->object, stmt->objarg, false,
+ 								   stmt->extname);
+ 			break;
+ 
+ 		case OBJECT_OPERATOR:
+ 			AlterOperatorExtension(stmt->object, stmt->objarg, stmt->extname);
+ 			break;
+ 
+ 		case OBJECT_OPCLASS:
+ 			AlterOpClassExtension(stmt->object, stmt->objarg, stmt->extname);
+ 			break;
+ 
+ 		case OBJECT_OPFAMILY:
+ 			AlterOpFamilyExtension(stmt->object, stmt->objarg, stmt->extname);
+ 			break;
+ 
+ 		case OBJECT_SEQUENCE:
+ 		case OBJECT_TABLE:
+ 		case OBJECT_VIEW:
+ 			CheckRelationOwnership(stmt->relation, true);
+ 			AlterTableExtension(stmt->relation, stmt->extname);
+ 			break;
+ 
+ 		case OBJECT_TSPARSER:
+ 			AlterTSParserExtension(stmt->object, stmt->extname);
+ 			break;
+ 
+ 		case OBJECT_TSDICTIONARY:
+ 			AlterTSDictionaryExtension(stmt->object, stmt->extname);
+ 			break;
+ 
+ 		case OBJECT_TSTEMPLATE:
+ 			AlterTSTemplateExtension(stmt->object, stmt->extname);
+ 			break;
+ 
+ 		case OBJECT_TSCONFIGURATION:
+ 			AlterTSConfigurationExtension(stmt->object, stmt->extname);
+ 			break;
+ 
+ 		case OBJECT_TYPE:
+ 		case OBJECT_DOMAIN:
+ 			AlterTypeExtension(stmt->object, stmt->extname);
+ 			break;
+ 
+ 		default:
+ 			elog(ERROR, "unrecognized AlterObjectExtensionStmt type: %d",
+ 				 (int) stmt->objectType);
+ 	}
+ }
+ 
+ /*
   * Generic function to change the namespace of a given object, for simple
   * cases (won't work for tables or functions, objects which have more than 2
   * key-attributes to use when searching for their syscache entries --- we
*** a/src/backend/commands/conversioncmds.c
--- b/src/backend/commands/conversioncmds.c
***************
*** 23,28 ****
--- 23,29 ----
  #include "catalog/pg_type.h"
  #include "commands/alter.h"
  #include "commands/conversioncmds.h"
+ #include "commands/extension.h"
  #include "mb/pg_wchar.h"
  #include "miscadmin.h"
  #include "parser/parse_func.h"
***************
*** 377,379 **** AlterConversionNamespace_oid(Oid convOid, Oid newNspOid)
--- 378,392 ----
  	heap_close(rel, NoLock);
  	return oldNspOid;
  }
+ 
+ /*
+  * Execute ALTER CONVERSION SET EXTENSION
+  */
+ void
+ AlterConversionExtension(List *name, const char *extname)
+ {
+ 	Oid			convOid;
+ 
+ 	convOid = get_conversion_oid(name, false);
+ 	AlterObjectExtension(extname, ConversionRelationId, convOid, 0);
+ }
*** a/src/backend/commands/extension.c
--- b/src/backend/commands/extension.c
***************
*** 55,60 ****
--- 55,61 ----
  #include "utils/fmgroids.h"
  #include "utils/genfile.h"
  #include "utils/guc.h"
+ #include "utils/lsyscache.h"
  #include "utils/memutils.h"
  #include "utils/rel.h"
  #include "utils/syscache.h"
***************
*** 74,79 **** ObjectAddress CreateExtensionAddress;
--- 75,84 ----
  bool create_extension = false;
  bool create_extension_with_user_data = true;
  
+ static void
+ execute_extension_script(ExtensionControlFile *control,
+ 						 const char *schema, bool create, bool user_data);
+ 
  /*
   * Utility functions to handle extension related paths
   */
***************
*** 168,182 **** get_extension_absolute_path(const char *filename)
   * reading it is only allowed to superuser, so we don't even try to minimize
   * memory allocation risks here.
   *
!  * Also note that only happens in CREATE EXTENSION and pg_extensions().
   *
!  * To support listing available extensions, the parameter init_gucs can be
!  * set to false to inhibit any GUC processing.
   */
  static ExtensionControlFile *
  parse_extension_control_file(const char *extname,
  							 const char *filename,
! 							 bool init_gucs)
  {
  	FILE	   *file;
  	bool		saw_relocatable = false;
--- 173,190 ----
   * reading it is only allowed to superuser, so we don't even try to minimize
   * memory allocation risks here.
   *
!  * Also note that only happens in CREATE EXTENSION, ALTER EXTENSION UPGRADE
!  * and pg_extensions() (used by psql \dx command).
   *
!  * When doing an upgrade we have to know from which version we're doing
!  * it. This version can be NULL in case of upgrading from a "dummy"
!  * extension.
   */
  static ExtensionControlFile *
  parse_extension_control_file(const char *extname,
  							 const char *filename,
! 							 bool upgrade,
! 							 const char *version)
  {
  	FILE	   *file;
  	bool		saw_relocatable = false;
***************
*** 194,200 **** parse_extension_control_file(const char *extname,
  	 * name, which ain't parsed
  	 */
  	control->name = pstrdup(extname);
! 	control->script = control->version = control->comment = NULL;
  	control->encoding = -1;
  
  	/*
--- 202,209 ----
  	 * name, which ain't parsed
  	 */
  	control->name = pstrdup(extname);
! 	control->script =
! 		control->version = control->comment = control->upgrade = NULL;
  	control->encoding = -1;
  
  	/*
***************
*** 243,257 **** parse_extension_control_file(const char *extname,
  						 errmsg("%s is not a valid encoding name in '%s'",
  								item->value, filename)));
  		}
  		else
  			ereport(ERROR,
  					(errmsg("Unsupported parameter '%s' in file: %s",
  							item->name, filename)));
  	}
  
! 	if (control->name == NULL)
  		ereport(ERROR,
! 				(errmsg("Missing a name parameter in file: %s", filename)));
  
  	/*
  	 * Handle default values
--- 252,319 ----
  						 errmsg("%s is not a valid encoding name in '%s'",
  								item->value, filename)));
  		}
+ 		/*
+ 		 * upgrade settings are split in any number of variable, all
+ 		 * beginning with "upgrade_from_" and with a value of the form:
+ 		 *
+ 		 * upgrade_from_null = 'null => lo.upgrade.sql'
+ 		 * upgrade_from_9_1  = '9.1.* => lo.upgrade.1.sql'
+ 		 *
+ 		 * We simply ignore the parameters when upgrade is false.
+ 		 *
+ 		 * strlen("upgrade_from_") = 13
+ 		 */
+ 		else if (strncmp(item->name, "upgrade_from_", 13) == 0)
+ 		{
+ 			/* first match win */
+ 			if (upgrade && control->upgrade == NULL)
+ 			{
+ 				char *sep = "=>";
+ 				char *re = text_to_cstring(
+ 					DatumGetTextPP(
+ 						DirectFunctionCall1(
+ 							btrim1,
+ 							DirectFunctionCall3(
+ 								split_text,
+ 								PointerGetDatum(cstring_to_text(item->value)),
+ 								PointerGetDatum(cstring_to_text(sep)), 1))));
+ 
+ 				char *filename = text_to_cstring(
+ 					DatumGetTextPP(
+ 						DirectFunctionCall1(
+ 							btrim1,
+ 							DirectFunctionCall3(
+ 								split_text,
+ 								PointerGetDatum(cstring_to_text(item->value)),
+ 								PointerGetDatum(cstring_to_text(sep)), 2))));
+ 
+ 				/* re is "null" to match current version being null */
+ 				if (version == NULL)
+ 				{
+ 					if (strcmp(re, "null") == 0)
+ 						control->upgrade = pstrdup(filename);
+ 				}
+ 				/* regexp match: re ~ version */
+ 				else if (DatumGetBool(
+ 							 DirectFunctionCall2(
+ 								 textregexeq,
+ 								 PointerGetDatum(cstring_to_text(version)),
+ 								 PointerGetDatum(cstring_to_text(re)))))
+ 				{
+ 					control->upgrade = pstrdup(filename);
+ 				}
+ 			}
+ 		}
  		else
  			ereport(ERROR,
  					(errmsg("Unsupported parameter '%s' in file: %s",
  							item->name, filename)));
  	}
  
! 	if (upgrade && control->upgrade == NULL)
  		ereport(ERROR,
! 				(errmsg("No matching regexp to upgrade extension \"%s\" from version \"%s\"",
! 						control->name, version)));
  
  	/*
  	 * Handle default values
***************
*** 282,294 **** parse_extension_control_file(const char *extname,
   * We're given an extension name, parse the {extname}.control file.
   */
  static ExtensionControlFile *
! read_extension_control_file(const char *extname, bool init_gucs)
  {
      char *filename = get_extension_control_filename(extname);
  
  	if (access(filename, R_OK) == 0)
  	{
! 		return parse_extension_control_file(extname, filename, true);
  	}
  
  	ereport(ERROR,
--- 344,358 ----
   * We're given an extension name, parse the {extname}.control file.
   */
  static ExtensionControlFile *
! read_extension_control_file(const char *extname,
! 							bool upgrade, const char *version)
  {
      char *filename = get_extension_control_filename(extname);
  
  	if (access(filename, R_OK) == 0)
  	{
! 		return parse_extension_control_file(extname, filename,
! 											upgrade, version);
  	}
  
  	ereport(ERROR,
***************
*** 303,312 **** read_extension_control_file(const char *extname, bool init_gucs)
   * Read the SQL script into a string, and care for its encoding
   */
  static char *
! read_extension_script_file(const ExtensionControlFile *control,
! 						   const char *encoding_name)
  {
! 	char *filename = get_extension_absolute_path(control->script);
  	Datum script = CStringGetTextDatum(filename);
  	Datum raw = DirectFunctionCall1(pg_read_binary_file_all, script);
  	int         src_encoding;
--- 367,376 ----
   * Read the SQL script into a string, and care for its encoding
   */
  static char *
! read_extension_script_file(const ExtensionControlFile *control, bool create)
  {
! 	char *scriptname = create ? control->script : control->upgrade;
! 	char *filename = get_extension_absolute_path(scriptname);
  	Datum script = CStringGetTextDatum(filename);
  	Datum raw = DirectFunctionCall1(pg_read_binary_file_all, script);
  	int         src_encoding;
***************
*** 317,332 **** read_extension_script_file(const ExtensionControlFile *control,
  	int         len;
  
  	/* use database encoding if not given */
! 	if (encoding_name == NULL)
  		src_encoding = dest_encoding;
  	else
! 		src_encoding = pg_char_to_encoding(encoding_name);
  
  	if (src_encoding < 0)
  		ereport(ERROR,
  				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  				 errmsg("invalid source encoding name \"%s\"",
! 						encoding_name)));
  
  	content = (bytea *)DatumGetPointer(raw);
  
--- 381,396 ----
  	int         len;
  
  	/* use database encoding if not given */
! 	if (control->encoding == -1)
  		src_encoding = dest_encoding;
  	else
! 		src_encoding = control->encoding;
  
  	if (src_encoding < 0)
  		ereport(ERROR,
  				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  				 errmsg("invalid source encoding name \"%s\"",
! 						pg_encoding_to_char(control->encoding))));
  
  	content = (bytea *)DatumGetPointer(raw);
  
***************
*** 372,377 **** execute_sql_string(const char *sql)
--- 436,586 ----
  }
  
  /*
+  * Execute given script to install or upgrade an extension
+  *
+  * Caller must have prepared CreateExtensionAddress.
+  */
+ static void
+ execute_extension_script(ExtensionControlFile *control,
+ 						 const char *schema, bool create, bool user_data)
+ {
+ 	char       *script = create ? control->script : control->upgrade;
+ 	char       *filename = get_extension_absolute_path(script);
+ 	char       *old_cmsgs = NULL, *old_lmsgs = NULL; /* silence compiler */
+ 	bool        reset_cmsgs = false, reset_lmsgs = false;
+ 
+ 	/*
+ 	 * Set create_extension and create_extension_with_user_data so that the
+ 	 * function pg_extension_with_user_data() returns the right value.
+ 	 */
+ 	create_extension = true;
+ 	create_extension_with_user_data = user_data;
+ 
+ 	elog(DEBUG1,
+ 		 "%s extension '%s' from '%s', in schema %s, %s user data",
+ 		 create ? "Installing" : "Upgrading",
+ 		 control->name,
+ 		 filename,
+ 		 schema ? schema : "public",
+ 		 create_extension_with_user_data ? "with" : "without");
+ 
+ 	/*
+ 	 * Force the min message settings here, because SPI will dump the
+ 	 * query_string into the messages and in this case, that's a full
+ 	 * extension script. At the time of this writing, contrib/isn/isn.sql.in
+ 	 * is already 3197 lines and will issue lots of CREATE TYPE shell
+ 	 * notices.
+ 	 *
+ 	 * We want to avoid dumping the full script for each of those.
+ 	 */
+ 	reset_cmsgs = client_min_messages < WARNING;
+ 	reset_lmsgs = log_min_messages < WARNING;
+ 
+ 	if (reset_cmsgs)
+ 	{
+ 		old_cmsgs = pstrdup((char *)GetConfigOption("client_min_messages", false));
+ 		SetConfigOption("client_min_messages", "warning", PGC_SUSET, PGC_S_SESSION);
+ 	}
+ 	if (reset_lmsgs)
+ 	{
+ 		old_lmsgs = pstrdup((char *)GetConfigOption("log_min_messages", false));
+ 		SetConfigOption("log_min_messages", "warning", PGC_SUSET, PGC_S_SESSION);
+ 	}
+ 
+ 	/*
+ 	 * On failure, ensure that create_extension does not remain set.
+ 	 */
+ 	PG_TRY();
+ 	{
+ 		char *sql = read_extension_script_file(control, create);
+ 
+ 		elog(DEBUG1, "Execute script \"%s\"", filename);
+ 
+ 		/*
+ 		 * An extension that is relocatable does not need any replacement,
+ 		 * we will ALTER EXTENSION SET SCHEMA just after installing it. See
+ 		 * below for details.
+ 		 *
+ 		 * An extension that is not relocatable need to offer a mechanism
+ 		 * for the users to be able to choose where to intall it, that's
+ 		 * using the placeholder @extschema@.
+ 		 *
+ 		 * At upgrade time, it's highly possible that the script will need
+ 		 * to ALTER already existing objects, so we only offer support for
+ 		 * the placeholder @extschema@.
+ 		 */
+ 		if (create && control->relocatable)
+ 		{
+ 			List *search_path = fetch_search_path(false);
+ 			Oid   first_schema, target_schema;
+ 
+ 			if (!execute_sql_string(sql))
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_DATA_EXCEPTION),
+ 						 errmsg("could not execute sql file: '%s'", filename)));
+ 
+ 			/*
+ 			 * CREATE EXTENSION foo WITH SCHEMA bar;
+ 			 *
+ 			 * The extension is relocatable, so we run its install script
+ 			 * and it has to install all objects into the same schema. Then
+ 			 * we relocate all the objects to the target SCHEMA bar,
+ 			 * ERRORing out if some objects are not in the same schema than
+ 			 * others, that's doing:
+ 			 *
+ 			 * ALTER EXTENSION foo SET SCHEMA bar;
+ 			 *
+ 			 * We skip this step if the target schema happens to be the
+ 			 * first schema of the current search_path.
+ 			 */
+ 			first_schema = linitial_oid(search_path);
+ 			target_schema = LookupCreationNamespace(schema);
+ 			list_free(search_path);
+ 
+ 			if (first_schema != target_schema)
+ 				AlterExtensionNamespace_oid(CreateExtensionAddress.objectId,
+ 											target_schema);
+ 		}
+ 		else
+ 		{
+ 			/*
+ 			 * Prepare the replacements we use in execute_sql_file(). Even if
+ 			 * the schema has not been specified by the user, we still have to
+ 			 * set @extschema@ to 'public' so that extension scripts can rely on
+ 			 * this placeholder.
+ 			 */
+ 			sql = text_to_cstring(
+ 				DatumGetTextPP(
+ 					DirectFunctionCall3(replace_text,
+ 										CStringGetTextDatum(sql),
+ 										CStringGetTextDatum("@extschema@"),
+ 										CStringGetTextDatum(schema))));
+ 
+ 			if (!execute_sql_string(sql))
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_DATA_EXCEPTION),
+ 						 errmsg("could not execute sql file: '%s'", filename)));
+ 		}
+ 	}
+ 	PG_CATCH();
+ 	{
+ 		create_extension = false;
+ 		PG_RE_THROW();
+ 	}
+ 	PG_END_TRY();
+ 
+ 	if (reset_cmsgs)
+ 		SetConfigOption("client_min_messages",
+ 						old_cmsgs, PGC_SUSET, PGC_S_SESSION);
+ 	if (reset_lmsgs)
+ 		SetConfigOption("log_min_messages",
+ 						old_lmsgs, PGC_SUSET, PGC_S_SESSION);
+ 
+ 	/* reset the current_extension dependency tracker */
+ 	create_extension = false;
+ }
+ 
+ /*
   * CREATE EXTENSION
   *
   * Only superusers can create an extension.
***************
*** 379,391 **** execute_sql_string(const char *sql)
  void
  CreateExtension(CreateExtensionStmt *stmt)
  {
- 
  	ListCell   *option;
  	DefElem    *d_user_data = NULL;
  	DefElem    *d_schema = NULL;
  	bool		user_data = true;
  	char       *schema = NULL;
- 	int			encoding = -1;
  
  	ExtensionControlFile *control;
  
--- 588,599 ----
  void
  CreateExtension(CreateExtensionStmt *stmt)
  {
  	ListCell   *option;
  	DefElem    *d_user_data = NULL;
  	DefElem    *d_schema = NULL;
+ 	DefElem    *d_sysid = NULL;
  	bool		user_data = true;
  	char       *schema = NULL;
  
  	ExtensionControlFile *control;
  
***************
*** 393,402 **** CreateExtension(CreateExtensionStmt *stmt)
  	Datum		values[Natts_pg_extension];
  	bool		nulls[Natts_pg_extension];
  	HeapTuple	tuple;
! 	Oid			extensionoid;
! 
! 	char       *old_cmsgs = NULL, *old_lmsgs = NULL; /* silence compiler */
! 	bool        reset_cmsgs = false, reset_lmsgs = false;
  
  	/* Must be super user */
  	if (!superuser())
--- 601,607 ----
  	Datum		values[Natts_pg_extension];
  	bool		nulls[Natts_pg_extension];
  	HeapTuple	tuple;
! 	Oid			extensionoid = InvalidOid;
  
  	/* Must be super user */
  	if (!superuser())
***************
*** 426,432 **** CreateExtension(CreateExtensionStmt *stmt)
  	 * apply to reading the script, so we have to read it before to know
  	 * which encoding to choose.
  	 */
! 	control = read_extension_control_file(stmt->extname, true);
  
  	/*
  	 * Read the statement option list
--- 631,637 ----
  	 * apply to reading the script, so we have to read it before to know
  	 * which encoding to choose.
  	 */
! 	control = read_extension_control_file(stmt->extname, false, NULL);
  
  	/*
  	 * Read the statement option list
***************
*** 438,444 **** CreateExtension(CreateExtensionStmt *stmt)
  
  		if (strcmp(defel->defname, "user_data") == 0)
  		{
! 			if (d_user_data)
  				ereport(ERROR,
  						(errcode(ERRCODE_SYNTAX_ERROR),
  						 errmsg("conflicting or redundant options")));
--- 643,649 ----
  
  		if (strcmp(defel->defname, "user_data") == 0)
  		{
! 			if (d_user_data || d_sysid)
  				ereport(ERROR,
  						(errcode(ERRCODE_SYNTAX_ERROR),
  						 errmsg("conflicting or redundant options")));
***************
*** 452,457 **** CreateExtension(CreateExtensionStmt *stmt)
--- 657,670 ----
  						 errmsg("conflicting or redundant options")));
  			d_schema = defel;
  		}
+ 		else if (strcmp(defel->defname, "sysid") == 0)
+ 		{
+ 			if (d_sysid || d_user_data)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						 errmsg("conflicting or redundant options")));
+ 			d_sysid = defel;
+ 		}
  	}
  
  	if (d_user_data && d_user_data->arg)
***************
*** 466,486 **** CreateExtension(CreateExtensionStmt *stmt)
  		 */
  		schema = pstrdup("public");
  
! 	if (control->encoding != -1)
! 	{
! 		/* The encoding has already been validated */
! 		encoding = control->encoding;
! 	}
! 	else
  	{
! 		/* default to utf8 encoding */
! 		encoding = PG_UTF8;
  	}
  
  	elog(DEBUG1, "CreateExtensionStmt: %s user data, schema = '%s', encoding = '%s'",
  		 user_data ? "with" : "without",
  		 schema,
! 		 pg_encoding_to_char(encoding));
  
  	/*
  	 * Insert tuple into pg_extension.
--- 679,701 ----
  		 */
  		schema = pstrdup("public");
  
! 	if (d_sysid && d_sysid->arg)
  	{
! 		extensionoid = (Oid) intVal(d_sysid->arg);
! 		if (extensionoid <= InvalidOid || extensionoid > OID_MAX)
! 			ereport(ERROR,
! 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! 					 errmsg("invalid Oid: %d", extensionoid)));
  	}
  
+ 	/* default to utf8 encoding */
+ 	if (control->encoding == -1)
+ 		control->encoding = PG_UTF8;
+ 
  	elog(DEBUG1, "CreateExtensionStmt: %s user data, schema = '%s', encoding = '%s'",
  		 user_data ? "with" : "without",
  		 schema,
! 		 pg_encoding_to_char(control->encoding));
  
  	/*
  	 * Insert tuple into pg_extension.
***************
*** 504,509 **** CreateExtension(CreateExtensionStmt *stmt)
--- 719,737 ----
  				 errmsg("extension \"%s\" already exists", stmt->extname)));
  	}
  
+ 	if (OidIsValid(extensionoid))
+ 	{
+ 		char *extname = get_extension_name(extensionoid);
+ 		elog(DEBUG1, "checking for extension with OID %u: %s", extensionoid, extname);
+ 
+ 		if( extname != NULL  )
+ 		{
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_DUPLICATE_OBJECT),
+ 					 errmsg("extension \"%s\" already exists", extname)));
+ 		}
+ 	}
+ 
  	memset(values, 0, sizeof(values));
  	MemSet(nulls, false, sizeof(nulls));
  
***************
*** 513,526 **** CreateExtension(CreateExtensionStmt *stmt)
  	values[Anum_pg_extension_relocatable - 1] =
  		BoolGetDatum(control->relocatable);
  
! 	if( control->version == NULL )
  		nulls[Anum_pg_extension_extversion - 1] = true;
  	else
  		values[Anum_pg_extension_extversion - 1] =
  			CStringGetTextDatum(control->version);
  
! 	tuple = heap_form_tuple(rel->rd_att, values, nulls);
  	extensionoid = simple_heap_insert(rel, tuple);
  	CatalogUpdateIndexes(rel, tuple);
  	heap_freetuple(tuple);
  	heap_close(rel, ExclusiveLock);
--- 741,766 ----
  	values[Anum_pg_extension_relocatable - 1] =
  		BoolGetDatum(control->relocatable);
  
! 	/*
! 	 * We force version to NULL when CREATE WRAPPER EXTENSION is used: then
! 	 * we UPGRADE from NULL */
! 	if( control->version == NULL || stmt->wrapper)
  		nulls[Anum_pg_extension_extversion - 1] = true;
  	else
  		values[Anum_pg_extension_extversion - 1] =
  			CStringGetTextDatum(control->version);
  
! 	tuple = heap_form_tuple(RelationGetDescr(rel), values, nulls);
! 
! 	/*
! 	 * CREATE EXTENSION WITH OIDS allows to force it for upgrading
! 	 */
! 	if (OidIsValid(extensionoid))
! 		HeapTupleSetOid(tuple, extensionoid);
  	extensionoid = simple_heap_insert(rel, tuple);
+ 
+ 	elog(DEBUG1, "HeapTupleIsValid: %s", HeapTupleIsValid(tuple) ? "true" : "false");
+ 
  	CatalogUpdateIndexes(rel, tuple);
  	heap_freetuple(tuple);
  	heap_close(rel, ExclusiveLock);
***************
*** 539,658 **** CreateExtension(CreateExtensionStmt *stmt)
  	CreateExtensionAddress.classId = ExtensionRelationId;
  	CreateExtensionAddress.objectId = extensionoid;
  	CreateExtensionAddress.objectSubId = 0;
- 	create_extension = true;
- 
- 	/*
- 	 * Also set create_extension_with_user_data so that the function
- 	 * pg_extension_with_user_data returns the right value.
- 	 */
- 	create_extension_with_user_data = user_data;
- 
- 	elog(DEBUG1,
- 		 "Installing extension '%s' from '%s', in schema %s, %s user data",
- 		 stmt->extname,
- 		 get_extension_absolute_path(control->script),
- 		 schema ? schema : "public",
- 		 create_extension_with_user_data ? "with" : "without");
- 
- 	/*
- 	 * Force the min message settings here, because SPI will dump the
- 	 * query_string into the messages and in this case, that's a full
- 	 * extension script. At the time of this writing, contrib/isn/isn.sql.in
- 	 * is already 3197 lines and will issue lots of CREATE TYPE shell
- 	 * notices.
- 	 *
- 	 * We want to avoid dumping the full script for each of those.
- 	 */
- 	reset_cmsgs = client_min_messages < WARNING;
- 	reset_lmsgs = log_min_messages < WARNING;
- 
- 	if (reset_cmsgs)
- 	{
- 		old_cmsgs = pstrdup((char *)GetConfigOption("client_min_messages", false));
- 		SetConfigOption("client_min_messages", "warning", PGC_SUSET, PGC_S_SESSION);
- 	}
- 	if (reset_lmsgs)
- 	{
- 		old_lmsgs = pstrdup((char *)GetConfigOption("log_min_messages", false));
- 		SetConfigOption("log_min_messages", "warning", PGC_SUSET, PGC_S_SESSION);
- 	}
- 
- 	/*
- 	 * On failure, ensure that create_extension does not remain set.
- 	 */
- 	PG_TRY();
- 	{
- 		char *filename = get_extension_absolute_path(control->script);
- 		char *sql = read_extension_script_file(control,
- 											   pg_encoding_to_char(encoding));
- 
- 		/*
- 		 * An extension that is relocatable does not need any replacement,
- 		 * we will ALTER EXTENSION SET SCHEMA just after installing it. See
- 		 * below for details.
- 		 *
- 		 * An extension that is not relocatable need to offer a mechanism
- 		 * for the users to be able to choose where to intall it, that's
- 		 * using the placeholder @extschema@.
- 		 */
- 		if (control->relocatable)
- 		{
- 			List *search_path = fetch_search_path(false);
- 			Oid   first_schema, target_schema;
- 
- 			if (!execute_sql_string(sql))
- 				ereport(ERROR,
- 						(errcode(ERRCODE_DATA_EXCEPTION),
- 						 errmsg("could not execute sql file: '%s'", filename)));
- 
- 			/*
- 			 * CREATE EXTENSION foo WITH SCHEMA bar;
- 			 *
- 			 * The extension is relocatable, so we run its install script
- 			 * and it has to install all objects into the same schema. Then
- 			 * we relocate all the objects to the target SCHEMA bar,
- 			 * ERRORing out if some objects are not in the same schema than
- 			 * others, that's doing:
- 			 *
- 			 * ALTER EXTENSION foo SET SCHEMA bar;
- 			 *
- 			 * We skip this step if the target schema happens to be the
- 			 * first schema of the current search_path.
- 			 */
- 			first_schema = linitial_oid(search_path);
- 			target_schema = LookupCreationNamespace(schema);
- 			list_free(search_path);
- 
- 			if (first_schema != target_schema)
- 				AlterExtensionNamespace_oid(extensionoid, target_schema);
- 		}
- 		else
- 		{
- 			/*
- 			 * Prepare the replacements we use in execute_sql_file(). Even if
- 			 * the schema has not been specified by the user, we still have to
- 			 * set @extschema@ to 'public' so that extension scripts can rely on
- 			 * this placeholder.
- 			 */
- 			sql = text_to_cstring(
- 				DatumGetTextPP(
- 					DirectFunctionCall3(replace_text,
- 										CStringGetTextDatum(sql),
- 										CStringGetTextDatum("@extschema@"),
- 										CStringGetTextDatum(schema))));
- 
- 			if (!execute_sql_string(sql))
- 				ereport(ERROR,
- 						(errcode(ERRCODE_DATA_EXCEPTION),
- 						 errmsg("could not execute sql file: '%s'", filename)));
- 		}
- 	}
- 	PG_CATCH();
- 	{
- 		create_extension = false;
- 		PG_RE_THROW();
- 	}
- 	PG_END_TRY();
  
  	/*
  	 * the extension depends on the schema we used, record that
--- 779,784 ----
***************
*** 667,682 **** CreateExtension(CreateExtensionStmt *stmt)
  		recordDependencyOn(&CreateExtensionAddress, &nsp, DEPENDENCY_NORMAL);
  	}
  
! 	if (reset_cmsgs)
! 		SetConfigOption("client_min_messages",
! 						old_cmsgs, PGC_SUSET, PGC_S_SESSION);
! 	if (reset_lmsgs)
! 		SetConfigOption("log_min_messages",
! 						old_lmsgs, PGC_SUSET, PGC_S_SESSION);
! 
! 	/* reset the current_extension dependency tracker */
! 	create_extension = false;
  
  	return;
  }
  
--- 793,808 ----
  		recordDependencyOn(&CreateExtensionAddress, &nsp, DEPENDENCY_NORMAL);
  	}
  
! 	/*
! 	 * When CREATE WRAPPER EXTENSION is used, skip the script
! 	 */
! 	if (stmt->wrapper)
! 	{
! 		elog(DEBUG1, "Skip the script for a WRAPPER extension");
! 		return;
! 	}
  
+ 	execute_extension_script(control, schema, true, user_data);
  	return;
  }
  
***************
*** 832,837 **** get_extension_name(Oid ext_oid)
--- 958,1016 ----
  }
  
  /*
+  * get_extension_version - given an extension OID, look up the version
+  *
+  * Returns a palloc'd string, or NULL if no such extension.
+  */
+ char *
+ get_extension_version(Oid ext_oid)
+ {
+ 	char	   *result;
+ 	Relation	rel;
+ 	HeapScanDesc scandesc;
+ 	HeapTuple	tuple;
+ 	ScanKeyData entry[1];
+ 
+ 	/*
+ 	 * Search pg_extension.  We use a heapscan here even though there is an
+ 	 * index on oid, on the theory that pg_extension will usually have just a
+ 	 * few entries and so an indexed lookup is a waste of effort.
+ 	 */
+ 	rel = heap_open(ExtensionRelationId, AccessShareLock);
+ 
+ 	ScanKeyInit(&entry[0],
+ 				ObjectIdAttributeNumber,
+ 				BTEqualStrategyNumber, F_OIDEQ,
+ 				ObjectIdGetDatum(ext_oid));
+ 	scandesc = heap_beginscan(rel, SnapshotNow, 1, entry);
+ 	tuple = heap_getnext(scandesc, ForwardScanDirection);
+ 
+ 	/* We assume that there can be at most one matching tuple */
+ 	if (HeapTupleIsValid(tuple))
+ 	{
+ 		bool  isnull;
+ 		Datum d_version;
+ 
+ 		d_version = heap_getattr(tuple,
+ 								 Anum_pg_extension_extversion,
+ 								 rel->rd_att,
+ 								 &isnull);
+ 
+ 		if (isnull)
+ 			result = NULL;
+ 		else
+ 			result = pstrdup(TextDatumGetCString(d_version));
+ 	}
+ 	else
+ 		result = NULL;
+ 
+ 	heap_endscan(scandesc);
+ 	heap_close(rel, AccessShareLock);
+ 
+ 	return result;
+ }
+ 
+ /*
   * extension_relocatable_p
   *
   * Returns true when the extension is marked 'relocatable'
***************
*** 1120,1126 **** pg_extensions(PG_FUNCTION_ARGS)
  		 */
  		control = parse_extension_control_file(
  			get_extension_name_from_control_filename(de->d_name),
! 			get_extension_absolute_path(de->d_name), false);
  
  		memset(values, 0, sizeof(values));
  		MemSet(nulls, false, sizeof(nulls));
--- 1299,1305 ----
  		 */
  		control = parse_extension_control_file(
  			get_extension_name_from_control_filename(de->d_name),
! 			get_extension_absolute_path(de->d_name), false, NULL);
  
  		memset(values, 0, sizeof(values));
  		MemSet(nulls, false, sizeof(nulls));
***************
*** 1250,1256 **** AlterExtensionNamespace(List *name, const char *newschema)
  void
  AlterExtensionNamespace_oid(Oid extensionOid, Oid nspOid)
  {
! 	Oid			     oldNspOid;
  	ObjectAddress   *object;
  	ObjectAddresses *targetObjects;
  	int i;
--- 1429,1435 ----
  void
  AlterExtensionNamespace_oid(Oid extensionOid, Oid nspOid)
  {
! 	Oid			     oldNspOid = InvalidOid;
  	ObjectAddress   *object;
  	ObjectAddresses *targetObjects;
  	int i;
***************
*** 1270,1282 **** AlterExtensionNamespace_oid(Oid extensionOid, Oid nspOid)
  	object->objectId = extensionOid;
  	object->objectSubId = 0;
  
- 	/*
- 	 * At CREATE EXTENSION time the dependency towards the schema where live
- 	 * the extension's objects is not registered yet, so we get InvalidOid
- 	 * here.
- 	 */
- 	oldNspOid = get_extension_namespace(extensionOid);
- 
  	targetObjects = listDependentObjects(object);
  
  	for (i = 0; i < targetObjects->numrefs; i++)
--- 1449,1454 ----
***************
*** 1313,1315 **** AlterExtensionNamespace_oid(Oid extensionOid, Oid nspOid)
--- 1485,1609 ----
  
  	free_object_addresses(targetObjects);
  }
+ 
+ /*
+  * ALTER EXTENSION name UPGRADE
+  *
+  * Only superusers can upgrade an extension.
+  */
+ void
+ UpgradeExtension(AlterExtensionStmt *stmt)
+ {
+ 	char       *version, *nspname;
+ 	Oid			extensionoid = InvalidOid;
+ 	ExtensionControlFile *control;
+ 
+ 	/* Must be super user */
+ 	if (!superuser())
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 				 errmsg("permission denied to upgrade extension \"%s\"",
+ 						stmt->extname),
+ 				 errhint("Must be superuser to upgrade an extension.")));
+ 
+ 	/* We can only upgrade installed extension */
+ 	extensionoid = get_extension_oid(stmt->extname, false);
+ 	nspname = get_namespace_name(get_extension_namespace(extensionoid));
+ 	version = get_extension_version(extensionoid);
+ 	control = read_extension_control_file(stmt->extname, true, version);
+ 
+ 	/*
+ 	 * Prepare the same script execution context as in CREATE EXTENSION
+ 	 */
+ 	CreateExtensionAddress.classId = ExtensionRelationId;
+ 	CreateExtensionAddress.objectId = extensionoid;
+ 	CreateExtensionAddress.objectSubId = 0;
+ 
+ 	create_extension = true;
+ 	create_extension_with_user_data = false;
+ 
+ 	/*
+ 	 * When doing and extension UPGRADE the user data is certainly there
+ 	 * already, so the user_data argument is 'false' here.
+ 	 */
+ 	execute_extension_script(control, nspname, false, false);
+ 
+ 	/* Now upgrade the version in the catalogs */
+ 	{
+ 		Relation	rel;
+ 		HeapScanDesc scandesc;
+ 		ScanKeyData entry[1];
+ 		HeapTuple	tup, newtup;
+ 		Datum	   *values;
+ 		bool	   *nulls;
+ 		bool	   *replaces;
+ 
+ 		rel = heap_open(ExtensionRelationId, RowExclusiveLock);
+ 
+ 		ScanKeyInit(&entry[0],
+ 					Anum_pg_extension_extname,
+ 					BTEqualStrategyNumber, F_NAMEEQ,
+ 					CStringGetDatum(stmt->extname));
+ 		scandesc = heap_beginscan(rel, SnapshotNow, 1, entry);
+ 		tup = heap_getnext(scandesc, ForwardScanDirection);
+ 
+ 		values = palloc0(rel->rd_att->natts * sizeof(Datum));
+ 		nulls = palloc0(rel->rd_att->natts * sizeof(bool));
+ 		replaces = palloc0(rel->rd_att->natts * sizeof(bool));
+ 		values[Anum_pg_extension_extversion - 1] =
+  			CStringGetTextDatum(control->version);
+ 		replaces[Anum_pg_extension_extversion - 1] = true;
+ 		newtup = heap_modify_tuple(tup, rel->rd_att, values, nulls, replaces);
+ 
+ 		/* Perform actual update */
+ 		simple_heap_update(rel, &tup->t_self, newtup);
+ 		CatalogUpdateIndexes(rel, newtup);
+ 
+ 		/* Release memory */
+ 		pfree(values);
+ 		pfree(nulls);
+ 		pfree(replaces);
+ 
+ 		heap_endscan(scandesc);
+ 		heap_close(rel, RowExclusiveLock);
+ 	}
+ }
+ 
+ /*
+  * ALTER OBJECT SET EXTENSION
+  *
+  * This form allows for upgrading from previous PostgreSQL version to one
+  * supporting extensions, or to upgrade user objects to get to use the
+  * extension infrastructure.
+  *
+  * All we have to do is record an INTERNAL dependency between the selected
+  * object and the extension, which must of course already exist.
+  */
+ void
+ AlterObjectExtension(const char *extname,
+ 					 Oid classId, Oid objectId, Oid objectSubId)
+ {
+ 	Oid			extensionoid = InvalidOid;
+ 	ObjectAddress   *extension, *object;
+ 
+ 	if (!superuser())
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 				 (errmsg("must be superuser to SET EXTENSION"))));
+ 
+ 	extensionoid = get_extension_oid(extname, false);
+ 
+ 	extension = (ObjectAddress *)palloc(sizeof(ObjectAddress));
+ 	extension->classId = ExtensionRelationId;
+ 	extension->objectId = extensionoid;
+ 	extension->objectSubId = 0;
+ 
+ 	object = (ObjectAddress *)palloc(sizeof(ObjectAddress));
+ 	object->classId = classId;
+ 	object->objectId = objectId;
+ 	object->objectSubId = objectSubId;
+ 
+ 	recordDependencyOn(object, extension, DEPENDENCY_INTERNAL);
+ 
+ 	return;
+ }
*** a/src/backend/commands/functioncmds.c
--- b/src/backend/commands/functioncmds.c
***************
*** 1950,1955 **** AlterFunctionNamespace_oid(Oid procOid, Oid nspOid)
--- 1950,1979 ----
  	return oldNspOid;
  }
  
+ /*
+  * Execute ALTER FUNCTION/AGGREGATE SET EXTENSION
+  *
+  * These commands are identical except for the lookup procedure, so share code.
+  */
+ void
+ AlterFunctionExtension(List *name, List *argtypes, bool isagg,
+ 					   const char *extname)
+ {
+ 	Oid			procOid;
+ 
+ 	/* get function OID */
+ 	if (isagg)
+ 		procOid = LookupAggNameTypeNames(name, argtypes, false);
+ 	else
+ 		procOid = LookupFuncNameTypeNames(name, argtypes, false);
+ 
+ 	/* check permissions on function */
+ 	if (!pg_proc_ownercheck(procOid, GetUserId()))
+ 		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
+ 					   NameListToString(name));
+ 
+ 	AlterObjectExtension(extname, ProcedureRelationId, procOid, 0);
+ }
  
  /*
   * ExecuteDoStmt
*** a/src/backend/commands/opclasscmds.c
--- b/src/backend/commands/opclasscmds.c
***************
*** 2065,2070 **** AlterOpClassNamespace_oid(Oid opclassOid, Oid newNspOid)
--- 2065,2089 ----
  }
  
  /*
+  * ALTER OPERATOR CLASS any_name USING access_method SET EXTENSION name
+  */
+ void
+ AlterOpClassExtension(List *name, List *argam, const char *extname)
+ {
+ 	Oid			amOid;
+ 	char       *access_method = linitial(argam);
+ 	Oid			oid;
+ 
+ 	Assert(list_length(argam) == 1);
+ 
+ 	/* Look up the opclass. */
+ 	amOid = get_am_oid(access_method, false);
+ 	oid = get_opclass_oid(amOid, name, false);
+ 
+ 	AlterObjectExtension(extname, OperatorClassRelationId, oid, 0);
+ }
+ 
+ /*
   * Change opfamily owner by name
   */
  void
***************
*** 2281,2283 **** AlterOpFamilyNamespace_oid(Oid opfamilyOid, Oid newNspOid)
--- 2300,2321 ----
  	heap_close(rel, NoLock);
  	return oldNspOid;
  }
+ 
+ /*
+  * ALTER OPERATOR FAMILY any_name USING access_method SET EXTENSION name
+  */
+ void
+ AlterOpFamilyExtension(List *name, List *argam, const char *extname)
+ {
+ 	Oid			amOid;
+ 	char       *access_method = linitial(argam);
+ 	Oid			oid;
+ 
+ 	Assert(list_length(argam) == 1);
+ 
+ 	/* Look up the opfamily */
+ 	amOid = get_am_oid(access_method, false);
+ 	oid = get_opfamily_oid(amOid, name, false);
+ 
+ 	AlterObjectExtension(extname, OperatorFamilyRelationId, oid, 0);
+ }
*** a/src/backend/commands/operatorcmds.c
--- b/src/backend/commands/operatorcmds.c
***************
*** 43,48 ****
--- 43,49 ----
  #include "catalog/pg_type.h"
  #include "commands/alter.h"
  #include "commands/defrem.h"
+ #include "commands/extension.h"
  #include "miscadmin.h"
  #include "parser/parse_func.h"
  #include "parser/parse_oper.h"
***************
*** 507,509 **** AlterOperatorNamespace_oid(Oid operOid, Oid newNspOid)
--- 508,529 ----
  
  	return oldNspOid;
  }
+ 
+ /*
+  * Execute ALTER OPERATOR SET EXTENSION
+  */
+ void
+ AlterOperatorExtension(List *names, List *argtypes, const char *extname)
+ {
+ 	List	   *operatorName = names;
+ 	TypeName   *typeName1 = (TypeName *) linitial(argtypes);
+ 	TypeName   *typeName2 = (TypeName *) lsecond(argtypes);
+ 	Oid			operOid;
+ 
+ 	Assert(list_length(argtypes) == 2);
+ 	operOid = LookupOperNameTypeNames(NULL, operatorName,
+ 									  typeName1, typeName2,
+ 									  false, -1);
+ 
+ 	AlterObjectExtension(extname, OperatorRelationId, operOid, 0);
+ }
*** a/src/backend/commands/tablecmds.c
--- b/src/backend/commands/tablecmds.c
***************
*** 8268,8273 **** AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
--- 8268,8290 ----
  }
  
  /*
+  * Execute ALTER TABLE|SEQUENCE|VIEW SET EXTENSION
+  *
+  * Note: caller must have checked ownership of the relation already
+  */
+ void
+ AlterTableExtension(RangeVar *relation, const char *extname)
+ {
+ 	Relation	rel;
+ 	Oid			relid;
+ 
+ 	rel = relation_openrv(relation, AccessExclusiveLock);
+ 	relid = RelationGetRelid(rel);
+ 	AlterObjectExtension(extname, RelationRelationId, relid, 0);
+ 	relation_close(rel, NoLock);
+ }
+ 
+ /*
   * Move all indexes for the specified relation to another namespace.
   *
   * Note: we assume adequate permission checking was done by the caller,
*** a/src/backend/commands/tsearchcmds.c
--- b/src/backend/commands/tsearchcmds.c
***************
*** 448,453 **** AlterTSParserNamespace_oid(Oid prsId, Oid newNspOid)
--- 448,463 ----
  	return oldNspOid;
  }
  
+ /*
+  * ALTER TEXT SEARCH PARSER any_name SET EXTENSION name
+  */
+ void
+ AlterTSParserExtension(List *name, const char *extname)
+ {
+ 	Oid			prsId = get_ts_parser_oid(name, false);
+ 	AlterObjectExtension(extname, TSParserRelationId, prsId, 0);
+ }
+ 
  /* ---------------------- TS Dictionary commands -----------------------*/
  
  /*
***************
*** 732,737 **** AlterTSDictionaryNamespace_oid(Oid dictId, Oid newNspOid)
--- 742,757 ----
  }
  
  /*
+  * ALTER TEXT SEARCH DICTIONARY any_name SET EXTENSION name
+  */
+ void
+ AlterTSDictionaryExtension(List *name, const char *extname)
+ {
+ 	Oid			dictId = get_ts_dict_oid(name, false);
+ 	AlterObjectExtension(extname, TSDictionaryRelationId, dictId, 0);
+ }
+ 
+ /*
   * DROP TEXT SEARCH DICTIONARY
   */
  void
***************
*** 1265,1270 **** AlterTSTemplateNamespace_oid(Oid tmplId, Oid newNspOid)
--- 1285,1300 ----
  }
  
  /*
+  * ALTER TEXT SEARCH TEMPLATE any_name SET EXTENSION name
+  */
+ void
+ AlterTSTemplateExtension(List *name, const char *extname)
+ {
+ 	Oid			tmplId = get_ts_template_oid(name, false);
+ 	AlterObjectExtension(extname, TSTemplateRelationId, tmplId, 0);
+ }
+ 
+ /*
   * DROP TEXT SEARCH TEMPLATE
   */
  void
***************
*** 1719,1724 **** AlterTSConfigurationNamespace_oid(Oid cfgId, Oid newNspOid)
--- 1749,1764 ----
  }
  
  /*
+  * ALTER TEXT SEARCH CONFIGURATION any_name SET EXTENSION name
+  */
+ void
+ AlterTSConfigurationExtension(List *name, const char *extname)
+ {
+ 	Oid			cfgId = get_ts_config_oid(name, false);
+ 	AlterObjectExtension(extname, TSConfigRelationId, cfgId, 0);
+ }
+ 
+ /*
   * DROP TEXT SEARCH CONFIGURATION
   */
  void
*** a/src/backend/commands/typecmds.c
--- b/src/backend/commands/typecmds.c
***************
*** 2923,2925 **** AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
--- 2923,2941 ----
  
  	return oldNspOid;
  }
+ 
+ /*
+  * Execute ALTER TYPE SET EXTENSION
+  */
+ void
+ AlterTypeExtension(List *names, const char *extname)
+ {
+ 	TypeName   *typename;
+ 	Oid			typeOid;
+ 
+ 	/* Make a TypeName so we can use standard type lookup machinery */
+ 	typename = makeTypeNameFromNameList(names);
+ 	typeOid = typenameTypeId(NULL, typename);
+ 
+ 	AlterObjectExtension(extname, TypeRelationId, typeOid, 0);
+ }
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
***************
*** 2826,2831 **** _copyAlterObjectSchemaStmt(AlterObjectSchemaStmt *from)
--- 2826,2846 ----
  	return newnode;
  }
  
+ static AlterObjectExtensionStmt *
+ _copyAlterObjectExtensionStmt(AlterObjectExtensionStmt *from)
+ {
+ 	AlterObjectExtensionStmt *newnode = makeNode(AlterObjectExtensionStmt);
+ 
+ 	COPY_SCALAR_FIELD(objectType);
+ 	COPY_NODE_FIELD(relation);
+ 	COPY_NODE_FIELD(object);
+ 	COPY_NODE_FIELD(objarg);
+ 	COPY_STRING_FIELD(addname);
+ 	COPY_STRING_FIELD(extname);
+ 
+ 	return newnode;
+ }
+ 
  static AlterOwnerStmt *
  _copyAlterOwnerStmt(AlterOwnerStmt *from)
  {
***************
*** 3156,3161 **** _copyCreateExtensionStmt(CreateExtensionStmt *from)
--- 3171,3177 ----
  	CreateExtensionStmt *newnode = makeNode(CreateExtensionStmt);
  
  	COPY_STRING_FIELD(extname);
+ 	COPY_SCALAR_FIELD(wrapper);
  	COPY_NODE_FIELD(options);
  
  	return newnode;
***************
*** 3173,3178 **** _copyDropExtensionStmt(DropExtensionStmt *from)
--- 3189,3204 ----
  	return newnode;
  }
  
+ static AlterExtensionStmt *
+ _copyAlterExtensionStmt(AlterExtensionStmt *from)
+ {
+ 	AlterExtensionStmt *newnode = makeNode(AlterExtensionStmt);
+ 
+ 	COPY_STRING_FIELD(extname);
+ 
+ 	return newnode;
+ }
+ 
  static CreateTableSpaceStmt *
  _copyCreateTableSpaceStmt(CreateTableSpaceStmt *from)
  {
***************
*** 3694,3699 **** copyObject(void *from)
--- 3720,3727 ----
  	/* Guard against stack overflow due to overly complex expressions */
  	check_stack_depth();
  
+ 	Assert(nodeTag(from) <= T_InlineCodeBlock);
+ 
  	switch (nodeTag(from))
  	{
  			/*
***************
*** 4104,4109 **** copyObject(void *from)
--- 4132,4140 ----
  		case T_AlterObjectSchemaStmt:
  			retval = _copyAlterObjectSchemaStmt(from);
  			break;
+ 		case T_AlterObjectExtensionStmt:
+ 			retval = _copyAlterObjectExtensionStmt(from);
+ 			break;
  		case T_AlterOwnerStmt:
  			retval = _copyAlterOwnerStmt(from);
  			break;
***************
*** 4197,4202 **** copyObject(void *from)
--- 4228,4236 ----
  		case T_DropExtensionStmt:
  			retval = _copyDropExtensionStmt(from);
  			break;
+ 		case T_AlterExtensionStmt:
+ 			retval = _copyAlterExtensionStmt(from);
+ 			break;
  		case T_AlterTableSpaceOptionsStmt:
  			retval = _copyAlterTableSpaceOptionsStmt(from);
  			break;
***************
*** 4392,4398 **** copyObject(void *from)
  			break;
  
  		default:
! 			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(from));
  			retval = from;		/* keep compiler quiet */
  			break;
  	}
--- 4426,4432 ----
  			break;
  
  		default:
! 			elog(ERROR, "unrecognized BAR node type: %d", (int) nodeTag(from));
  			retval = from;		/* keep compiler quiet */
  			break;
  	}
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
***************
*** 1325,1330 **** _equalAlterObjectSchemaStmt(AlterObjectSchemaStmt *a, AlterObjectSchemaStmt *b)
--- 1325,1343 ----
  }
  
  static bool
+ _equalAlterObjectExtensionStmt(AlterObjectExtensionStmt *a, AlterObjectExtensionStmt *b)
+ {
+ 	COMPARE_SCALAR_FIELD(objectType);
+ 	COMPARE_NODE_FIELD(relation);
+ 	COMPARE_NODE_FIELD(object);
+ 	COMPARE_NODE_FIELD(objarg);
+ 	COMPARE_STRING_FIELD(addname);
+ 	COMPARE_STRING_FIELD(extname);
+ 
+ 	return true;
+ }
+ 
+ static bool
  _equalAlterOwnerStmt(AlterOwnerStmt *a, AlterOwnerStmt *b)
  {
  	COMPARE_SCALAR_FIELD(objectType);
***************
*** 1617,1622 **** static bool
--- 1630,1636 ----
  _equalCreateExtensionStmt(CreateExtensionStmt *a, CreateExtensionStmt *b)
  {
  	COMPARE_STRING_FIELD(extname);
+ 	COMPARE_SCALAR_FIELD(wrapper);
  	COMPARE_NODE_FIELD(options);
  
  	return true;
***************
*** 1633,1638 **** _equalDropExtensionStmt(DropExtensionStmt *a, DropExtensionStmt *b)
--- 1647,1660 ----
  }
  
  static bool
+ _equalAlterExtensionStmt(AlterExtensionStmt *a, AlterExtensionStmt *b)
+ {
+ 	COMPARE_STRING_FIELD(extname);
+ 
+ 	return true;
+ }
+ 
+ static bool
  _equalAlterTableSpaceOptionsStmt(AlterTableSpaceOptionsStmt *a,
  								 AlterTableSpaceOptionsStmt *b)
  {
***************
*** 2723,2728 **** equal(void *a, void *b)
--- 2745,2753 ----
  		case T_AlterObjectSchemaStmt:
  			retval = _equalAlterObjectSchemaStmt(a, b);
  			break;
+ 		case T_AlterObjectExtensionStmt:
+ 			retval = _equalAlterObjectExtensionStmt(a, b);
+ 			break;
  		case T_AlterOwnerStmt:
  			retval = _equalAlterOwnerStmt(a, b);
  			break;
***************
*** 2810,2815 **** equal(void *a, void *b)
--- 2835,2843 ----
  		case T_DropExtensionStmt:
  			retval = _equalDropExtensionStmt(a, b);
  			break;
+ 		case T_AlterExtensionStmt:
+ 			retval = _equalAlterExtensionStmt(a, b);
+ 			break;
  		case T_CreateTableSpaceStmt:
  			retval = _equalCreateTableSpaceStmt(a, b);
  			break;
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 183,190 **** static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
  
  %type <node>	stmt schema_stmt
  		AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterEnumStmt
! 		AlterFdwStmt AlterForeignServerStmt AlterGroupStmt
! 		AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
  		AlterCompositeTypeStmt AlterUserStmt AlterUserMappingStmt AlterUserSetStmt
  		AlterRoleStmt AlterRoleSetStmt
  		AlterDefaultPrivilegesStmt DefACLAction
--- 183,191 ----
  
  %type <node>	stmt schema_stmt
  		AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterEnumStmt
! 		AlterExtensionStmt AlterFdwStmt AlterForeignServerStmt AlterGroupStmt
! 		AlterObjectSchemaStmt AlterObjectExtensionStmt AlterOwnerStmt
!         AlterSeqStmt AlterTableStmt
  		AlterCompositeTypeStmt AlterUserStmt AlterUserMappingStmt AlterUserSetStmt
  		AlterRoleStmt AlterRoleSetStmt
  		AlterDefaultPrivilegesStmt DefACLAction
***************
*** 537,543 **** static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
  	TRUNCATE TRUSTED TYPE_P
  
  	UNBOUNDED UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNTIL
! 	UPDATE USER USING
  
  	VACUUM VALID VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING
  	VERBOSE VERSION_P VIEW VOLATILE
--- 538,544 ----
  	TRUNCATE TRUSTED TYPE_P
  
  	UNBOUNDED UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNTIL
! 	UPDATE UPGRADE USER USING
  
  	VACUUM VALID VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING
  	VERBOSE VERSION_P VIEW VOLATILE
***************
*** 654,664 **** stmt :
--- 655,667 ----
  			| AlterDefaultPrivilegesStmt
  			| AlterDomainStmt
  			| AlterEnumStmt
+ 			| AlterExtensionStmt
  			| AlterFdwStmt
  			| AlterForeignServerStmt
  			| AlterFunctionStmt
  			| AlterGroupStmt
  			| AlterObjectSchemaStmt
+ 			| AlterObjectExtensionStmt
  			| AlterOwnerStmt
  			| AlterSeqStmt
  			| AlterTableStmt
***************
*** 3088,3106 **** opt_procedural:
  /*****************************************************************************
   *
   * 		QUERY:
!  *             CREATE EXTENSION extension
   *             [ [ WITH ] [ [ NO ] USER DATA ]
   *                        [ SCHEMA [=] schema ] ]
   *
   *****************************************************************************/
  
! CreateExtensionStmt: CREATE EXTENSION name opt_with create_extension_opt_list
  				{
  					CreateExtensionStmt *n = makeNode(CreateExtensionStmt);
  					n->extname = $3;
  					n->options = $5;
  					$$ = (Node *) n;
  				}
  		;
  
  create_extension_opt_list:
--- 3091,3120 ----
  /*****************************************************************************
   *
   * 		QUERY:
!  *             CREATE [ WRAPPER ] EXTENSION extension
   *             [ [ WITH ] [ [ NO ] USER DATA ]
+  *                        [ SYSID  [=] oid ] ]
   *                        [ SCHEMA [=] schema ] ]
   *
   *****************************************************************************/
  
! CreateExtensionStmt:
! 			CREATE EXTENSION name opt_with create_extension_opt_list
  				{
  					CreateExtensionStmt *n = makeNode(CreateExtensionStmt);
  					n->extname = $3;
+ 					n->wrapper = false;
  					n->options = $5;
  					$$ = (Node *) n;
  				}
+ 			| CREATE WRAPPER EXTENSION name opt_with create_extension_opt_list
+ 				{
+ 					CreateExtensionStmt *n = makeNode(CreateExtensionStmt);
+ 					n->extname = $4;
+ 					n->wrapper = true;
+ 					n->options = $6;
+ 					$$ = (Node *) n;
+ 				}
  		;
  
  create_extension_opt_list:
***************
*** 3124,3129 **** create_extension_opt_item:
--- 3138,3147 ----
  				{
  					$$ = makeDefElem("schema", (Node *)makeString($3));
  				}
+ 			| SYSID opt_equal Iconst
+ 				{
+ 					$$ = makeDefElem("sysid", (Node *)makeInteger($3));
+ 				}
  		;
  
  /*****************************************************************************
***************
*** 3154,3159 **** DropExtensionStmt: DROP EXTENSION name opt_drop_behavior
--- 3172,3192 ----
  /*****************************************************************************
   *
   * 		QUERY:
+  *             ALTER EXTENSION <extension> UPGRADE
+  *
+  *****************************************************************************/
+ 
+ AlterExtensionStmt: ALTER EXTENSION name UPGRADE
+ 				{
+ 					AlterExtensionStmt *n = makeNode(AlterExtensionStmt);
+ 					n->extname = $3;
+ 					$$ = (Node *) n;
+ 				}
+ 		;
+ 
+ /*****************************************************************************
+  *
+  * 		QUERY:
   *             CREATE TABLESPACE tablespace LOCATION '/path/to/tablespace/'
   *
   *****************************************************************************/
***************
*** 6249,6254 **** AlterObjectSchemaStmt:
--- 6282,6421 ----
  
  /*****************************************************************************
   *
+  * ALTER THING name SET EXTENSION name
+  *
+  *****************************************************************************/
+ 
+ AlterObjectExtensionStmt:
+ 			ALTER AGGREGATE func_name aggr_args SET EXTENSION name
+ 				{
+ 					AlterObjectExtensionStmt *n = makeNode(AlterObjectExtensionStmt);
+ 					n->objectType = OBJECT_AGGREGATE;
+ 					n->object = $3;
+ 					n->objarg = $4;
+ 					n->extname = $7;
+ 					$$ = (Node *)n;
+ 				}
+ 			| ALTER CONVERSION_P any_name SET EXTENSION name
+ 				{
+ 					AlterObjectExtensionStmt *n = makeNode(AlterObjectExtensionStmt);
+ 					n->objectType = OBJECT_CONVERSION;
+ 					n->object = $3;
+ 					n->extname = $6;
+ 					$$ = (Node *)n;
+ 				}
+ 			| ALTER DOMAIN_P any_name SET EXTENSION name
+ 				{
+ 					AlterObjectExtensionStmt *n = makeNode(AlterObjectExtensionStmt);
+ 					n->objectType = OBJECT_DOMAIN;
+ 					n->object = $3;
+ 					n->extname = $6;
+ 					$$ = (Node *)n;
+ 				}
+ 			| ALTER FUNCTION function_with_argtypes SET EXTENSION name
+ 				{
+ 					AlterObjectExtensionStmt *n = makeNode(AlterObjectExtensionStmt);
+ 					n->objectType = OBJECT_FUNCTION;
+ 					n->object = $3->funcname;
+ 					n->objarg = $3->funcargs;
+ 					n->extname = $6;
+ 					$$ = (Node *)n;
+ 				}
+ 			| ALTER OPERATOR any_operator oper_argtypes SET EXTENSION name
+ 				{
+ 					AlterObjectExtensionStmt *n = makeNode(AlterObjectExtensionStmt);
+ 					n->objectType = OBJECT_OPERATOR;
+ 					n->object = $3;
+ 					n->objarg = $4;
+ 					n->extname = $7;
+ 					$$ = (Node *)n;
+ 				}
+ 			| ALTER OPERATOR CLASS any_name USING access_method SET EXTENSION name
+ 				{
+ 					AlterObjectExtensionStmt *n = makeNode(AlterObjectExtensionStmt);
+ 					n->objectType = OBJECT_OPCLASS;
+ 					n->object = $4;
+ 					n->objarg = list_make1($6);
+ 					n->extname = $9;
+ 					$$ = (Node *)n;
+ 				}
+ 			| ALTER OPERATOR FAMILY any_name USING access_method SET EXTENSION name
+ 				{
+ 					AlterObjectExtensionStmt *n = makeNode(AlterObjectExtensionStmt);
+ 					n->objectType = OBJECT_OPFAMILY;
+ 					n->object = $4;
+ 					n->objarg = list_make1($6);
+ 					n->extname = $9;
+ 					$$ = (Node *)n;
+ 				}
+ 			| ALTER TABLE relation_expr SET EXTENSION name
+ 				{
+ 					AlterObjectExtensionStmt *n = makeNode(AlterObjectExtensionStmt);
+ 					n->objectType = OBJECT_TABLE;
+ 					n->relation = $3;
+ 					n->extname = $6;
+ 					$$ = (Node *)n;
+ 				}
+ 			| ALTER TEXT_P SEARCH PARSER any_name SET EXTENSION name
+ 				{
+ 					AlterObjectExtensionStmt *n = makeNode(AlterObjectExtensionStmt);
+ 					n->objectType = OBJECT_TSPARSER;
+ 					n->object = $5;
+ 					n->extname = $8;
+ 					$$ = (Node *)n;
+ 				}
+ 			| ALTER TEXT_P SEARCH DICTIONARY any_name SET EXTENSION name
+ 				{
+ 					AlterObjectExtensionStmt *n = makeNode(AlterObjectExtensionStmt);
+ 					n->objectType = OBJECT_TSDICTIONARY;
+ 					n->object = $5;
+ 					n->extname = $8;
+ 					$$ = (Node *)n;
+ 				}
+ 			| ALTER TEXT_P SEARCH TEMPLATE any_name SET EXTENSION name
+ 				{
+ 					AlterObjectExtensionStmt *n = makeNode(AlterObjectExtensionStmt);
+ 					n->objectType = OBJECT_TSTEMPLATE;
+ 					n->object = $5;
+ 					n->extname = $8;
+ 					$$ = (Node *)n;
+ 				}
+ 			| ALTER TEXT_P SEARCH CONFIGURATION any_name SET EXTENSION name
+ 				{
+ 					AlterObjectExtensionStmt *n = makeNode(AlterObjectExtensionStmt);
+ 					n->objectType = OBJECT_TSCONFIGURATION;
+ 					n->object = $5;
+ 					n->extname = $8;
+ 					$$ = (Node *)n;
+ 				}
+ 			| ALTER SEQUENCE qualified_name SET EXTENSION name
+ 				{
+ 					AlterObjectExtensionStmt *n = makeNode(AlterObjectExtensionStmt);
+ 					n->objectType = OBJECT_SEQUENCE;
+ 					n->relation = $3;
+ 					n->extname = $6;
+ 					$$ = (Node *)n;
+ 				}
+ 			| ALTER VIEW qualified_name SET EXTENSION name
+ 				{
+ 					AlterObjectExtensionStmt *n = makeNode(AlterObjectExtensionStmt);
+ 					n->objectType = OBJECT_VIEW;
+ 					n->relation = $3;
+ 					n->extname = $6;
+ 					$$ = (Node *)n;
+ 				}
+ 			| ALTER TYPE_P any_name SET EXTENSION name
+ 				{
+ 					AlterObjectExtensionStmt *n = makeNode(AlterObjectExtensionStmt);
+ 					n->objectType = OBJECT_TYPE;
+ 					n->object = $3;
+ 					n->extname = $6;
+ 					$$ = (Node *)n;
+ 				}
+ 		;
+ 
+ /*****************************************************************************
+  *
   * ALTER THING name OWNER TO newname
   *
   *****************************************************************************/
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
***************
*** 162,171 **** check_xact_readonly(Node *parsetree)
--- 162,173 ----
  		case T_AlterDatabaseStmt:
  		case T_AlterDatabaseSetStmt:
  		case T_AlterDomainStmt:
+ 		case T_AlterExtensionStmt:
  		case T_AlterFunctionStmt:
  		case T_AlterRoleStmt:
  		case T_AlterRoleSetStmt:
  		case T_AlterObjectSchemaStmt:
+ 		case T_AlterObjectExtensionStmt:
  		case T_AlterOwnerStmt:
  		case T_AlterSeqStmt:
  		case T_AlterTableStmt:
***************
*** 569,574 **** standard_ProcessUtility(Node *parsetree,
--- 571,580 ----
  			DropExtension((DropExtensionStmt *) parsetree);
  			break;
  
+ 		case T_AlterExtensionStmt:
+ 			UpgradeExtension((AlterExtensionStmt *) parsetree);
+ 			break;
+ 
  		case T_CreateTableSpaceStmt:
  			PreventTransactionChain(isTopLevel, "CREATE TABLESPACE");
  			CreateTableSpace((CreateTableSpaceStmt *) parsetree);
***************
*** 718,723 **** standard_ProcessUtility(Node *parsetree,
--- 724,733 ----
  			ExecAlterObjectSchemaStmt((AlterObjectSchemaStmt *) parsetree);
  			break;
  
+ 		case T_AlterObjectExtensionStmt:
+ 			ExecAlterObjectExtensionStmt((AlterObjectExtensionStmt *) parsetree);
+ 			break;
+ 
  		case T_AlterOwnerStmt:
  			ExecAlterOwnerStmt((AlterOwnerStmt *) parsetree);
  			break;
***************
*** 1528,1533 **** CreateCommandTag(Node *parsetree)
--- 1538,1547 ----
  			tag = "DROP EXTENSION";
  			break;
  
+ 		case T_AlterExtensionStmt:
+ 			tag = "ALTER EXTENSION";
+ 			break;
+ 
  		case T_CreateTableSpaceStmt:
  			tag = "CREATE TABLESPACE";
  			break;
***************
*** 1764,1769 **** CreateCommandTag(Node *parsetree)
--- 1778,1837 ----
  			}
  			break;
  
+ 		case T_AlterObjectExtensionStmt:
+ 			switch (((AlterObjectExtensionStmt *) parsetree)->objectType)
+ 			{
+ 				case OBJECT_AGGREGATE:
+ 					tag = "ALTER AGGREGATE";
+ 					break;
+ 				case OBJECT_CONVERSION:
+ 					tag = "ALTER CONVERSION";
+ 					break;
+ 				case OBJECT_DOMAIN:
+ 					tag = "ALTER DOMAIN";
+ 					break;
+ 				case OBJECT_FUNCTION:
+ 					tag = "ALTER FUNCTION";
+ 					break;
+ 				case OBJECT_OPERATOR:
+ 					tag = "ALTER OPERATOR";
+ 					break;
+ 				case OBJECT_OPCLASS:
+ 					tag = "ALTER OPERATOR CLASS";
+ 					break;
+ 				case OBJECT_OPFAMILY:
+ 					tag = "ALTER OPERATOR FAMILY";
+ 					break;
+ 				case OBJECT_SEQUENCE:
+ 					tag = "ALTER SEQUENCE";
+ 					break;
+ 				case OBJECT_TABLE:
+ 					tag = "ALTER TABLE";
+ 					break;
+ 				case OBJECT_TYPE:
+ 					tag = "ALTER TYPE";
+ 					break;
+ 				case OBJECT_TSPARSER:
+ 					tag = "ALTER TEXT SEARCH PARSER";
+ 					break;
+ 				case OBJECT_TSDICTIONARY:
+ 					tag = "ALTER TEXT SEARCH DICTIONARY";
+ 					break;
+ 				case OBJECT_TSTEMPLATE:
+ 					tag = "ALTER TEXT SEARCH TEMPLATE";
+ 					break;
+ 				case OBJECT_TSCONFIGURATION:
+ 					tag = "ALTER TEXT SEARCH CONFIGURATION";
+ 					break;
+ 				case OBJECT_VIEW:
+ 					tag = "ALTER VIEW";
+ 					break;
+ 				default:
+ 					tag = "???";
+ 					break;
+ 			}
+ 			break;
+ 
  		case T_AlterOwnerStmt:
  			switch (((AlterOwnerStmt *) parsetree)->objectType)
  			{
***************
*** 2349,2354 **** GetCommandLogLevel(Node *parsetree)
--- 2417,2426 ----
  			lev = LOGSTMT_DDL;
  			break;
  
+ 		case T_AlterExtensionStmt:
+ 			lev = LOGSTMT_DDL;
+ 			break;
+ 
  		case T_CreateTableSpaceStmt:
  			lev = LOGSTMT_DDL;
  			break;
***************
*** 2431,2436 **** GetCommandLogLevel(Node *parsetree)
--- 2503,2512 ----
  			lev = LOGSTMT_DDL;
  			break;
  
+ 		case T_AlterObjectExtensionStmt:
+ 			lev = LOGSTMT_DDL;
+ 			break;
+ 
  		case T_AlterOwnerStmt:
  			lev = LOGSTMT_DDL;
  			break;
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
***************
*** 2394,2401 **** getExtensions(int *numExtensions)
  	int			i_tableoid;
  	int			i_oid;
  	int			i_extname;
  	int			i_extversion;
- 	int			i_extcclass;
  	int			i_nspname;
  
  	/*
--- 2394,2401 ----
  	int			i_tableoid;
  	int			i_oid;
  	int			i_extname;
+ 	int			i_relocatable;
  	int			i_extversion;
  	int			i_nspname;
  
  	/*
***************
*** 2418,2424 **** getExtensions(int *numExtensions)
  	 */
  	appendPQExpBuffer(query,
  					  "SELECT x.tableoid, x.oid, "
! 					  "x.extname, x.extversion, x.custom_class, n.nspname "
  					  "FROM pg_extension x "
  					  "JOIN pg_depend d ON d.objid = x.oid "
  					  "and d.refclassid = 'pg_catalog.pg_namespace'::regclass "
--- 2418,2424 ----
  	 */
  	appendPQExpBuffer(query,
  					  "SELECT x.tableoid, x.oid, "
! 					  "x.extname, x.relocatable, x.extversion, n.nspname "
  					  "FROM pg_extension x "
  					  "JOIN pg_depend d ON d.objid = x.oid "
  					  "and d.refclassid = 'pg_catalog.pg_namespace'::regclass "
***************
*** 2434,2441 **** getExtensions(int *numExtensions)
  	i_tableoid = PQfnumber(res, "tableoid");
  	i_oid = PQfnumber(res, "oid");
  	i_extname = PQfnumber(res, "extname");
  	i_extversion = PQfnumber(res, "extversion");
- 	i_extcclass = PQfnumber(res, "custom_class");
  	i_nspname = PQfnumber(res, "nspname");
  
  	for (i = 0; i < ntups; i++)
--- 2434,2441 ----
  	i_tableoid = PQfnumber(res, "tableoid");
  	i_oid = PQfnumber(res, "oid");
  	i_extname = PQfnumber(res, "extname");
+ 	i_relocatable = PQfnumber(res, "relocatable");
  	i_extversion = PQfnumber(res, "extversion");
  	i_nspname = PQfnumber(res, "nspname");
  
  	for (i = 0; i < ntups; i++)
***************
*** 2445,2452 **** getExtensions(int *numExtensions)
  		extinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
  		AssignDumpId(&extinfo[i].dobj);
  		extinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_extname));
  		extinfo[i].extversion = strdup(PQgetvalue(res, i, i_extversion));
- 		extinfo[i].custom_class = strdup(PQgetvalue(res, i, i_extcclass));
  		extinfo[i].namespace = strdup(PQgetvalue(res, i, i_nspname));
  	}
  
--- 2445,2452 ----
  		extinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
  		AssignDumpId(&extinfo[i].dobj);
  		extinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_extname));
+ 		extinfo[i].relocatable = strdup(PQgetvalue(res, i, i_relocatable));
  		extinfo[i].extversion = strdup(PQgetvalue(res, i, i_extversion));
  		extinfo[i].namespace = strdup(PQgetvalue(res, i, i_nspname));
  	}
  
*** a/src/bin/pg_dump/pg_dump.h
--- b/src/bin/pg_dump/pg_dump.h
***************
*** 137,144 **** typedef struct _extensionInfo
  {
  	DumpableObject dobj;
  	char	   *extname;
  	char	   *extversion;
- 	char	   *custom_class;
  	char       *namespace;
  } ExtensionInfo;
  
--- 137,144 ----
  {
  	DumpableObject dobj;
  	char	   *extname;
+ 	char	   *relocatable;
  	char	   *extversion;
  	char       *namespace;
  } ExtensionInfo;
  
*** a/src/include/commands/alter.h
--- b/src/include/commands/alter.h
***************
*** 21,26 ****
--- 21,27 ----
  
  extern void ExecRenameStmt(RenameStmt *stmt);
  extern void ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt);
+ extern void ExecAlterObjectExtensionStmt(AlterObjectExtensionStmt *stmt);
  extern Oid AlterObjectNamespace(Relation rel, int cacheId,
  								 Oid classId, Oid objid, Oid nspId,
  								 int Anum_name, int Anum_namespace, int Anum_owner,
*** a/src/include/commands/conversioncmds.h
--- b/src/include/commands/conversioncmds.h
***************
*** 24,28 **** extern void AlterConversionOwner(List *name, Oid newOwnerId);
--- 24,29 ----
  extern void AlterConversionOwner_oid(Oid conversionOid, Oid newOwnerId);
  extern void AlterConversionNamespace(List *name, const char *newschema);
  extern Oid AlterConversionNamespace_oid(Oid convOid, Oid newNspOid);
+ extern void AlterConversionExtension(List *name, const char *extname);
  
  #endif   /* CONVERSIONCMDS_H */
*** a/src/include/commands/defrem.h
--- b/src/include/commands/defrem.h
***************
*** 67,72 **** extern void DropCastById(Oid castOid);
--- 67,74 ----
  extern void AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
  					   const char *newschema);
  extern Oid AlterFunctionNamespace_oid(Oid procOid, Oid nspOid);
+ extern void AlterFunctionExtension(List *name, List *argtypes, bool isagg,
+ 								   const char *extname);
  extern void ExecuteDoStmt(DoStmt *stmt);
  extern Oid get_cast_oid(Oid sourcetypeid, Oid targettypeid, bool missing_ok);
  
***************
*** 82,87 **** extern Oid get_opclass_oid(Oid amID, List *opclassname, bool missing_ok);
--- 84,90 ----
  extern Oid get_opfamily_oid(Oid amID, List *opfamilyname, bool missing_ok);
  extern Oid AlterOperatorNamespace_oid(Oid operOid, Oid newNspOid);
  extern void AlterOperatorNamespace(List *names, List *argtypes, const char *newschema);
+ extern void AlterOperatorExtension(List *names, List *argtypes, const char *extname);
  
  /* commands/aggregatecmds.c */
  extern void DefineAggregate(List *name, List *args, bool oldstyle,
***************
*** 106,116 **** extern void AlterOpClassOwner(List *name, const char *access_method, Oid newOwne
--- 109,121 ----
  extern void AlterOpClassOwner_oid(Oid opclassOid, Oid newOwnerId);
  extern void AlterOpClassNamespace(List *name, List *argam, const char *newschema);
  extern Oid AlterOpClassNamespace_oid(Oid opclassOid, Oid newNspOid);
+ extern void AlterOpClassExtension(List *name, List *argam, const char *extname);
  extern void AlterOpFamilyOwner(List *name, const char *access_method, Oid newOwnerId);
  extern void AlterOpFamilyOwner_oid(Oid opfamilyOid, Oid newOwnerId);
  extern Oid get_am_oid(const char *amname, bool missing_ok);
  extern void AlterOpFamilyNamespace(List *name, List *argam, const char *newschema);
  extern Oid AlterOpFamilyNamespace_oid(Oid opfamilyOid, Oid newNspOid);
+ extern void AlterOpFamilyExtension(List *name, List *argam, const char *extname);
  
  /* commands/tsearchcmds.c */
  extern void DefineTSParser(List *names, List *parameters);
***************
*** 120,125 **** extern void RemoveTSParsers(DropStmt *drop);
--- 125,131 ----
  extern void RemoveTSParserById(Oid prsId);
  extern void AlterTSParserNamespace(List *name, const char *newschema);
  extern Oid AlterTSParserNamespace_oid(Oid prsId, Oid newNspOid);
+ extern void AlterTSParserExtension(List *name, const char *extname);
  
  extern void DefineTSDictionary(List *names, List *parameters);
  extern void RenameTSDictionary(List *oldname, const char *newname);
***************
*** 129,139 **** extern void AlterTSDictionary(AlterTSDictionaryStmt *stmt);
--- 135,147 ----
  extern void AlterTSDictionaryOwner(List *name, Oid newOwnerId);
  extern void AlterTSDictionaryNamespace(List *name, const char *newschema);
  extern Oid AlterTSDictionaryNamespace_oid(Oid dictId, Oid newNspOid);
+ extern void AlterTSDictionaryExtension(List *name, const char *extname);
  
  extern void DefineTSTemplate(List *names, List *parameters);
  extern void RenameTSTemplate(List *oldname, const char *newname);
  extern void AlterTSTemplateNamespace(List *name, const char *newschema);
  extern Oid AlterTSTemplateNamespace_oid(Oid tmplId, Oid newNspOid);
+ extern void AlterTSTemplateExtension(List *name, const char *extname);
  extern void RemoveTSTemplates(DropStmt *stmt);
  extern void RemoveTSTemplateById(Oid tmplId);
  
***************
*** 146,151 **** extern void RemoveTSConfigurationById(Oid cfgId);
--- 154,160 ----
  extern void AlterTSConfiguration(AlterTSConfigurationStmt *stmt);
  extern void AlterTSConfigurationOwner(List *name, Oid newOwnerId);
  extern void AlterTSConfigurationNamespace(List *name, const char *newschema);
+ extern void AlterTSConfigurationExtension(List *name, const char *extname);
  
  extern text *serialize_deflist(List *deflist);
  extern List *deserialize_deflist(Datum txt);
*** a/src/include/commands/extension.h
--- b/src/include/commands/extension.h
***************
*** 40,45 **** typedef struct ExtensionControlFile
--- 40,46 ----
  	char *comment;      /* the control file can also contain a comment */
  	int   encoding;     /* encoding of the given script */
  	bool  relocatable;  /* when true, ALTER EXTENSION SET SCHEMA is supported */
+ 	char *upgrade;      /* filename of the upgrade script to execute, or NULL */
  } ExtensionControlFile;
  
  /* expansible list of extensions */
***************
*** 59,71 **** typedef struct extension_objects_fctx
--- 60,76 ----
  
  extern void CreateExtension(CreateExtensionStmt *stmt);
  extern void DropExtension(DropExtensionStmt *stmt);
+ extern void UpgradeExtension(AlterExtensionStmt *stmt);
  
  extern Oid get_extension_oid(const char *extname, bool missing_ok);
  extern char *get_extension_name(Oid ext_oid);
+ extern char *get_extension_version(Oid ext_oid);
  extern bool extension_relocatable_p(Oid ext_oid);
  extern void RemoveExtensionById(Oid extId);
  extern void AlterExtensionNamespace(List *name, const char *newschema);
  extern void AlterExtensionNamespace_oid(Oid extensionOid, Oid nspOid);
+ extern void AlterObjectExtension(const char *extname,
+ 								 Oid classId, Oid objectId, Oid objectSubId);
  
  
  #endif   /* EXTENSION_H */
*** a/src/include/commands/tablecmds.h
--- b/src/include/commands/tablecmds.h
***************
*** 38,43 **** extern Oid AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
--- 38,45 ----
  							   Oid oldNspOid, Oid newNspOid,
  							   bool hasDependEntry);
  
+ extern void AlterTableExtension(RangeVar *relation, const char *extname);
+ 
  extern void CheckTableNotInUse(Relation rel, const char *stmt);
  
  extern void ExecuteTruncate(TruncateStmt *stmt);
*** a/src/include/commands/typecmds.h
--- b/src/include/commands/typecmds.h
***************
*** 45,49 **** extern Oid AlterTypeNamespace_oid(Oid typeOid, Oid nspOid);
--- 45,50 ----
  extern Oid AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
  						   bool isImplicitArray,
  						   bool errorOnTableType);
+ extern void AlterTypeExtension(List *names, const char *extname);
  
  #endif   /* TYPECMDS_H */
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
***************
*** 334,339 **** typedef enum NodeTag
--- 334,340 ----
  	T_CreateTableSpaceStmt,
  	T_DropTableSpaceStmt,
  	T_AlterObjectSchemaStmt,
+ 	T_AlterObjectExtensionStmt,
  	T_AlterOwnerStmt,
  	T_DropOwnedStmt,
  	T_ReassignOwnedStmt,
***************
*** 355,360 **** typedef enum NodeTag
--- 356,362 ----
  	T_SecLabelStmt,
  	T_CreateExtensionStmt,
  	T_DropExtensionStmt,
+ 	T_AlterExtensionStmt,
  
  	/*
  	 * TAGS FOR PARSE TREE NODES (parsenodes.h)
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
***************
*** 1499,1504 **** typedef struct CreateExtensionStmt
--- 1499,1505 ----
  {
  	NodeTag		type;
  	char	   *extname;
+ 	bool        wrapper;        /* Create a wrapper extension, no script */
  	List	   *options;		/* List of DefElem nodes */
  } CreateExtensionStmt;
  
***************
*** 1510,1515 **** typedef struct DropExtensionStmt
--- 1511,1522 ----
  	DropBehavior behavior;		/* drop behavior - cascade/restrict */
  } DropExtensionStmt;
  
+ typedef struct AlterExtensionStmt
+ {
+ 	NodeTag		type;
+ 	char	    *extname;
+ } AlterExtensionStmt;
+ 
  /* ----------------------
   *		Create/Drop Table Space Statements
   * ----------------------
***************
*** 2114,2119 **** typedef struct AlterObjectSchemaStmt
--- 2121,2141 ----
  } AlterObjectSchemaStmt;
  
  /* ----------------------
+  *		ALTER object SET EXTENSION Statement
+  * ----------------------
+  */
+ typedef struct AlterObjectExtensionStmt
+ {
+ 	NodeTag		type;
+ 	ObjectType objectType;		/* OBJECT_TABLE, OBJECT_TYPE, etc */
+ 	RangeVar   *relation;		/* in case it's a table */
+ 	List	   *object;			/* in case it's some other object */
+ 	List	   *objarg;			/* argument types, if applicable */
+ 	char	   *addname;		/* additional name if needed */
+ 	char	   *extname;		/* the target extension */
+ } AlterObjectExtensionStmt;
+ 
+ /* ----------------------
   *		Alter Object Owner Statement
   * ----------------------
   */
*** a/src/include/parser/kwlist.h
--- b/src/include/parser/kwlist.h
***************
*** 391,396 **** PG_KEYWORD("unknown", UNKNOWN, UNRESERVED_KEYWORD)
--- 391,397 ----
  PG_KEYWORD("unlisten", UNLISTEN, UNRESERVED_KEYWORD)
  PG_KEYWORD("until", UNTIL, UNRESERVED_KEYWORD)
  PG_KEYWORD("update", UPDATE, UNRESERVED_KEYWORD)
+ PG_KEYWORD("upgrade", UPGRADE, UNRESERVED_KEYWORD)
  PG_KEYWORD("user", USER, RESERVED_KEYWORD)
  PG_KEYWORD("using", USING, RESERVED_KEYWORD)
  PG_KEYWORD("vacuum", VACUUM, UNRESERVED_KEYWORD)
