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);
 

Reply via email to