aboutsummaryrefslogtreecommitdiff
path: root/src/backend/commands/extension.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2023-03-20 18:37:11 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2023-03-20 18:37:11 -0400
commit72a5b1fc880481914da2d4233077438dd87840ca (patch)
treea4ff9a54fb004dca734053512f98e260d1ec2664 /src/backend/commands/extension.c
parent30e9f2608adf2f61fc2e1be13ef8bc88c250f303 (diff)
downloadpostgresql-72a5b1fc880481914da2d4233077438dd87840ca.tar.gz
postgresql-72a5b1fc880481914da2d4233077438dd87840ca.zip
Add @extschema:name@ and no_relocate options to extensions.
@extschema:name@ extends the existing @extschema@ feature so that we can also insert the schema name of some required extension, thus making cross-extension references robust even if they are in different schemas. However, this has the same hazard as @extschema@: if the schema name is embedded literally in an installed object, rather than being looked up once during extension script execution, then it's no longer safe to relocate the other extension to another schema. To deal with that without restricting things unnecessarily, add a "no_relocate" option to extension control files. This allows an extension to specify that it cannot handle relocation of some of its required extensions, even if in themselves those extensions are relocatable. We detect "no_relocate" requests of dependent extensions during ALTER EXTENSION SET SCHEMA. Regina Obe, reviewed by Sandro Santilli and myself Discussion: https://postgr.es/m/003001d8f4ae$402282c0$c0678840$@pcorp.us
Diffstat (limited to 'src/backend/commands/extension.c')
-rw-r--r--src/backend/commands/extension.c81
1 files changed, 78 insertions, 3 deletions
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index 02ff4a9a7fb..0eabe18335e 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -90,6 +90,8 @@ typedef struct ExtensionControlFile
bool trusted; /* allow becoming superuser on the fly? */
int encoding; /* encoding of the script file, or -1 */
List *requires; /* names of prerequisite extensions */
+ List *no_relocate; /* names of prerequisite extensions that
+ * should not be relocated */
} ExtensionControlFile;
/*
@@ -606,6 +608,21 @@ parse_extension_control_file(ExtensionControlFile *control,
item->name)));
}
}
+ else if (strcmp(item->name, "no_relocate") == 0)
+ {
+ /* Need a modifiable copy of string */
+ char *rawnames = pstrdup(item->value);
+
+ /* Parse string into list of identifiers */
+ if (!SplitIdentifierString(rawnames, ',', &control->no_relocate))
+ {
+ /* 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),
@@ -845,6 +862,8 @@ extension_is_trusted(ExtensionControlFile *control)
* Execute the appropriate script file for installing or updating the extension
*
* If from_version isn't NULL, it's an update
+ *
+ * Note: requiredSchemas must be one-for-one with the control->requires list
*/
static void
execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
@@ -860,6 +879,7 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
int save_nestlevel;
StringInfoData pathbuf;
ListCell *lc;
+ ListCell *lc2;
/*
* Enforce superuser-ness if appropriate. We postpone these checks until
@@ -1031,6 +1051,27 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
}
/*
+ * Likewise, substitute required extensions' schema names for
+ * occurrences of @extschema:extension_name@.
+ */
+ Assert(list_length(control->requires) == list_length(requiredSchemas));
+ forboth(lc, control->requires, lc2, requiredSchemas)
+ {
+ char *reqextname = (char *) lfirst(lc);
+ Oid reqschema = lfirst_oid(lc2);
+ char *schemaName = get_namespace_name(reqschema);
+ const char *qSchemaName = quote_identifier(schemaName);
+ char *repltoken;
+
+ repltoken = psprintf("@extschema:%s@", reqextname);
+ t_sql = DirectFunctionCall3Coll(replace_text,
+ C_COLLATION_OID,
+ t_sql,
+ CStringGetTextDatum(repltoken),
+ CStringGetTextDatum(qSchemaName));
+ }
+
+ /*
* If module_pathname was set in the control file, substitute its
* value for occurrences of MODULE_PATHNAME.
*/
@@ -2817,9 +2858,43 @@ AlterExtensionNamespace(const char *extensionName, const char *newschema, Oid *o
Oid dep_oldNspOid;
/*
- * Ignore non-membership dependencies. (Currently, the only other
- * case we could see here is a normal dependency from another
- * extension.)
+ * If a dependent extension has a no_relocate request for this
+ * extension, disallow SET SCHEMA. (XXX it's a bit ugly to do this in
+ * the same loop that's actually executing the renames: we may detect
+ * the error condition only after having expended a fair amount of
+ * work. However, the alternative is to do two scans of pg_depend,
+ * which seems like optimizing for failure cases. The rename work
+ * will all roll back cleanly enough if we do fail here.)
+ */
+ if (pg_depend->deptype == DEPENDENCY_NORMAL &&
+ pg_depend->classid == ExtensionRelationId)
+ {
+ char *depextname = get_extension_name(pg_depend->objid);
+ ExtensionControlFile *dcontrol;
+ ListCell *lc;
+
+ dcontrol = read_extension_control_file(depextname);
+ foreach(lc, dcontrol->no_relocate)
+ {
+ char *nrextname = (char *) lfirst(lc);
+
+ if (strcmp(nrextname, NameStr(extForm->extname)) == 0)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot SET SCHEMA of extension \"%s\" because other extensions prevent it",
+ NameStr(extForm->extname)),
+ errdetail("Extension \"%s\" requests no relocation of extension \"%s\".",
+ depextname,
+ NameStr(extForm->extname))));
+ }
+ }
+ }
+
+ /*
+ * Otherwise, ignore non-membership dependencies. (Currently, the
+ * only other case we could see here is a normal dependency from
+ * another extension.)
*/
if (pg_depend->deptype != DEPENDENCY_EXTENSION)
continue;