Hi,

Again, thanks very much for the review.  Here's an updated patch (just
merged against master) fixing most of your comments here.  I couldn't
reproduce previous problems with the attached:

 - DROP EXTENSION was broken, asking to cascade to self
 - CREATE EXTENSION was bypassing "requires"

I could reproduce the second problem then fix it with the following one
liner. I missed it because my test case still fails for not finding the
cube type rather than the cube extension without this fix:

-       if (!OidIsValid(featoid) && !missing_ok)
+       if (!OidIsValid(*featoid) && !missing_ok)

Thank you all for your patience while I was busy elsewhere, it's
definitely not a show stopper in my book :)

dim=# create extension earthdistance;
ERROR:  feature "cube" is not currently provided
HINT:  Please install an extension that provides it first

dim=# create extension cube;
CREATE EXTENSION

dim=# create extension earthdistance;
CREATE EXTENSION

dim=# drop extension cube cascade;
NOTICE:  drop cascades to extension earthdistance
DROP EXTENSION

Hitoshi Harada <umi.tan...@gmail.com> writes:
> - There are some mixture of pg_extension_feature and pg_extension_feature"s"

Fixed.

> - The doc says pg_extension_feature"s" has four columns but it's not true.

Well the SGML table describing the catalog has 4 cols :)

> - Line 608 is bad. In the loop, provides_itself is repeatedly changed
> to true and false and I guess that's not what you meant.

Fixed.

> - Line 854+, you can fold two blocks into one.  The two blocks are
> similar and by giving provides list with list_make1 when
> control->provides  == NIL you can do it in one block.

Fixed.

> - s/trak/track/

Fixed, I guess the English would need rephrasing.

> - Line 960, you missed updating classId for dependency.

I don't think so.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr     PostgreSQL : Expertise, Formation et Support

diff --git a/contrib/pg_upgrade_support/pg_upgrade_support.c b/contrib/pg_upgrade_support/pg_upgrade_support.c
index 472f152..b5633d4 100644
--- a/contrib/pg_upgrade_support/pg_upgrade_support.c
+++ b/contrib/pg_upgrade_support/pg_upgrade_support.c
@@ -151,6 +151,7 @@ create_empty_extension(PG_FUNCTION_ARGS)
 	Datum		extConfig;
 	Datum		extCondition;
 	List	   *requiredExtensions;
+	List       *features = NIL;	/* FIXME, get features from catalogs */
 
 	if (PG_ARGISNULL(4))
 		extConfig = PointerGetDatum(NULL);
@@ -190,7 +191,8 @@ create_empty_extension(PG_FUNCTION_ARGS)
 						 text_to_cstring(extVersion),
 						 extConfig,
 						 extCondition,
-						 requiredExtensions);
+						 requiredExtensions,
+						 features);
 
 	PG_RETURN_VOID();
 }
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 9564e01..bf7dd74 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -149,6 +149,11 @@
      </row>
 
      <row>
+      <entry><link linkend="catalog-pg-extension"><structname>pg_extension_feature</structname></link></entry>
+      <entry>features provided by installed extensions</entry>
+     </row>
+
+     <row>
       <entry><link linkend="catalog-pg-foreign-data-wrapper"><structname>pg_foreign_data_wrapper</structname></link></entry>
       <entry>foreign-data wrapper definitions</entry>
      </row>
@@ -3058,6 +3063,51 @@
   </para>
  </sect1>
 
+ <sect1 id="catalog-pg-extension-feature">
+  <title><structname>pg_extension_feature</structname></title>
+
+  <indexterm zone="catalog-pg-extension-feature">
+   <primary>pg_extension_feature</primary>
+  </indexterm>
+
+  <para>
+   The catalog <structname>pg_extension_feature</structname> stores
+   information about the features provided by installed extensions.
+   See <xref linkend="extend-extensions"> for details about extensions.
+  </para>
+
+  <table>
+   <title><structname>pg_extension_feature</> Columns</title>
+
+   <tgroup cols="4">
+    <thead>
+     <row>
+      <entry>Name</entry>
+      <entry>Type</entry>
+      <entry>References</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+    <tbody>
+     <row>
+      <entry><structfield>extoid</structfield></entry>
+      <entry><type>oid</type></entry>
+      <entry><literal><link linkend="catalog-pg-extension"><structname>pg_extension</structname></link>.oid</literal></entry>
+      <entry>Oid of the extension that provides this feature</entry>
+     </row>
+
+     <row>
+      <entry><structfield>extfeature</structfield></entry>
+      <entry><type>name</type></entry>
+      <entry></entry>
+      <entry>Name of the feature</entry>
+     </row>
+
+    </tbody>
+   </tgroup>
+  </table>
+ </sect1>
 
  <sect1 id="catalog-pg-foreign-data-wrapper">
   <title><structname>pg_foreign_data_wrapper</structname></title>
@@ -6827,11 +6877,17 @@
      <row>
       <entry><structfield>requires</structfield></entry>
       <entry><type>name[]</type></entry>
-      <entry>Names of prerequisite extensions,
+      <entry>Names of prerequisite features,
        or <literal>NULL</literal> if none</entry>
      </row>
 
      <row>
+      <entry><structfield>provides</structfield></entry>
+      <entry><type>name[]</type></entry>
+      <entry>Names of provided features</entry>
+     </row>
+
+     <row>
       <entry><structfield>comment</structfield></entry>
       <entry><type>text</type></entry>
       <entry>Comment string from the extension's control file</entry>
diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgml
index 8d5b9d0..af5fc4c 100644
--- a/doc/src/sgml/extend.sgml
+++ b/doc/src/sgml/extend.sgml
@@ -463,9 +463,30 @@
       <term><varname>requires</varname> (<type>string</type>)</term>
       <listitem>
        <para>
-        A list of names of extensions that this extension depends on,
-        for example <literal>requires = 'foo, bar'</literal>.  Those
-        extensions must be installed before this one can be installed.
+        A list of features that this extension depends on, for
+        example <literal>requires = 'foo, bar'</literal>. Those features
+        must be provided by an already installed extension before this one
+        can be installed.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><varname>provides</varname> (<type>string</type>)</term>
+      <listitem>
+       <para>
+        A list of names of features that this extension provides, for
+        example <literal>provides = 'foo, extname_bugfix_12345'</literal>.
+        Those features can help providing finer dependencies: when updating
+        an existing extension you can add new features in this list so that
+        it's possible to depend on those new features. It also makes it
+        possible to deprecate features that an extension would no longer
+        provide.
+       </para>
+       <para>
+        The extension's name itself is always considered a member of
+        the <literal>provides</literal> list, so that you can entirely omit
+        this parameter.
        </para>
       </listitem>
      </varlistentry>
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 5a4419d..fec3e0d 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -36,7 +36,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
 	pg_database.h pg_db_role_setting.h pg_tablespace.h pg_pltemplate.h \
 	pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \
 	pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \
-	pg_ts_parser.h pg_ts_template.h pg_extension.h \
+	pg_ts_parser.h pg_ts_template.h pg_extension.h pg_extension_feature.h \
 	pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
 	pg_foreign_table.h \
 	pg_default_acl.h pg_seclabel.h pg_shseclabel.h pg_collation.h pg_range.h \
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index fed724c..04b2fb9 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -35,6 +35,7 @@
 #include "catalog/pg_default_acl.h"
 #include "catalog/pg_depend.h"
 #include "catalog/pg_extension.h"
+#include "catalog/pg_extension_feature.h"
 #include "catalog/pg_foreign_data_wrapper.h"
 #include "catalog/pg_foreign_server.h"
 #include "catalog/pg_language.h"
@@ -158,7 +159,8 @@ static const Oid object_classes[MAX_OCLASS] = {
 	ForeignServerRelationId,	/* OCLASS_FOREIGN_SERVER */
 	UserMappingRelationId,		/* OCLASS_USER_MAPPING */
 	DefaultAclRelationId,		/* OCLASS_DEFACL */
-	ExtensionRelationId			/* OCLASS_EXTENSION */
+	ExtensionRelationId,		/* OCLASS_EXTENSION */
+	ExtensionFeatureRelationId	/* OCLASS_EXTENSION_FEATURE */
 };
 
 
@@ -1205,6 +1207,10 @@ doDeletion(const ObjectAddress *object)
 			RemoveExtensionById(object->objectId);
 			break;
 
+		case OCLASS_EXTENSION_FEATURE:
+			RemoveExtensionFeatureById(object->objectId);
+			break;
+
 		default:
 			elog(ERROR, "unrecognized object class: %u",
 				 object->classId);
@@ -2248,6 +2254,9 @@ getObjectClass(const ObjectAddress *object)
 
 		case ExtensionRelationId:
 			return OCLASS_EXTENSION;
+
+		case ExtensionFeatureRelationId:
+			return OCLASS_EXTENSION_FEATURE;
 	}
 
 	/* shouldn't get here */
@@ -2882,6 +2891,18 @@ getObjectDescription(const ObjectAddress *object)
 				break;
 			}
 
+		case OCLASS_EXTENSION_FEATURE:
+			{
+				char	   *feature;
+
+				feature = get_extension_feature_name(object->objectId);
+				if (!feature)
+					elog(ERROR, "cache lookup failed for extension feature %u",
+						 object->objectId);
+				appendStringInfo(&buffer, _("extension feature %s"), feature);
+				break;
+			}
+
 		default:
 			appendStringInfo(&buffer, "unrecognized object %u %u %d",
 							 object->classId,
diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c
index 843f03d..3085866 100644
--- a/src/backend/catalog/pg_depend.c
+++ b/src/backend/catalog/pg_depend.c
@@ -281,6 +281,53 @@ deleteDependencyRecordsForClass(Oid classId, Oid objectId,
 }
 
 /*
+ * deleteDependencyRefRecordsForClass -- delete all records with given dependee
+ * classId/objectId, depender classId, and deptype.
+ * Returns the number of records deleted.
+ */
+long
+deleteDependencyRefRecordsForClass(Oid refclassId, Oid refobjectId,
+								   Oid classId, char deptype)
+{
+	long		count = 0;
+	Relation	depRel;
+	ScanKeyData key[2];
+	SysScanDesc scan;
+	HeapTuple	tup;
+
+	depRel = heap_open(DependRelationId, RowExclusiveLock);
+
+	ScanKeyInit(&key[0],
+				Anum_pg_depend_refclassid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(refclassId));
+	ScanKeyInit(&key[1],
+				Anum_pg_depend_refobjid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(refobjectId));
+
+	scan = systable_beginscan(depRel, DependReferenceIndexId, true,
+							  SnapshotNow, 2, key);
+
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
+
+		if (depform->classid == classId && depform->deptype == deptype)
+		{
+			simple_heap_delete(depRel, &tup->t_self);
+			count++;
+		}
+	}
+
+	systable_endscan(scan);
+
+	heap_close(depRel, RowExclusiveLock);
+
+	return count;
+}
+
+/*
  * Adjust dependency record(s) to point to a different object of the same type
  *
  * classId/objectId specify the referencing object.
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index ab594eb..9736490 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -186,7 +186,7 @@ CREATE VIEW pg_available_extensions AS
 
 CREATE VIEW pg_available_extension_versions AS
     SELECT E.name, E.version, (X.extname IS NOT NULL) AS installed,
-           E.superuser, E.relocatable, E.schema, E.requires, E.comment
+           E.superuser, E.relocatable, E.schema, E.requires, E.provides, E.comment
       FROM pg_available_extension_versions() AS E
            LEFT JOIN pg_extension AS X
              ON E.name = X.extname AND E.version = X.extversion;
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index 732791c..8ec3e18 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -36,6 +36,7 @@
 #include "catalog/pg_collation.h"
 #include "catalog/pg_depend.h"
 #include "catalog/pg_extension.h"
+#include "catalog/pg_extension_feature.h"
 #include "catalog/pg_namespace.h"
 #include "catalog/pg_type.h"
 #include "commands/alter.h"
@@ -73,6 +74,7 @@ typedef struct ExtensionControlFile
 	bool		superuser;		/* must be superuser to install? */
 	int			encoding;		/* encoding of the script file, or -1 */
 	List	   *requires;		/* names of prerequisite extensions */
