Alvaro Herrera <alvhe...@commandprompt.com> writes: > get_available_versions_for_extension seems to contain a bunch of > commented-out lines ...
Damn. Sorry about that. Here's a cleaned-up version of the patch. 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..284517a 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,38 @@ get_available_versions_for_extension(ExtensionControlFile *pcontrol, NAMEDATALEN, false, 'c'); values[5] = PointerGetDatum(a); } + /* provides */ + nulls[6] = false; + { + 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 +2918,7 @@ ApplyExtensionUpdates(Oid extensionOid, ExtensionControlFile *control; char *schemaName; Oid schemaOid; - List *requiredExtensions; + List *requiredFeatures; List *requiredSchemas; Relation extRel; ScanKeyData key[1]; @@ -2516,7 +2929,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 +2985,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