On Mon, Mar 18, 2019 at 09:38:19PM -0500, Eric Hanson wrote: > #1: Dependencies > > Let's say we have two extensions, A and B, both of which depend on a third > extension C, let's just say C is hstore. A and B are written by different > developers, and both contain in their .control file the line > > requires = 'hstore' > > When A is installed, if A creates a schema, it puts hstore in that schema. > If not, hstore is already installed, it uses it in that location. How does > the extension know where to reference hstore? > > Then, when B is installed, it checks to see if extension hstore is > installed, sees that it is, and moves on. What if it expects it in a > different place than A does? The hstore extension can only be installed > once, in a single schema, but if multiple extensions depend on it and look > for it in different places, they are incompatible. > > I have heard talk of a way to write extensions so that they dynamically > reference the schema of their dependencies, but sure don't know how that > would work if it's possible. The @extschema@ variable references the > *current* extension's schema, but not there is no dynamic variable to > reference the schema of a dependency.
If desperate, you can do it like this: DO $$ BEGIN EXECUTE format('SELECT %I.earth()', (SELECT nspname FROM pg_namespace n JOIN pg_extension ON n.oid = extnamespace WHERE extname = 'earthdistance' )); END $$; Needless to say, that's too ugly. Though probably unimportant in practice, it also has a race condition vs. ALTER EXTENSION SET SCHEMA. > Also it is possible in theory to dynamically set search_path to contain > every schema of every dependency in play and then just not specify a schema > when you use something in a dependency. But this ANDs together all the > scopes of all the dependencies of an extension, introducing potential for > collisions, and is generally kind of clunky. That's how it works today, and it has the problems you describe. I discussed some solution candidates here: https://www.postgresql.org/message-id/20180710014308.ga805...@rfd.leadboat.com The @DEPNAME_schema@ thing was trivial to implement, but I shelved it. I'm attaching the proof of concept, for your information. > #2: Data in Extensions > > Extensions that are just a collection of functions and types seem to be the > norm. Extensions can contain what the docs call "configuration" data, but > rows are really second class citizens: They aren't tracked with > pg_catalog.pg_depend, they aren't deleted when the extension is dropped, > etc. > > Sometimes it would make sense for an extension to contain *only* data, or > insert some rows in a table that the extension doesn't "own", but has as a > dependency. For example, a "webserver" extension might contain a > "resource" table that serves up the content of resources in the table at a > specified path. But then, another extension, say an app, might want to just > list the webserver extension as a dependency, and insert a few resource > rows into it. This is really from what I can tell beyond the scope of what > extensions are capable of. I never thought of this use case. Interesting.
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c index 2e45381..cd061ea 100644 --- a/src/backend/commands/extension.c +++ b/src/backend/commands/extension.c @@ -786,13 +786,14 @@ static void execute_extension_script(Oid extensionOid, ExtensionControlFile *control, const char *from_version, const char *version, + List *requiredExtensions, List *requiredSchemas, const char *schemaName, Oid schemaOid) { char *filename; int save_nestlevel; StringInfoData pathbuf; - ListCell *lc; + ListCell *lc, *le; /* * Enforce superuser-ness if appropriate. We postpone this check until @@ -907,6 +908,19 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control, } /* + * For each dependency, substitute the dependency's schema for + * @DEPNAME_schema@. This is fishy for a relocatable dependency, but + * we accept that risk. + */ + forboth (le, requiredExtensions, lc, requiredSchemas) + { + t_sql = DirectFunctionCall3(replace_text, + t_sql, + CStringGetTextDatum(psprintf("@%s_schema@", get_extension_name(lfirst_oid(le)))), + CStringGetTextDatum(get_namespace_name(lfirst_oid(lc)))); + } + + /* * If module_pathname was set in the control file, substitute its * value for occurrences of MODULE_PATHNAME. */ @@ -1528,6 +1542,7 @@ CreateExtensionInternal(char *extensionName, */ execute_extension_script(extensionOid, control, oldVersionName, versionName, + requiredExtensions, requiredSchemas, schemaName, schemaOid); @@ -3147,6 +3162,7 @@ ApplyExtensionUpdates(Oid extensionOid, */ execute_extension_script(extensionOid, control, oldVersionName, versionName, + requiredExtensions, requiredSchemas, schemaName, schemaOid);