+	List	   *provides;		/* names of provided features */
 } ExtensionControlFile;
 
 /*
@@ -89,6 +91,27 @@ typedef struct ExtensionVersionInfo
 	struct ExtensionVersionInfo *previous;		/* current best predecessor */
 } ExtensionVersionInfo;
 
+/*
+ * Data Structure to handle upgrading of extension features dependencies, and
+ * allow manage features that didn't change, added ones and removed ones.
+ */
+struct feature
+{
+	Oid   oid;				/* feature oid */
+	char *name;				/* feature name */
+	int   count;			/* feature usage count */
+};
+
+/* bsearch function to compare string and struct feature by name */
+static int
+cmpfeatname(const void *a, const void *b)
+{
+	char  *p = (char *) a;
+	struct feature *f = (struct feature *) b;
+
+	return strcmp(p, f->name);
+}
+
 /* Local functions */
 static List *find_update_path(List *evi_list,
 				 ExtensionVersionInfo *evi_start,
@@ -101,7 +124,10 @@ static void ApplyExtensionUpdates(Oid extensionOid,
 					  ExtensionControlFile *pcontrol,
 					  const char *initialVersion,
 					  List *updateVersions);
-
+static void insert_extension_features(const char *extName, ObjectAddress myself,
+									  List *features);
+static void insert_extension_feature(Relation rel, ObjectAddress myself,
+									 const char *feature);
 
 /*
  * get_extension_oid - given an extension name, look up the OID
@@ -228,6 +254,80 @@ get_extension_schema(Oid ext_oid)
 }
 
 /*
+ * Given a feature name, returns its pg_extension_feature oid.
+ */
+static void
+get_extension_feature_oids(const char *feature, bool missing_ok,
+						   Oid *extoid, Oid *featoid)
+{
+	Relation	rel;
+	SysScanDesc scandesc;
+	HeapTuple	tuple;
+	ScanKeyData entry[1];
+
+	rel = heap_open(ExtensionFeatureRelationId, AccessShareLock);
+
+	ScanKeyInit(&entry[0],
+				Anum_pg_extension_feature_extfeature,
+				BTEqualStrategyNumber, F_NAMEEQ,
+				CStringGetDatum(feature));
+
+	scandesc = systable_beginscan(rel, ExtensionFeatureIndexId, true,
+								  SnapshotNow, 1, entry);
+
+	tuple = systable_getnext(scandesc);
+
+	/* We assume that there can be at most one matching tuple */
+	if (HeapTupleIsValid(tuple))
+	{
+		*extoid = ((Form_pg_extension_feature)tuple)->extoid;
+		*featoid = HeapTupleGetOid(tuple);
+	}
+	else
+	{
+		*extoid = InvalidOid;
+		*featoid = InvalidOid;
+	}
+
+	systable_endscan(scandesc);
+
+	heap_close(rel, AccessShareLock);
+
+	if (!OidIsValid(*featoid) && !missing_ok)
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("feature \"%s\" is not currently provided",
+						feature),
+				 errhint("Please install an extension that provides it first")));
+}
+
+/*
+ * Look up the prerequisite extensions, and build lists of their OIDs and
+ * the OIDs of their target schemas.
+ */
+static void
+get_required_extension_features(List *requires,
+								List **requiredFeatures,
+								List **requiredSchemas)
+{
+	ListCell   *lc;
+
+	*requiredFeatures = NIL;
+	*requiredSchemas  = NIL;
+
+	foreach(lc, requires)
+	{
+		char	   *curreq = (char *) lfirst(lc);
+		Oid			reqext, featoid, reqschema;
+
+		get_extension_feature_oids(curreq, false, &reqext, &featoid);
+		reqschema = get_extension_schema(reqext);
+		*requiredFeatures = lappend_oid(*requiredFeatures, featoid);
+		*requiredSchemas = lappend_oid(*requiredSchemas, reqschema);
+	}
+}
+
+/*
  * Utility functions to check validity of extension and version names
  */
 static void
