extension-provides.v5.patch
text/x-diff
Filename: extension-provides.v5.patch
Type: text/x-diff
Part: 0
Message:
Re: Finer Extension dependencies
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
Series: patch v5
| File | + | − |
|---|---|---|
| contrib/pg_upgrade_support/pg_upgrade_support.c | 3 | 1 |
| doc/src/sgml/catalogs.sgml | 57 | 1 |
| doc/src/sgml/datatype.sgml | 1 | 1 |
| doc/src/sgml/extend.sgml | 24 | 3 |
| doc/src/sgml/libpq.sgml | 2 | 4 |
| doc/src/sgml/xfunc.sgml | 2 | 3 |
| src/backend/catalog/dependency.c | 22 | 1 |
| src/backend/catalog/Makefile | 1 | 1 |
| src/backend/catalog/pg_depend.c | 47 | 0 |
| src/backend/catalog/system_views.sql | 1 | 1 |
| src/backend/commands/extension.c | 457 | 82 |
| src/include/catalog/dependency.h | 4 | 0 |
| src/include/catalog/indexing.h | 9 | 0 |
| src/include/catalog/pg_extension_feature.h | 57 | 0 |
| src/include/catalog/pg_proc.h | 1 | 2 |
| src/include/commands/extension.h | 4 | 1 |
| src/interfaces/libpq/fe-connect.c | 101 | 84 |
| src/pl/plpgsql/src/pl_exec.c | 9 | 1 |
| src/test/regress/expected/rules.out | 1 | 1 |
| src/test/regress/expected/sanity_check.out | 2 | 1 |
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/datatype.sgml b/doc/src/sgml/datatype.sgml
index 91dc35d..58e46b5 100644
--- a/doc/src/sgml/datatype.sgml
+++ b/doc/src/sgml/datatype.sgml
@@ -568,7 +568,7 @@ NUMERIC
type is more akin to <type>varchar(<replaceable>n</>)</type>
than to <type>char(<replaceable>n</>)</type>.) The actual storage
requirement is two bytes for each group of four decimal digits,
- plus three to eight bytes overhead.
+ plus five to eight bytes overhead.
</para>
<indexterm>
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/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 96064bb..72c9384 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1027,10 +1027,8 @@ PQconninfoOption *PQconninfoParse(const char *conninfo, char **errmsg);
</para>
<para>
- All legal options will be present in the result array, but the
- <literal>PQconninfoOption</literal> for any option not present
- in the connection string will have <literal>val</literal> set to
- <literal>NULL</literal>; default values are not inserted.
+ Note that only options explicitly specified in the string will have
+ values set in the result array; no defaults are inserted.
</para>
<para>
diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml
index e8620ef..d81d63f 100644
--- a/doc/src/sgml/xfunc.sgml
+++ b/doc/src/sgml/xfunc.sgml
@@ -1767,8 +1767,7 @@ typedef struct
<para>
Finally, all variable-length types must also be passed
by reference. All variable-length types must begin
- with an opaque length field of exactly 4 bytes, which will be set
- by SET_VARSIZE; never set this field directly! All data to
+ with a length field of exactly 4 bytes, and all data to
be stored within that type must be located in the memory
immediately following that length field. The
length field contains the total length of the structure,
@@ -1834,7 +1833,7 @@ memcpy(destination->data, buffer, 40);
]]>
</programlisting>
- <literal>VARHDRSZ</> is the same as <literal>sizeof(int32)</>, but
+ <literal>VARHDRSZ</> is the same as <literal>sizeof(int4)</>, but
it's considered good style to use the macro <literal>VARHDRSZ</>
to refer to the size of the overhead for a variable-length type.
Also, the length field <emphasis>must</> be set using the
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..b706ffc 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, ExtensionFeatureNameIndexId, 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(ExtensionFeatureExtensionNameIndexId, 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..c2bcbcd 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -303,6 +303,15 @@ 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_name_index, 3181, on pg_extension_feature using btree(extfeature name_ops));
+#define ExtensionFeatureNameIndexId 3181
+
+DECLARE_INDEX(pg_extension_feature_extoid_name_index, 3182, on pg_extension_feature using btree(extoid oid_ops, extfeature name_ops));
+#define ExtensionFeatureExtensionNameIndexId 3182
+
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/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 6a20a14..817d83b 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -121,9 +121,9 @@ static int ldapServiceLookup(const char *purl, PQconninfoOption *options,
* fallback is available. If after all no value can be determined
* for an option, an error is returned.
*
- * The value for the username is treated specially in conninfo_add_defaults.
- * If the value is not obtained any other way, the username is determined
- * by pg_fe_getauthname().
+ * The value for the username is treated specially in conninfo_parse.
+ * If the Compiled-in resource is specified as a NULL value, the
+ * user is determined by pg_fe_getauthname().
*
* The Label and Disp-Char entries are provided for applications that
* want to use PQconndefaults() to create a generic database connection
@@ -292,14 +292,11 @@ static PGconn *makeEmptyPGconn(void);
static void fillPGconn(PGconn *conn, PQconninfoOption *connOptions);
static void freePGconn(PGconn *conn);
static void closePGconn(PGconn *conn);
-static PQconninfoOption *conninfo_init(PQExpBuffer errorMessage);
static PQconninfoOption *conninfo_parse(const char *conninfo,
PQExpBuffer errorMessage, bool use_defaults);
static PQconninfoOption *conninfo_array_parse(const char *const * keywords,
const char *const * values, PQExpBuffer errorMessage,
bool use_defaults, int expand_dbname);
-static bool conninfo_add_defaults(PQconninfoOption *options,
- PQExpBuffer errorMessage);
static char *conninfo_getval(PQconninfoOption *connOptions,
const char *keyword);
static void defaultNoticeReceiver(void *arg, const PGresult *res);
@@ -816,9 +813,10 @@ connectOptions2(PGconn *conn)
/*
* PQconndefaults
*
- * Construct a default connection options array, which identifies all the
- * available options and shows any default values that are available from the
- * environment etc. On error (eg out of memory), NULL is returned.
+ * Parse an empty string like PQconnectdb() would do and return the
+ * resulting connection options array, ie, all the default values that are
+ * available from the environment etc. On error (eg out of memory),
+ * NULL is returned.
*
* Using this function, an application may determine all possible options
* and their current default values.
@@ -835,21 +833,10 @@ PQconndefaults(void)
PQExpBufferData errorBuf;
PQconninfoOption *connOptions;
- /* We don't actually report any errors here, but callees want a buffer */
initPQExpBuffer(&errorBuf);
if (PQExpBufferDataBroken(errorBuf))
return NULL; /* out of memory already :-( */
-
- connOptions = conninfo_init(&errorBuf);
- if (connOptions != NULL)
- {
- if (!conninfo_add_defaults(connOptions, &errorBuf))
- {
- PQconninfoFree(connOptions);
- connOptions = NULL;
- }
- }
-
+ connOptions = conninfo_parse("", &errorBuf, true);
termPQExpBuffer(&errorBuf);
return connOptions;
}
@@ -4000,25 +3987,6 @@ PQconninfoParse(const char *conninfo, char **errmsg)
}
/*
- * Build a working copy of the constant PQconninfoOptions array.
- */
-static PQconninfoOption *
-conninfo_init(PQExpBuffer errorMessage)
-{
- PQconninfoOption *options;
-
- options = (PQconninfoOption *) malloc(sizeof(PQconninfoOptions));
- if (options == NULL)
- {
- printfPQExpBuffer(errorMessage,
- libpq_gettext("out of memory\n"));
- return NULL;
- }
- memcpy(options, PQconninfoOptions, sizeof(PQconninfoOptions));
- return options;
-}
-
-/*
* Conninfo parser routine
*
* If successful, a malloc'd PQconninfoOption array is returned.
@@ -4034,15 +4002,21 @@ conninfo_parse(const char *conninfo, PQExpBuffer errorMessage,
char *pname;
char *pval;
char *buf;
+ char *tmp;
char *cp;
char *cp2;
PQconninfoOption *options;
PQconninfoOption *option;
/* Make a working copy of PQconninfoOptions */
- options = conninfo_init(errorMessage);
+ options = malloc(sizeof(PQconninfoOptions));
if (options == NULL)
+ {
+ printfPQExpBuffer(errorMessage,
+ libpq_gettext("out of memory\n"));
return NULL;
+ }
+ memcpy(options, PQconninfoOptions, sizeof(PQconninfoOptions));
/* Need a modifiable copy of the input string */
if ((buf = strdup(conninfo)) == NULL)
@@ -4196,14 +4170,73 @@ conninfo_parse(const char *conninfo, PQExpBuffer errorMessage,
free(buf);
/*
- * Add in defaults if the caller wants that.
+ * Stop here if caller doesn't want defaults filled in.
+ */
+ if (!use_defaults)
+ return options;
+
+ /*
+ * If there's a service spec, use it to obtain any not-explicitly-given
+ * parameters.
+ */
+ if (parseServiceInfo(options, errorMessage))
+ {
+ PQconninfoFree(options);
+ return NULL;
+ }
+
+ /*
+ * Get the fallback resources for parameters not specified in the conninfo
+ * string nor the service.
*/
- if (use_defaults)
+ for (option = options; option->keyword != NULL; option++)
{
- if (!conninfo_add_defaults(options, errorMessage))
+ if (option->val != NULL)
+ continue; /* Value was in conninfo or service */
+
+ /*
+ * Try to get the environment variable fallback
+ */
+ if (option->envvar != NULL)
{
- PQconninfoFree(options);
- return NULL;
+ if ((tmp = getenv(option->envvar)) != NULL)
+ {
+ option->val = strdup(tmp);
+ if (!option->val)
+ {
+ printfPQExpBuffer(errorMessage,
+ libpq_gettext("out of memory\n"));
+ PQconninfoFree(options);
+ return NULL;
+ }
+ continue;
+ }
+ }
+
+ /*
+ * No environment variable specified or this one isn't set - try
+ * compiled in
+ */
+ if (option->compiled != NULL)
+ {
+ option->val = strdup(option->compiled);
+ if (!option->val)
+ {
+ printfPQExpBuffer(errorMessage,
+ libpq_gettext("out of memory\n"));
+ PQconninfoFree(options);
+ return NULL;
+ }
+ continue;
+ }
+
+ /*
+ * Special handling for user
+ */
+ if (strcmp(option->keyword, "user") == 0)
+ {
+ option->val = pg_fe_getauthname(errorMessage);
+ continue;
}
}
@@ -4229,6 +4262,7 @@ conninfo_array_parse(const char *const * keywords, const char *const * values,
PQExpBuffer errorMessage, bool use_defaults,
int expand_dbname)
{
+ char *tmp;
PQconninfoOption *options;
PQconninfoOption *str_options = NULL;
PQconninfoOption *option;
@@ -4264,15 +4298,18 @@ conninfo_array_parse(const char *const * keywords, const char *const * values,
}
/* Make a working copy of PQconninfoOptions */
- options = conninfo_init(errorMessage);
+ options = malloc(sizeof(PQconninfoOptions));
if (options == NULL)
{
+ printfPQExpBuffer(errorMessage,
+ libpq_gettext("out of memory\n"));
PQconninfoFree(str_options);
return NULL;
}
+ memcpy(options, PQconninfoOptions, sizeof(PQconninfoOptions));
- /* Parse the keywords/values arrays */
i = 0;
+ /* Parse the keywords/values arrays */
while (keywords[i])
{
const char *pname = keywords[i];
@@ -4349,42 +4386,20 @@ conninfo_array_parse(const char *const * keywords, const char *const * values,
PQconninfoFree(str_options);
/*
- * Add in defaults if the caller wants that.
+ * Stop here if caller doesn't want defaults filled in.
*/
- if (use_defaults)
- {
- if (!conninfo_add_defaults(options, errorMessage))
- {
- PQconninfoFree(options);
- return NULL;
- }
- }
-
- return options;
-}
-
-/*
- * Add the default values for any unspecified options to the connection
- * options array.
- *
- * Defaults are obtained from a service file, environment variables, etc.
- *
- * Returns TRUE if successful, otherwise FALSE; errorMessage is filled in
- * upon failure. Note that failure to locate a default value is not an
- * error condition here --- we just leave the option's value as NULL.
- */
-static bool
-conninfo_add_defaults(PQconninfoOption *options, PQExpBuffer errorMessage)
-{
- PQconninfoOption *option;
- char *tmp;
+ if (!use_defaults)
+ return options;
/*
* If there's a service spec, use it to obtain any not-explicitly-given
* parameters.
*/
- if (parseServiceInfo(options, errorMessage) != 0)
- return false;
+ if (parseServiceInfo(options, errorMessage))
+ {
+ PQconninfoFree(options);
+ return NULL;
+ }
/*
* Get the fallback resources for parameters not specified in the conninfo
@@ -4407,15 +4422,16 @@ conninfo_add_defaults(PQconninfoOption *options, PQExpBuffer errorMessage)
{
printfPQExpBuffer(errorMessage,
libpq_gettext("out of memory\n"));
- return false;
+ PQconninfoFree(options);
+ return NULL;
}
continue;
}
}
/*
- * No environment variable specified or the variable isn't set - try
- * compiled-in default
+ * No environment variable specified or this one isn't set - try
+ * compiled in
*/
if (option->compiled != NULL)
{
@@ -4424,13 +4440,14 @@ conninfo_add_defaults(PQconninfoOption *options, PQExpBuffer errorMessage)
{
printfPQExpBuffer(errorMessage,
libpq_gettext("out of memory\n"));
- return false;
+ PQconninfoFree(options);
+ return NULL;
}
continue;
}
/*
- * Special handling for "user" option
+ * Special handling for user
*/
if (strcmp(option->keyword, "user") == 0)
{
@@ -4439,7 +4456,7 @@ conninfo_add_defaults(PQconninfoOption *options, PQExpBuffer errorMessage)
}
}
- return true;
+ return options;
}
static char *
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index a385b9a..09c86aa 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -1447,9 +1447,17 @@ exec_stmt_getdiag(PLpgSQL_execstate *estate, PLpgSQL_stmt_getdiag *stmt)
foreach(lc, stmt->diag_items)
{
PLpgSQL_diag_item *diag_item = (PLpgSQL_diag_item *) lfirst(lc);
- PLpgSQL_datum *var = estate->datums[diag_item->target];
+ PLpgSQL_datum *var;
bool isnull = false;
+ if (diag_item->target <= 0)
+ continue;
+
+ var = estate->datums[diag_item->target];
+
+ if (var == NULL)
+ continue;
+
switch (diag_item->kind)
{
case PLPGSQL_GETDIAG_ROW_COUNT:
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