diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index df49a78..59f9e21 100644
*** a/src/backend/commands/extension.c
--- b/src/backend/commands/extension.c
*************** typedef struct ExtensionVersionInfo
*** 100,113 ****
  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);
  static char *read_whole_file(const char *filename, int *length);
  
  
--- 100,124 ----
  static List *find_update_path(List *evi_list,
  				 ExtensionVersionInfo *evi_start,
  				 ExtensionVersionInfo *evi_target,
+ 				 bool reject_indirect,
  				 bool reinitialize);
+ static Oid get_required_extension(char *reqExtensionName,
+ 					   char *extensionName,
+ 					   char *origSchemaName,
+ 					   bool cascade,
+ 					   List *parents,
+ 					   bool is_create);
  static void get_available_versions_for_extension(ExtensionControlFile *pcontrol,
  									 Tuplestorestate *tupstore,
  									 TupleDesc tupdesc);
+ static Datum convert_requires_to_datum(List *requires);
  static void ApplyExtensionUpdates(Oid extensionOid,
  					  ExtensionControlFile *pcontrol,
  					  const char *initialVersion,
! 					  List *updateVersions,
! 					  char *origSchemaName,
! 					  bool cascade,
! 					  bool is_create);
  static char *read_whole_file(const char *filename, int *length);
  
  