@@ -557,6 +657,21 @@ parse_extension_control_file(ExtensionControlFile *control,
 						item->name)));
 			}
 		}
+		else if (strcmp(item->name, "provides") == 0)
+		{
+			/* Need a modifiable copy of string */
+			char	   *rawnames = pstrdup(item->value);
+
+			/* Parse string into list of identifiers */
+			if (!SplitIdentifierString(rawnames, ',', &control->provides))
+			{
+				/* syntax error in name list */
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("parameter \"%s\" must be a list of extension names",
+						item->name)));
+			}
+		}
 		else
 			ereport(ERROR,
 					(errcode(ERRCODE_SYNTAX_ERROR),
@@ -1186,7 +1301,7 @@ CreateExtension(CreateExtensionStmt *stmt)
 	ExtensionControlFile *pcontrol;
 	ExtensionControlFile *control;
 	List	   *updateVersions;
-	List	   *requiredExtensions;
+	List	   *requiredFeatures;
 	List	   *requiredSchemas;
 	Oid			extensionOid;
 	ListCell   *lc;
@@ -1414,28 +1529,9 @@ CreateExtension(CreateExtensionStmt *stmt)
 	 * Look up the prerequisite extensions, and build lists of their OIDs and
 	 * the OIDs of their target schemas.
 	 */
-	requiredExtensions = NIL;
-	requiredSchemas = NIL;
-	foreach(lc, control->requires)
-	{
-		char	   *curreq = (char *) lfirst(lc);
-		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);
-	}
+	get_required_extension_features(control->requires,
+									&requiredFeatures,
+									&requiredSchemas);
 
 	/*
 	 * Insert new tuple into pg_extension, and create dependency entries.
@@ -1445,7 +1541,8 @@ CreateExtension(CreateExtensionStmt *stmt)
 										versionName,
 										PointerGetDatum(NULL),
 										PointerGetDatum(NULL),
-										requiredExtensions);
+										requiredFeatures,
+										control->provides);
 
 	/*
 	 * Apply any control-file comment on extension
@@ -1486,7 +1583,7 @@ Oid
 InsertExtensionTuple(const char *extName, Oid extOwner,
 					 Oid schemaOid, bool relocatable, const char *extVersion,
 					 Datum extConfig, Datum extCondition,
-					 List *requiredExtensions)
+					 List *requiredFeatures, List *features)
 {
 	Oid			extensionOid;
 	Relation	rel;
@@ -1545,17 +1642,22 @@ InsertExtensionTuple(const char *extName, Oid extOwner,
 
 	recordDependencyOn(&myself, &nsp, DEPENDENCY_NORMAL);
 
-	foreach(lc, requiredExtensions)
+	foreach(lc, requiredFeatures)
 	{
-		Oid			reqext = lfirst_oid(lc);
-		ObjectAddress otherext;
+		Oid			reqfeat = lfirst_oid(lc);
+		ObjectAddress feature;
 
-		otherext.classId = ExtensionRelationId;
-		otherext.objectId = reqext;
-		otherext.objectSubId = 0;
+		feature.classId = ExtensionFeatureRelationId;
+		feature.objectId = reqfeat;
+		feature.objectSubId = 0;
 
-		recordDependencyOn(&myself, &otherext, DEPENDENCY_NORMAL);
+		recordDependencyOn(&myself, &feature, DEPENDENCY_NORMAL);
 	}
+	/*
+	 * Insert extension's features into pg_extension_feature catalog
+	 */
+	insert_extension_features(extName, myself, features);
+
 	/* Post creation hook for new extension */
 	InvokeObjectAccessHook(OAT_POST_CREATE,
 						   ExtensionRelationId, extensionOid, 0, NULL);
@@ -1615,6 +1717,290 @@ RemoveExtensionById(Oid extId)
 }
 
 /*
+ * Get an extension's feature name given its objectId
+ */
+char *
+get_extension_feature_name(Oid featoid)
+{
+	Relation rel;
+	SysScanDesc scandesc;
+	HeapTuple	tuple;
+	ScanKeyData entry[1];
+	char *name;
+
+	rel = heap_open(ExtensionFeatureRelationId, AccessShareLock);
+
+	ScanKeyInit(&entry[0],
+				ObjectIdAttributeNumber,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(featoid));
+
+	scandesc = systable_beginscan(rel, ExtensionFeatureOidIndexId, true,
+								  SnapshotNow, 1, entry);
+
+	tuple = systable_getnext(scandesc);
+
+	/* We assume that there can be at most one matching tuple */
+	if (HeapTupleIsValid(tuple))
+	{
+		Form_pg_extension_feature f = (Form_pg_extension_feature) GETSTRUCT(tuple);
+		name = pstrdup(NameStr(f->extfeature));
+	}
+	else
+		name = NULL;
+
+	systable_endscan(scandesc);
+
+	heap_close(rel, AccessShareLock);
+
+	return name;
+}
+
+/*
+ * Insert provided features into the pg_extension_feature catalog
+ */
+static void
+insert_extension_features(const char *extName, ObjectAddress extObject,
+						  List *features)
+{
+	Relation	rel;
+	ListCell   *lc;
+	bool        provides_itself = false;
+
+	rel = heap_open(ExtensionFeatureRelationId, RowExclusiveLock);
+
+	foreach(lc, features)
+	{
+		char       *feature = (char *) lfirst(lc);
+		insert_extension_feature(rel, extObject, feature);
+
+		provides_itself = provides_itself || (strcmp(feature, extName) == 0);
+	}
+
+	if (!provides_itself)
+		insert_extension_feature(rel, extObject, extName);
+
+	heap_close(rel, RowExclusiveLock);
+}
+
+static void
+insert_extension_feature(Relation rel,
+						 ObjectAddress extObject, const char *feature)
+{
+	Oid			featureOid;
+	Datum		values[Natts_pg_extension_feature];
+	bool		nulls[Natts_pg_extension_feature];
+	HeapTuple	tuple;
+	ObjectAddress myself;
+	Oid			ext, featoid;
+
+	/*
+	 * Build a nice error message when the feature is already installed..
+	 */
+	get_extension_feature_oids(feature, true, &ext, &featoid);
+	if (featoid != InvalidOid)
+		ereport(ERROR,
+				(errcode(ERRCODE_DUPLICATE_OBJECT),
+				 errmsg("extension feature \"%s\" already exists [%u]",
+						feature, featoid)));
+
+	/*
+	 * Build and insert the pg_extension_feature tuple
+	 */
+	memset(values, 0, sizeof(values));
+	memset(nulls, 0, sizeof(nulls));
+
+	values[Anum_pg_extension_feature_extoid - 1] =
+		ObjectIdGetDatum(extObject.objectId);
+	values[Anum_pg_extension_feature_extfeature - 1] =
+		DirectFunctionCall1(namein, CStringGetDatum(feature));
+
+	tuple = heap_form_tuple(rel->rd_att, values, nulls);
+
+	featureOid = simple_heap_insert(rel, tuple);
+	CatalogUpdateIndexes(rel, tuple);
+
+	heap_freetuple(tuple);
+
+	/* handle internal dependencies between the extension tuple and the
+	 * extension's feature tuple
+	 */
+	myself.classId = ExtensionFeatureRelationId;
+	myself.objectId = featureOid;
+	myself.objectSubId = 0;
+
+	recordDependencyOn(&myself, &extObject, DEPENDENCY_INTERNAL);
+}
+
+/* static struct feature * */
+static int
+list_extension_features(Oid extoid, struct feature **features)
+{
+	int nbfeats = 0, size = 10;
+	Relation	rel, irel;
+	SysScanDesc scandesc;
+	HeapTuple	tuple;
+	ScanKeyData entry[1];
+
+	*features = (struct feature *) palloc(size * sizeof(struct feature));
+
+	rel = heap_open(ExtensionFeatureRelationId, AccessShareLock);
+	irel = index_open(ExtensionFeatureIndexId, AccessShareLock);
+
+	ScanKeyInit(&entry[0],
+				Anum_pg_extension_feature_extoid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(extoid));
+
+	scandesc = systable_beginscan_ordered(rel, irel, SnapshotNow, 1, entry);
+
+	while (HeapTupleIsValid(tuple = systable_getnext_ordered(scandesc, ForwardScanDirection)))
+	{
+		Form_pg_extension_feature f = (Form_pg_extension_feature) GETSTRUCT(tuple);
+
+		if (nbfeats == size)
+		{
+			size += 10;
+			*features = repalloc(*features, size);
+		}
+		(*features)[nbfeats].oid   = HeapTupleGetOid(tuple);
+		(*features)[nbfeats].name  = pstrdup(NameStr(f->extfeature));
+		(*features)[nbfeats].count = 0;
+
+		nbfeats++;
+	}
+	systable_endscan_ordered(scandesc);
+
+	index_close(irel, AccessShareLock);
+	heap_close(rel, AccessShareLock);
+
+	return nbfeats;
+}
+
+/*
+ * Care about an extension's provided features changes:
+ *  - do nothing when the feature was already provided
+ *  - add new dependencies when a new feature is provided
+ *  - delete dependencies that are not provided anymore
+ */
+static void
+update_extension_feature_list(ExtensionControlFile *control,
+							  ObjectAddress ext)
+{
+	Relation    rel;
+	struct feature *features;
+    int         nbfeats = list_extension_features(ext.objectId, &features);
+	int         i;
+	ListCell   *lc;
+
+	/*
+	 * Remove all this extension features dependencies, and add them again
+	 * while processing the new "provides" list. That allows to use the
+	 * pg_depend performDeletion() API to implement removing a feature from the
+	 * provide list: we have to skip the extension providing the feature itself
+	 * when following dependencies in DROP_RESTRICT mode.
+	 */
+	i = deleteDependencyRefRecordsForClass(ext.classId, ext.objectId,
+										   ExtensionFeatureRelationId,
+										   DEPENDENCY_INTERNAL);
+
+	/* Have that change visible now, for the performDeletion() call */
+	CommandCounterIncrement();
+
+	rel = heap_open(ExtensionFeatureRelationId, RowExclusiveLock);
+
+	foreach(lc, control->provides)
+	{
+		char  *feat = (char *) lfirst(lc);
+		struct feature *found = NULL;
+
+		found = (struct feature *)
+			bsearch(feat, features, nbfeats, sizeof(struct feature), &cmpfeatname);
+
+		if (found)
+		{
+			if (found->count > 0)
+				ereport(ERROR,
+						(errcode(ERRCODE_SYNTAX_ERROR),
+						 errmsg("conflicting or redundant options \"%s\"",
+								feat)));
+
+			found->count++;
+		}
+		else
+			insert_extension_feature(rel, ext, feat);
+	}
+
+	/* upgrade is done, remove features not provided anymore, and avoid
+	 * removing the extension's name (will not appear in control->provides)
+	 */
+	for(i=0; i < nbfeats; i++)
+	{
+		ObjectAddress feature;
+
+		feature.classId = ExtensionFeatureRelationId;
+		feature.objectId = features[i].oid;
+		feature.objectSubId = 0;
+
+		if (strcmp(features[i].name, control->name) == 0)
+			/*
+			 * The extension's name itself is not in the provide list but still
+			 * provided, we have to care about it separately.
+			 */
+			recordDependencyOn(&feature, &ext, DEPENDENCY_INTERNAL);
+
+		else if (features[i].count == 0)
+			/*
+			 * Drop the extension's feature that is no longer provided, raising
+			 * an error instead if some other extensions are still depending on
+			 * it (control->requires installs pg_depend entries for this case).
+			 */
+			performDeletion(&feature, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
+
+		else if (features[i].count > 0)
+			/*
+			 * Re-install the dependency entry, we removed it only to allow
+			 * using DROP_RESTRICT.
+			 */
+			recordDependencyOn(&feature, &ext, DEPENDENCY_INTERNAL);
+	}
+	heap_close(rel, RowExclusiveLock);
+}
+
+/*
+ * Extension feature's deletion.
+ *
+ * All we need do here is remove the pg_extension_feature tuple itself.
+ */
+void
+RemoveExtensionFeatureById(Oid extFeatId)
+{
+	Relation	rel;
+	SysScanDesc scandesc;
+	HeapTuple	tuple;
+	ScanKeyData entry[1];
+
+	rel = heap_open(ExtensionFeatureRelationId, RowExclusiveLock);
+
+	ScanKeyInit(&entry[0],
+				ObjectIdAttributeNumber,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(extFeatId));
+	scandesc = systable_beginscan(rel, ExtensionFeatureOidIndexId, true,
+								  SnapshotNow, 1, entry);
+
+	tuple = systable_getnext(scandesc);
+
+	/* We assume that there can be at most one matching tuple */
+	if (HeapTupleIsValid(tuple))
+		simple_heap_delete(rel, &tuple->t_self);
+
+	systable_endscan(scandesc);
+
+	heap_close(rel, RowExclusiveLock);
+}
+
+/*
  * This function lists the available extensions (one row per primary control
  * file in the control directory).	We parse each control file and report the
  * interesting fields.
@@ -1836,8 +2222,8 @@ get_available_versions_for_extension(ExtensionControlFile *pcontrol,
 	{
 		ExtensionControlFile *control;
 		char	   *vername;
-		Datum		values[7];
-		bool		nulls[7];
+		Datum		values[8];
+		bool		nulls[8];
 
 		/* must be a .sql file ... */
 		if (!is_extension_script_filename(de->d_name))
@@ -1905,11 +2291,52 @@ get_available_versions_for_extension(ExtensionControlFile *pcontrol,
 								NAMEDATALEN, false, 'c');
 			values[5] = PointerGetDatum(a);
 		}