*************** identify_update_path(ExtensionControlFil
*** 1071,1077 ****
  	evi_target = get_ext_ver_info(newVersion, &evi_list);
  
  	/* Find shortest path */
! 	result = find_update_path(evi_list, evi_start, evi_target, false);
  
  	if (result == NIL)
  		ereport(ERROR,
--- 1082,1088 ----
  	evi_target = get_ext_ver_info(newVersion, &evi_list);
  
  	/* Find shortest path */
! 	result = find_update_path(evi_list, evi_start, evi_target, false, false);
  
  	if (result == NIL)
  		ereport(ERROR,
*************** identify_update_path(ExtensionControlFil
*** 1086,1091 ****
--- 1097,1105 ----
   * Apply Dijkstra's algorithm to find the shortest path from evi_start to
   * evi_target.
   *
+  * If reject_indirect is true, ignore paths that go through installable
+  * versions (presumably, caller will consider starting from such versions).
+  *
   * If reinitialize is false, assume the ExtensionVersionInfo list has not
   * been used for this before, and the initialization done by get_ext_ver_info
   * is still good.
*************** static List *
*** 1097,1102 ****
--- 1111,1117 ----
  find_update_path(List *evi_list,
  				 ExtensionVersionInfo *evi_start,
  				 ExtensionVersionInfo *evi_target,
+ 				 bool reject_indirect,
  				 bool reinitialize)
  {
  	List	   *result;
*************** find_update_path(List *evi_list,
*** 1105,1110 ****
--- 1120,1127 ----
  
  	/* Caller error if start == target */
  	Assert(evi_start != evi_target);
+ 	/* Caller error if reject_indirect and target is installable */
+ 	Assert(!(reject_indirect && evi_target->installable));
  
  	if (reinitialize)
  	{
*************** find_update_path(List *evi_list,
*** 1131,1136 ****
--- 1148,1156 ----
  			ExtensionVersionInfo *evi2 = (ExtensionVersionInfo *) lfirst(lc);
  			int			newdist;
  
+ 			/* if reject_indirect, treat installable versions as unreachable */
+ 			if (reject_indirect && evi2->installable)
+ 				continue;
  			newdist = evi->distance + 1;
  			if (newdist < evi2->distance)
  			{
*************** find_update_path(List *evi_list,
*** 1167,1172 ****
--- 1187,1249 ----
  }
  
  /*
+  * Given a target version that is not directly installable, find the
+  * best installation sequence starting from a directly-installable version.
+  *
+  * evi_list: previously-collected version update graph
+  * evi_target: member of that list that we want to reach
+  *
+  * Returns the best starting-point version, or NULL if there is none.
+  * On success, *best_path is set to the path from the start point.
+  *
+  * If there's more than one possible start point, prefer shorter update paths,
+  * and break any ties arbitrarily on the basis of strcmp'ing the starting
+  * versions' names.
+  */
+ static ExtensionVersionInfo *
+ find_install_path(List *evi_list, ExtensionVersionInfo *evi_target,
+ 				  List **best_path)
+ {
+ 	ExtensionVersionInfo *evi_start = NULL;
+ 	ListCell   *lc;
+ 
+ 	/* Target should not be installable */
+ 	Assert(!evi_target->installable);
+ 
+ 	*best_path = NIL;
+ 
+ 	/* Consider all installable versions as start points */
+ 	foreach(lc, evi_list)
+ 	{
+ 		ExtensionVersionInfo *evi1 = (ExtensionVersionInfo *) lfirst(lc);
+ 		List	   *path;
+ 
+ 		if (!evi1->installable)
+ 			continue;
+ 
+ 		/*
+ 		 * Find shortest path from evi1 to evi_target; but no need to consider
+ 		 * paths going through other installable versions.
+ 		 */
+ 		path = find_update_path(evi_list, evi1, evi_target, true, true);
+ 		if (path == NIL)
+ 			continue;
+ 
+ 		/* Remember best path */
+ 		if (evi_start == NULL ||
+ 			list_length(path) < list_length(*best_path) ||
+ 			(list_length(path) == list_length(*best_path) &&
+ 			 strcmp(evi_start->name, evi1->name) < 0))
+ 		{
+ 			evi_start = evi1;
+ 			*best_path = path;
+ 		}
+ 	}
+ 
+ 	return evi_start;
+ }
+ 
+ /*
   * CREATE EXTENSION worker
   *
   * When CASCADE is specified, CreateExtensionInternal() recurses if required
*************** find_update_path(List *evi_list,
*** 1175,1191 ****
   * installed, allowing us to error out if we recurse to one of those.
   */
  static ObjectAddress
! CreateExtensionInternal(ParseState *pstate, CreateExtensionStmt *stmt, List *parents)
  {
! 	DefElem    *d_schema = NULL;
! 	DefElem    *d_new_version = NULL;
! 	DefElem    *d_old_version = NULL;
! 	DefElem    *d_cascade = NULL;
! 	char	   *schemaName = NULL;
  	Oid			schemaOid = InvalidOid;
- 	char	   *versionName;
- 	char	   *oldVersionName;
- 	bool		cascade = false;
  	Oid			extowner = GetUserId();
  	ExtensionControlFile *pcontrol;
  	ExtensionControlFile *control;
--- 1252,1267 ----
   * installed, allowing us to error out if we recurse to one of those.
   */
  static ObjectAddress
! CreateExtensionInternal(char *extensionName,
! 						char *schemaName,
! 						char *versionName,
! 						char *oldVersionName,
! 						bool cascade,
! 						List *parents,
! 						bool is_create)
  {
! 	char	   *origSchemaName = schemaName;
  	Oid			schemaOid = InvalidOid;
  	Oid			extowner = GetUserId();
  	ExtensionControlFile *pcontrol;
  	ExtensionControlFile *control;
*************** CreateExtensionInternal(ParseState *psta
*** 1193,1279 ****
  	List	   *requiredExtensions;
  	List	   *requiredSchemas;
  	Oid			extensionOid;
- 	ListCell   *lc;
  	ObjectAddress address;
  
  	/*
  	 * Read the primary control file.  Note we assume that it does not contain
  	 * 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
! 	 */
! 	foreach(lc, stmt->options)
! 	{
! 		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"),
! 						 parser_errposition(pstate, defel->location)));
! 			d_schema = defel;
! 		}
! 		else if (strcmp(defel->defname, "new_version") == 0)
! 		{
! 			if (d_new_version)
! 				ereport(ERROR,
! 						(errcode(ERRCODE_SYNTAX_ERROR),
! 						 errmsg("conflicting or redundant options"),
! 						 parser_errposition(pstate, defel->location)));
! 			d_new_version = defel;
! 		}
! 		else if (strcmp(defel->defname, "old_version") == 0)
! 		{
! 			if (d_old_version)
! 				ereport(ERROR,
! 						(errcode(ERRCODE_SYNTAX_ERROR),
! 						 errmsg("conflicting or redundant options"),
! 						 parser_errposition(pstate, defel->location)));
! 			d_old_version = defel;
! 		}
! 		else if (strcmp(defel->defname, "cascade") == 0)
! 		{
! 			if (d_cascade)
! 				ereport(ERROR,
! 						(errcode(ERRCODE_SYNTAX_ERROR),
! 						 errmsg("conflicting or redundant options"),
! 						 parser_errposition(pstate, defel->location)));
! 			d_cascade = defel;
! 			cascade = defGetBoolean(d_cascade);
! 		}
! 		else
! 			elog(ERROR, "unrecognized option: %s", defel->defname);
! 	}
  
  	/*
  	 * Determine the version to install
  	 */
! 	if (d_new_version && d_new_version->arg)
! 		versionName = strVal(d_new_version->arg);
! 	else if (pcontrol->default_version)
! 		versionName = pcontrol->default_version;
! 	else
  	{
! 		ereport(ERROR,
! 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! 				 errmsg("version to install must be specified")));
! 		versionName = NULL;		/* keep compiler quiet */
  	}
  	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)
--- 1269,1311 ----
  	List	   *requiredExtensions;
  	List	   *requiredSchemas;
  	Oid			extensionOid;
  	ObjectAddress address;
+ 	ListCell   *lc;
  
  	/*
  	 * Read the primary control file.  Note we assume that it does not contain
  	 * any non-ASCII data, so there is no need to worry about encoding at this
  	 * point.
  	 */
! 	pcontrol = read_extension_control_file(extensionName);
  
  	/*
  	 * Determine the version to install
  	 */
! 	if (versionName == NULL)
  	{
! 		if (pcontrol->default_version)
! 			versionName = pcontrol->default_version;
! 		else
! 			ereport(ERROR,
! 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! 					 errmsg("version to install must be specified")));
  	}
  	check_valid_version_name(versionName);
  
  	/*
! 	 * Figure out which script(s) we need to run to install the desired
! 	 * version of the extension.  If we do not have a script that directly
! 	 * does what is needed, we try to find a sequence of update scripts that
! 	 * will get us there.
  	 */
! 	if (oldVersionName)
  	{
! 		/*
! 		 * "FROM old_version" was specified, indicating that we're trying to
! 		 * update from some unpackaged version of the extension.  Locate a
! 		 * series of update scripts that will do it.
! 		 */
  		check_valid_version_name(oldVersionName);
  
  		if (strcmp(oldVersionName, versionName) == 0)
*************** CreateExtensionInternal(ParseState *psta
*** 1308,1315 ****
  	}
  	else
  	{
  		oldVersionName = NULL;
! 		updateVersions = NIL;
  	}
  
  	/*
--- 1340,1400 ----
  	}
  	else
  	{
+ 		/*
+ 		 * No FROM, so we're installing from scratch.  If there is an install
+ 		 * script for the desired version, we only need to run that one.
+ 		 */
+ 		char	   *filename;
+ 		struct stat fst;
+ 
  		oldVersionName = NULL;
! 
! 		filename = get_extension_script_filename(pcontrol, NULL, versionName);
! 		if (stat(filename, &fst) == 0)
! 		{
! 			/* Easy, no extra scripts */
! 			updateVersions = NIL;
! 		}
! 		else
! 		{
! 			/* Look for best way to install this version */
! 			List	   *evi_list;
! 			ExtensionVersionInfo *evi_target;
! 
! 			/* Extract the version update graph from the script directory */
! 			evi_list = get_ext_ver_list(pcontrol);
! 
! 			/* Identify the target version */
! 			evi_target = get_ext_ver_info(versionName, &evi_list);
! 
! 			/*
! 			 * We don't expect it to be installable, but maybe somebody added
! 			 * a suitable script right after our stat() test.
! 			 */
! 			if (evi_target->installable)
! 			{
! 				/* Easy, no extra scripts */
! 				updateVersions = NIL;
! 			}
! 			else
! 			{
! 				/* Identify best path to reach target */
! 				ExtensionVersionInfo *evi_start;
! 
! 				evi_start = find_install_path(evi_list, evi_target,
! 											  &updateVersions);
! 
! 				/* Fail if no path ... */
! 				if (evi_start == NULL)
! 					ereport(ERROR,
! 							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! 							 errmsg("extension \"%s\" has no installation script for version \"%s\"",
! 									pcontrol->name, versionName)));
! 
! 				/* Otherwise, install best starting point and then upgrade */
! 				versionName = evi_start->name;
! 			}
! 		}
  	}
  
  	/*
*************** CreateExtensionInternal(ParseState *psta
*** 1320,1332 ****
  	/*
  	 * Determine the target schema to install the extension into
  	 */
! 	if (d_schema && d_schema->arg)
  	{
- 		/*
- 		 * User given schema, CREATE EXTENSION ... WITH SCHEMA ...
- 		 */
- 		schemaName = strVal(d_schema->arg);
- 
  		/* If the user is giving us the schema name, it must exist already. */
  		schemaOid = get_namespace_oid(schemaName, false);
  	}
--- 1405,1412 ----
  	/*
  	 * Determine the target schema to install the extension into
  	 */
! 	if (schemaName)
  	{
  		/* If the user is giving us the schema name, it must exist already. */
  		schemaOid = get_namespace_oid(schemaName, false);
  	}
*************** CreateExtensionInternal(ParseState *psta
*** 1374,1380 ****
  	else if (!OidIsValid(schemaOid))
  	{
  		/*
! 		 * Neither user nor author of the extension specified schema, use the
  		 * current default creation namespace, which is the first explicit
  		 * entry in the search_path.
  		 */
--- 1454,1460 ----
  	else if (!OidIsValid(schemaOid))
  	{
  		/*
! 		 * Neither user nor author of the extension specified schema; use the
  		 * current default creation namespace, which is the first explicit
  		 * entry in the search_path.
  		 */
*************** CreateExtensionInternal(ParseState *psta
*** 1415,1480 ****
  		Oid			reqext;
  		Oid			reqschema;
  
! 		reqext = get_extension_oid(curreq, true);
! 		if (!OidIsValid(reqext))
! 		{
! 			if (cascade)
! 			{
! 				/* Must install it. */
! 				CreateExtensionStmt *ces;
! 				ListCell   *lc2;
! 				ObjectAddress addr;
! 				List	   *cascade_parents;
! 
! 				/* Check extension name validity before trying to cascade. */
! 				check_valid_extension_name(curreq);
! 
! 				/* Check for cyclic dependency between extensions. */
! 				foreach(lc2, parents)
! 				{
! 					char	   *pname = (char *) lfirst(lc2);
! 
! 					if (strcmp(pname, curreq) == 0)
! 						ereport(ERROR,
! 								(errcode(ERRCODE_INVALID_RECURSION),
! 								 errmsg("cyclic dependency detected between extensions \"%s\" and \"%s\"",
! 										curreq, stmt->extname)));
! 				}
! 
! 				ereport(NOTICE,
! 						(errmsg("installing required extension \"%s\"",
! 								curreq)));
! 
! 				/* Build a CREATE EXTENSION statement to pass down. */
! 				ces = makeNode(CreateExtensionStmt);
! 				ces->extname = curreq;
! 				ces->if_not_exists = false;
! 
! 				/* Propagate the CASCADE option. */
! 				ces->options = list_make1(d_cascade);
! 
! 				/* Propagate the SCHEMA option if given. */
! 				if (d_schema && d_schema->arg)
! 					ces->options = lappend(ces->options, d_schema);
! 
! 				/* Add current extension to list of parents to pass down. */
! 				cascade_parents =
! 					lappend(list_copy(parents), stmt->extname);
! 
! 				/* Create the required extension. */
! 				addr = CreateExtensionInternal(pstate, ces, cascade_parents);
! 
! 				/* Get its newly-assigned OID. */
! 				reqext = addr.objectId;
! 			}
! 			else
! 				ereport(ERROR,
! 						(errcode(ERRCODE_UNDEFINED_OBJECT),
! 						 errmsg("required extension \"%s\" is not installed",
! 								curreq),
! 						 errhint("Use CREATE EXTENSION ... CASCADE to install required extensions too.")));
! 		}
! 
  		reqschema = get_extension_schema(reqext);
  		requiredExtensions = lappend_oid(requiredExtensions, reqext);
  		requiredSchemas = lappend_oid(requiredSchemas, reqschema);
--- 1495,1506 ----
  		Oid			reqext;
  		Oid			reqschema;
  
! 		reqext = get_required_extension(curreq,
! 										extensionName,
! 										origSchemaName,
! 										cascade,
! 										parents,
! 										is_create);
  		reqschema = get_extension_schema(reqext);
  		requiredExtensions = lappend_oid(requiredExtensions, reqext);
  		requiredSchemas = lappend_oid(requiredSchemas, reqschema);
*************** CreateExtensionInternal(ParseState *psta
*** 1510,1526 ****
  	 * though a series of ALTER EXTENSION UPDATE commands were given
  	 */
  	ApplyExtensionUpdates(extensionOid, pcontrol,
! 						  versionName, updateVersions);
  
  	return address;
  }
  
  /*
   * CREATE EXTENSION
   */
  ObjectAddress
  CreateExtension(ParseState *pstate, CreateExtensionStmt *stmt)
  {
  	/* Check extension name validity before any filesystem access */
  	check_valid_extension_name(stmt->extname);
  
--- 1536,1635 ----
  	 * though a series of ALTER EXTENSION UPDATE commands were given
  	 */
  	ApplyExtensionUpdates(extensionOid, pcontrol,
! 						  versionName, updateVersions,
! 						  origSchemaName, cascade, is_create);
  
  	return address;
  }
  
  /*
+  * Get the OID of an extension listed in "requires", possibly creating it.
+  */
+ static Oid
+ get_required_extension(char *reqExtensionName,
+ 					   char *extensionName,
+ 					   char *origSchemaName,
+ 					   bool cascade,
+ 					   List *parents,
+ 					   bool is_create)
+ {
+ 	Oid			reqExtensionOid;
+ 
+ 	reqExtensionOid = get_extension_oid(reqExtensionName, true);
+ 	if (!OidIsValid(reqExtensionOid))
+ 	{
+ 		if (cascade)
+ 		{
+ 			/* Must install it. */
+ 			ObjectAddress addr;
+ 			List	   *cascade_parents;
+ 			ListCell   *lc;
+ 
+ 			/* Check extension name validity before trying to cascade. */
+ 			check_valid_extension_name(reqExtensionName);
+ 
+ 			/* Check for cyclic dependency between extensions. */
+ 			foreach(lc, parents)
+ 			{
+ 				char	   *pname = (char *) lfirst(lc);
+ 
+ 				if (strcmp(pname, reqExtensionName) == 0)
+ 					ereport(ERROR,
+ 							(errcode(ERRCODE_INVALID_RECURSION),
+ 							 errmsg("cyclic dependency detected between extensions \"%s\" and \"%s\"",
+ 									reqExtensionName, extensionName)));
+ 			}
+ 
+ 			ereport(NOTICE,
+ 					(errmsg("installing required extension \"%s\"",
+ 							reqExtensionName)));
+ 
+ 			/* Add current extension to list of parents to pass down. */
+ 			cascade_parents = lappend(list_copy(parents), extensionName);
+ 
+ 			/*
+ 			 * Create the required extension.  We propagate the SCHEMA option
+ 			 * if any, and CASCADE, but no other options.
+ 			 */
+ 			addr = CreateExtensionInternal(reqExtensionName,
+ 										   origSchemaName,
+ 										   NULL,
+ 										   NULL,
+ 										   cascade,
+ 										   cascade_parents,
+ 										   is_create);
+ 
+ 			/* Get its newly-assigned OID. */
+ 			reqExtensionOid = addr.objectId;
+ 		}
+ 		else
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_UNDEFINED_OBJECT),
+ 					 errmsg("required extension \"%s\" is not installed",
+ 							reqExtensionName),
+ 					 is_create ?
+ 					 errhint("Use CREATE EXTENSION ... CASCADE to install required extensions too.") : 0));
+ 	}
+ 
+ 	return reqExtensionOid;
+ }
+ 
+ /*
   * CREATE EXTENSION
   */
  ObjectAddress
  CreateExtension(ParseState *pstate, CreateExtensionStmt *stmt)
  {
+ 	DefElem    *d_schema = NULL;
+ 	DefElem    *d_new_version = NULL;
+ 	DefElem    *d_old_version = NULL;
+ 	DefElem    *d_cascade = NULL;
+ 	char	   *schemaName = NULL;
+ 	char	   *versionName = NULL;
+ 	char	   *oldVersionName = NULL;
+ 	bool		cascade = false;
+ 	ListCell   *lc;
+ 
  	/* Check extension name validity before any filesystem access */
  	check_valid_extension_name(stmt->extname);
  
*************** CreateExtension(ParseState *pstate, Crea
*** 1556,1563 ****
  				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  				 errmsg("nested CREATE EXTENSION is not supported")));
  
! 	/* Finally create the extension. */
! 	return CreateExtensionInternal(pstate, stmt, NIL);
  }
  
  /*
--- 1665,1727 ----
  				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  				 errmsg("nested CREATE EXTENSION is not supported")));
  
! 	/* Deconstruct the statement option list */
! 	foreach(lc, stmt->options)
! 	{
! 		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"),
! 						 parser_errposition(pstate, defel->location)));
! 			d_schema = defel;
! 			schemaName = defGetString(d_schema);
! 		}
! 		else if (strcmp(defel->defname, "new_version") == 0)
! 		{
! 			if (d_new_version)
! 				ereport(ERROR,
! 						(errcode(ERRCODE_SYNTAX_ERROR),
! 						 errmsg("conflicting or redundant options"),
! 						 parser_errposition(pstate, defel->location)));
! 			d_new_version = defel;
! 			versionName = defGetString(d_new_version);
! 		}
! 		else if (strcmp(defel->defname, "old_version") == 0)
! 		{
! 			if (d_old_version)
! 				ereport(ERROR,
! 						(errcode(ERRCODE_SYNTAX_ERROR),
! 						 errmsg("conflicting or redundant options"),
! 						 parser_errposition(pstate, defel->location)));
! 			d_old_version = defel;
! 			oldVersionName = defGetString(d_old_version);
! 		}
! 		else if (strcmp(defel->defname, "cascade") == 0)
! 		{
! 			if (d_cascade)
! 				ereport(ERROR,
! 						(errcode(ERRCODE_SYNTAX_ERROR),
! 						 errmsg("conflicting or redundant options"),
! 						 parser_errposition(pstate, defel->location)));
! 			d_cascade = defel;
! 			cascade = defGetBoolean(d_cascade);
! 		}
! 		else
! 			elog(ERROR, "unrecognized option: %s", defel->defname);
! 	}
! 
! 	/* Call CreateExtensionInternal to do the real work. */
! 	return CreateExtensionInternal(stmt->extname,
! 								   schemaName,
! 								   versionName,
! 								   oldVersionName,
! 								   cascade,
! 								   NIL,
! 								   true);
  }
  
  /*
*************** get_available_versions_for_extension(Ext
*** 1914,1956 ****
  									 Tuplestorestate *tupstore,
  									 TupleDesc tupdesc)
  {
! 	int			extnamelen = strlen(pcontrol->name);
! 	char	   *location;
! 	DIR		   *dir;
! 	struct dirent *de;
  
! 	location = get_extension_script_directory(pcontrol);
! 	dir = AllocateDir(location);
! 	/* 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];
  
! 		/* must be a .sql file ... */
! 		if (!is_extension_script_filename(de->d_name))
! 			continue;
! 
! 		/* ... matching extension name followed by separator */
! 		if (strncmp(de->d_name, pcontrol->name, extnamelen) != 0 ||
! 			de->d_name[extnamelen] != '-' ||
! 			de->d_name[extnamelen + 1] != '-')
! 			continue;
! 
! 		/* extract version name from 'extname--something.sql' filename */
! 		vername = pstrdup(de->d_name + extnamelen + 2);
! 		*strrchr(vername, '.') = '\0';
! 
! 		/* ignore it if it's an update script */
! 		if (strstr(vername, "--"))
  			continue;
  
  		/*
  		 * Fetch parameters for specific version (pcontrol is not changed)
  		 */
! 		control = read_extension_aux_control_file(pcontrol, vername);
  
  		memset(values, 0, sizeof(values));
  		memset(nulls, 0, sizeof(nulls));
--- 2078,2105 ----
  									 Tuplestorestate *tupstore,
  									 TupleDesc tupdesc)
  {
! 	List	   *evi_list;
! 	ListCell   *lc;
  
! 	/* Extract the version update graph from the script directory */
! 	evi_list = get_ext_ver_list(pcontrol);
! 
! 	/* For each installable version ... */
! 	foreach(lc, evi_list)
  	{
+ 		ExtensionVersionInfo *evi = (ExtensionVersionInfo *) lfirst(lc);
  		ExtensionControlFile *control;
  		Datum		values[7];
  		bool		nulls[7];
+ 		ListCell   *lc2;
  
! 		if (!evi->installable)
  			continue;
  
  		/*
  		 * Fetch parameters for specific version (pcontrol is not changed)
  		 */
! 		control = read_extension_aux_control_file(pcontrol, evi->name);
  
  		memset(values, 0, sizeof(values));
  		memset(nulls, 0, sizeof(nulls));
*************** get_available_versions_for_extension(Ext
*** 1959,1965 ****
  		values[0] = DirectFunctionCall1(namein,
  										CStringGetDatum(control->name));
  		/* version */
! 		values[1] = CStringGetTextDatum(vername);
  		/* superuser */
  		values[2] = BoolGetDatum(control->superuser);
  		/* relocatable */
--- 2108,2114 ----
  		values[0] = DirectFunctionCall1(namein,
  										CStringGetDatum(control->name));
  		/* version */
! 		values[1] = CStringGetTextDatum(evi->name);
  		/* superuser */
  		values[2] = BoolGetDatum(control->superuser);
  		/* relocatable */
*************** get_available_versions_for_extension(Ext
*** 1974,2000 ****
  		if (control->requires == NIL)
  			nulls[5] = true;
  		else
! 		{
! 			Datum	   *datums;
! 			int			ndatums;
! 			ArrayType  *a;
! 			ListCell   *lc;
! 
! 			ndatums = list_length(control->requires);
! 			datums = (Datum *) palloc(ndatums * sizeof(Datum));
! 			ndatums = 0;
! 			foreach(lc, control->requires)
! 			{
! 				char	   *curreq = (char *) lfirst(lc);
! 
! 				datums[ndatums++] =
! 					DirectFunctionCall1(namein, CStringGetDatum(curreq));
! 			}
! 			a = construct_array(datums, ndatums,
! 								NAMEOID,
! 								NAMEDATALEN, false, 'c');
! 			values[5] = PointerGetDatum(a);
! 		}
  		/* comment */
  		if (control->comment == NULL)
  			nulls[6] = true;
--- 2123,2129 ----
  		if (control->requires == NIL)
  			nulls[5] = true;
  		else
! 			values[5] = convert_requires_to_datum(control->requires);
  		/* comment */
  		if (control->comment == NULL)
  			nulls[6] = true;
*************** get_available_versions_for_extension(Ext
*** 2002,2010 ****
  			values[6] = CStringGetTextDatum(control->comment);
  
  		tuplestore_putvalues(tupstore, tupdesc, values, nulls);
  	}
  
! 	FreeDir(dir);
  }
  
  /*
--- 2131,2205 ----
  			values[6] = CStringGetTextDatum(control->comment);
  
  		tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ 
+ 		/*
+ 		 * Find all non-directly-installable versions that would be installed
+ 		 * starting from this version, and report them, inheriting the
+ 		 * appropriate parameters from this version.
+ 		 */
+ 		foreach(lc2, evi_list)
+ 		{
+ 			ExtensionVersionInfo *evi2 = (ExtensionVersionInfo *) lfirst(lc2);
+ 			List	   *best_path;
+ 
+ 			if (evi2->installable)
+ 				continue;
+ 			if (find_install_path(evi_list, evi2, &best_path) == evi)
+ 			{
+ 				/*
+ 				 * Fetch parameters for this version (pcontrol is not changed)
+ 				 */
+ 				control = read_extension_aux_control_file(pcontrol, evi2->name);
+ 
+ 				/* name stays the same */
+ 				/* version */
+ 				values[1] = CStringGetTextDatum(evi2->name);
+ 				/* superuser */
+ 				values[2] = BoolGetDatum(control->superuser);
+ 				/* relocatable */
+ 				values[3] = BoolGetDatum(control->relocatable);
+ 				/* schema stays the same */
+ 				/* requires */
+ 				if (control->requires == NIL)
+ 					nulls[5] = true;
+ 				else
+ 				{
+ 					values[5] = convert_requires_to_datum(control->requires);
+ 					nulls[5] = false;
+ 				}
+ 				/* comment stays the same */
+ 
+ 				tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ 			}
+ 		}
  	}
+ }
  
! /*
!  * Convert a list of extension names to a name[] Datum
!  */
! static Datum
! convert_requires_to_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);
  }
  
  /*
*************** pg_extension_update_paths(PG_FUNCTION_AR
*** 2076,2082 ****
  				continue;
  
  			/* Find shortest path from evi1 to evi2 */
! 			path = find_update_path(evi_list, evi1, evi2, true);
  
  			/* Emit result row */
  			memset(values, 0, sizeof(values));
--- 2271,2277 ----
  				continue;
  
  			/* Find shortest path from evi1 to evi2 */
! 			path = find_update_path(evi_list, evi1, evi2, false, true);
  
  			/* Emit result row */
  			memset(values, 0, sizeof(values));
*************** ExecAlterExtensionStmt(ParseState *pstat
*** 2808,2814 ****
  	 * time
  	 */
  	ApplyExtensionUpdates(extensionOid, control,
! 						  oldVersionName, updateVersions);
  
  	ObjectAddressSet(address, ExtensionRelationId, extensionOid);
  
--- 3003,3010 ----
  	 * time
  	 */
  	ApplyExtensionUpdates(extensionOid, control,
! 						  oldVersionName, updateVersions,
! 						  NULL, false, false);
  
  	ObjectAddressSet(address, ExtensionRelationId, extensionOid);
  
*************** static void
*** 2827,2833 ****
  ApplyExtensionUpdates(Oid extensionOid,
  					  ExtensionControlFile *pcontrol,
  					  const char *initialVersion,
! 					  List *updateVersions)
  {
  	const char *oldVersionName = initialVersion;
  	ListCell   *lcv;
--- 3023,3032 ----
  ApplyExtensionUpdates(Oid extensionOid,
  					  ExtensionControlFile *pcontrol,
  					  const char *initialVersion,
! 					  List *updateVersions,
! 					  char *origSchemaName,
! 					  bool cascade,
! 					  bool is_create)
  {
  	const char *oldVersionName = initialVersion;
  	ListCell   *lcv;
*************** ApplyExtensionUpdates(Oid extensionOid,
*** 2906,2913 ****
  		heap_close(extRel, RowExclusiveLock);
  
  		/*
! 		 * Look up the prerequisite extensions for this version, and build
! 		 * lists of their OIDs and the OIDs of their target schemas.
  		 */
  		requiredExtensions = NIL;
  		requiredSchemas = NIL;
--- 3105,3113 ----
  		heap_close(extRel, RowExclusiveLock);
  
  		/*
! 		 * Look up the prerequisite extensions for this version, install them
! 		 * if necessary, and build lists of their OIDs and the OIDs of their
! 		 * target schemas.
  		 */
  		requiredExtensions = NIL;
  		requiredSchemas = NIL;
*************** ApplyExtensionUpdates(Oid extensionOid,
*** 2917,2932 ****
  			Oid			reqext;
  			Oid			reqschema;
  
! 			/*
! 			 * We intentionally don't use get_extension_oid's default error
! 			 * message here, because it would be confusing in this context.
! 			 */
! 			reqext = get_extension_oid(curreq, true);
! 			if (!OidIsValid(reqext))
! 				ereport(ERROR,
! 						(errcode(ERRCODE_UNDEFINED_OBJECT),
! 						 errmsg("required extension \"%s\" is not installed",
! 								curreq)));
  			reqschema = get_extension_schema(reqext);
  			requiredExtensions = lappend_oid(requiredExtensions, reqext);
  			requiredSchemas = lappend_oid(requiredSchemas, reqschema);
--- 3117,3128 ----
  			Oid			reqext;
  			Oid			reqschema;
  
! 			reqext = get_required_extension(curreq,
! 											control->name,
! 											origSchemaName,
! 											cascade,
! 											NIL,
! 											is_create);
  			reqschema = get_extension_schema(reqext);
  			requiredExtensions = lappend_oid(requiredExtensions, reqext);
  			requiredSchemas = lappend_oid(requiredSchemas, reqschema);