+		/* provides */
+		nulls[6] = false;
+		/* if (control->provides == NIL) */
+		/* { */
+		/* 	Datum	   *datums; */
+		/* 	ArrayType  *a; */
+
+		/* 	datums = (Datum *) palloc(1 * sizeof(Datum)); */
+		/* 	datums[0] = */
+		/* 		DirectFunctionCall1(namein, CStringGetDatum(pcontrol->name)); */
+		/* 	a = construct_array(datums, 1, */
+		/* 						NAMEOID, */
+		/* 						NAMEDATALEN, false, 'c'); */
+		/* 	values[6] = PointerGetDatum(a); */
+		/* } */
+		/* else */
+		{
+			Datum	   *datums;
+			int			ndatums;
+			ArrayType  *a;
+			ListCell   *lc;
+
+			ndatums = 1 + list_length(control->provides);
+			datums = (Datum *) palloc(ndatums * sizeof(Datum));
+			ndatums = 0;
+			datums[ndatums++] =
+				DirectFunctionCall1(namein, CStringGetDatum(pcontrol->name));
+			foreach(lc, control->provides)
+			{
+				char	   *curreq = (char *) lfirst(lc);
+
+				/* don't add the extension's name more than once in there */
+				if (strcmp(curreq,pcontrol->name) != 0)
+					datums[ndatums++] =
+						DirectFunctionCall1(namein, CStringGetDatum(curreq));
+			}
+			a = construct_array(datums, ndatums,
+								NAMEOID,
+								NAMEDATALEN, false, 'c');
+			values[6] = PointerGetDatum(a);
+		}
 		/* comment */
 		if (control->comment == NULL)
-			nulls[6] = true;
+			nulls[7] = true;
 		else
-			values[6] = CStringGetTextDatum(control->comment);
+			values[7] = CStringGetTextDatum(control->comment);
 
 		tuplestore_putvalues(tupstore, tupdesc, values, nulls);
 	}
@@ -2505,7 +2932,7 @@ ApplyExtensionUpdates(Oid extensionOid,
 		ExtensionControlFile *control;
 		char	   *schemaName;
 		Oid			schemaOid;
-		List	   *requiredExtensions;
+		List	   *requiredFeatures;
 		List	   *requiredSchemas;
 		Relation	extRel;
 		ScanKeyData key[1];
@@ -2516,7 +2943,6 @@ ApplyExtensionUpdates(Oid extensionOid,
 		bool		nulls[Natts_pg_extension];
 		bool		repl[Natts_pg_extension];
 		ObjectAddress myself;
-		ListCell   *lc;
 
 		/*
 		 * Fetch parameters for specific version (pcontrol is not changed)
@@ -2573,58 +2999,21 @@ ApplyExtensionUpdates(Oid extensionOid,
 		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.
+		 * Update extension features list and dependencies
 		 */
-		requiredExtensions = NIL;
-		requiredSchemas = NIL;
-		foreach(lc, control->requires)
-		{
-			char	   *curreq = (char *) lfirst(lc);
-			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);
-		}
-
-		/*
-		 * Remove and recreate dependencies on prerequisite extensions
-		 */
-		deleteDependencyRecordsForClass(ExtensionRelationId, extensionOid,
-										ExtensionRelationId,
-										DEPENDENCY_NORMAL);
-
 		myself.classId = ExtensionRelationId;
 		myself.objectId = extensionOid;
 		myself.objectSubId = 0;
 
-		foreach(lc, requiredExtensions)
-		{
-			Oid			reqext = lfirst_oid(lc);
-			ObjectAddress otherext;
-
-			otherext.classId = ExtensionRelationId;
-			otherext.objectId = reqext;
-			otherext.objectSubId = 0;
-
-			recordDependencyOn(&myself, &otherext, DEPENDENCY_NORMAL);
-		}
+		update_extension_feature_list(control, myself);
 
 		/*
 		 * Finally, execute the update script file
 		 */
+		get_required_extension_features(control->requires,
+										&requiredFeatures,
+										&requiredSchemas);
+
 		execute_extension_script(extensionOid, control,
 								 oldVersionName, versionName,
 								 requiredSchemas,
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 28e68c5..72c357e 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -146,6 +146,7 @@ typedef enum ObjectClass
 	OCLASS_USER_MAPPING,		/* pg_user_mapping */
 	OCLASS_DEFACL,				/* pg_default_acl */
 	OCLASS_EXTENSION,			/* pg_extension */
+	OCLASS_EXTENSION_FEATURE,	/* pg_extension_feature */
 	MAX_OCLASS					/* MUST BE LAST */
 } ObjectClass;
 
@@ -211,6 +212,9 @@ extern long deleteDependencyRecordsFor(Oid classId, Oid objectId,
 extern long deleteDependencyRecordsForClass(Oid classId, Oid objectId,
 								Oid refclassId, char deptype);
 
+extern long deleteDependencyRefRecordsForClass(Oid refclassId, Oid refobjectId,
+											   Oid classId, char deptype);
+
 extern long changeDependencyFor(Oid classId, Oid objectId,
 					Oid refClassId, Oid oldRefObjectId,
 					Oid newRefObjectId);
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 450ec25..4ebad12 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -303,6 +303,12 @@ DECLARE_UNIQUE_INDEX(pg_extension_oid_index, 3080, on pg_extension using btree(o
 DECLARE_UNIQUE_INDEX(pg_extension_name_index, 3081, on pg_extension using btree(extname name_ops));
 #define ExtensionNameIndexId 3081
 
+DECLARE_UNIQUE_INDEX(pg_extension_feature_oid_index, 3180, on pg_extension_feature using btree(oid oid_ops));
+#define ExtensionFeatureOidIndexId 3180
+
+DECLARE_UNIQUE_INDEX(pg_extension_feature_index, 3181, on pg_extension_feature using btree(extoid oid_ops, extfeature name_ops));
+#define ExtensionFeatureIndexId 3181
+
 DECLARE_UNIQUE_INDEX(pg_range_rngtypid_index, 3542, on pg_range using btree(rngtypid oid_ops));
 #define RangeTypidIndexId					3542
 
diff --git a/src/include/catalog/pg_extension_feature.h b/src/include/catalog/pg_extension_feature.h
new file mode 100644
index 0000000..3b09465
--- /dev/null
+++ b/src/include/catalog/pg_extension_feature.h
@@ -0,0 +1,57 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_extension_feature.h
+ *	  definition of the system "extension feature" relation
+ *	  (pg_extension_features), that tracks what features an extension provides
+ *
+ *
+ * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_extension_feature.h
+ *
+ * NOTES
+ *	  the genbki.pl script reads this file and generates .bki
+ *	  information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_EXTENSION_FEATURE_H
+#define PG_EXTENSION_FEATURE_H
+
+#include "catalog/genbki.h"
+
+/* ----------------
+ *		pg_extension_feature definition.  cpp turns this into
+ *		typedef struct FormData_pg_extension_feature
+ * ----------------
+ */
+#define ExtensionFeatureRelationId 3179
+
+CATALOG(pg_extension_feature,3179)
+{
+	Oid			extoid;		/* extension Oid */
+	NameData	extfeature;		/* extension feature */
+} FormData_pg_extension_feature;
+
+/* ----------------
+ *		Form_pg_extension_feature corresponds to a pointer to a tuple with the
+ *		format of pg_extension_feature relation.
+ * ----------------
+ */
+typedef FormData_pg_extension_feature *Form_pg_extension_feature;
+/* ----------------
+ *		compiler constants for pg_extension_feature
+ * ----------------
+ */
+
+#define Natts_pg_extension_feature				2
+#define Anum_pg_extension_feature_extoid		1
+#define Anum_pg_extension_feature_extfeature	2
+
+/* ----------------
+ *		pg_extension_feature has no initial contents
+ * ----------------
+ */
+
+#endif   /* PG_EXTENSION_FEATURE_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 2db8489..aeefae2 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -4397,7 +4397,7 @@ DESCR("less-equal-greater");
 /* Extensions */
 DATA(insert OID = 3082 (  pg_available_extensions		PGNSP PGUID 12 10 100 0 0 f f f f t t s 0 0 2249 "" "{19,25,25}" "{o,o,o}" "{name,default_version,comment}" _null_ pg_available_extensions _null_ _null_ _null_ ));
 DESCR("list available extensions");
-DATA(insert OID = 3083 (  pg_available_extension_versions	PGNSP PGUID 12 10 100 0 0 f f f f t t s 0 0 2249 "" "{19,25,16,16,19,1003,25}" "{o,o,o,o,o,o,o}" "{name,version,superuser,relocatable,schema,requires,comment}" _null_ pg_available_extension_versions _null_ _null_ _null_ ));
+DATA(insert OID = 3083 (  pg_available_extension_versions	PGNSP PGUID 12 10 100 0 0 f f f f t t s 0 0 2249 "" "{19,25,16,16,19,1003,1003,25}" "{o,o,o,o,o,o,o,o}" "{name,version,superuser,relocatable,schema,requires,provides,comment}" _null_ pg_available_extension_versions _null_ _null_ _null_ ));
 DESCR("list available extension versions");
 DATA(insert OID = 3084 (  pg_extension_update_paths		PGNSP PGUID 12 10 100 0 0 f f f f t t s 1 0 2249 "19" "{19,25,25,25}" "{i,o,o,o}" "{name,source,target,path}" _null_ pg_extension_update_paths _null_ _null_ _null_ ));
 DESCR("list an extension's version update paths");
@@ -4651,4 +4651,3 @@ DESCR("SP-GiST support for suffix tree over text");
 #define PROARGMODE_TABLE	't'
 
 #endif   /* PG_PROC_H */
-
diff --git a/src/include/commands/extension.h b/src/include/commands/extension.h
index 7fc8a92..18973cc 100644
--- a/src/include/commands/extension.h
+++ b/src/include/commands/extension.h
@@ -30,11 +30,12 @@ extern Oid	CurrentExtensionObject;
 extern void CreateExtension(CreateExtensionStmt *stmt);
 
 extern void RemoveExtensionById(Oid extId);
+extern void RemoveExtensionFeatureById(Oid extFeatId);
 
 extern Oid InsertExtensionTuple(const char *extName, Oid extOwner,
 					 Oid schemaOid, bool relocatable, const char *extVersion,
 					 Datum extConfig, Datum extCondition,
-					 List *requiredExtensions);
+					 List *requiredExtensions, List *features);
 
 extern void ExecAlterExtensionStmt(AlterExtensionStmt *stmt);
 
@@ -45,4 +46,6 @@ extern char *get_extension_name(Oid ext_oid);
 
 extern void AlterExtensionNamespace(List *names, const char *newschema);
 
+extern char * get_extension_feature_name(Oid featoid);
+
 #endif   /* EXTENSION_H */
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index aaf0cca..1689f5b 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1279,7 +1279,7 @@ SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schem
             viewname             |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   definition                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   
 ---------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  iexit                           | SELECT ih.name, ih.thepath, interpt_pp(ih.thepath, r.thepath) AS exit FROM ihighway ih, ramp r WHERE (ih.thepath ## r.thepath);
- pg_available_extension_versions | SELECT e.name, e.version, (x.extname IS NOT NULL) AS installed, e.superuser, e.relocatable, e.schema, e.requires, e.comment FROM (pg_available_extension_versions() e(name, version, superuser, relocatable, schema, requires, comment) LEFT JOIN pg_extension x ON (((e.name = x.extname) AND (e.version = x.extversion))));
+ pg_available_extension_versions | SELECT e.name, e.version, (x.extname IS NOT NULL) AS installed, e.superuser, e.relocatable, e.schema, e.requires, e.provides, e.comment FROM (pg_available_extension_versions() e(name, version, superuser, relocatable, schema, requires, provides, comment) LEFT JOIN pg_extension x ON (((e.name = x.extname) AND (e.version = x.extversion))));
  pg_available_extensions         | SELECT e.name, e.default_version, x.extversion AS installed_version, e.comment FROM (pg_available_extensions() e(name, default_version, comment) LEFT JOIN pg_extension x ON ((e.name = x.extname)));
  pg_cursors                      | SELECT c.name, c.statement, c.is_holdable, c.is_binary, c.is_scrollable, c.creation_time FROM pg_cursor() c(name, statement, is_holdable, is_binary, is_scrollable, creation_time);
  pg_group                        | SELECT pg_authid.rolname AS groname, pg_authid.oid AS grosysid, ARRAY(SELECT pg_auth_members.member FROM pg_auth_members WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist FROM pg_authid WHERE (NOT pg_authid.rolcanlogin);
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 7f560d2..c0c1fff 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -103,6 +103,7 @@ SELECT relname, relhasindex
  pg_description          | t
  pg_enum                 | t
  pg_extension            | t
+ pg_extension_feature    | t
  pg_foreign_data_wrapper | t
  pg_foreign_server       | t
  pg_foreign_table        | t
@@ -164,7 +165,7 @@ SELECT relname, relhasindex
  timetz_tbl              | f
  tinterval_tbl           | f
  varchar_tbl             | f
-(153 rows)
+(154 rows)
 
 --
 -- another sanity check: every system catalog that has OIDs should have
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to