aboutsummaryrefslogtreecommitdiff
path: root/src/backend/commands/extension.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2024-03-04 14:49:31 -0500
committerTom Lane <tgl@sss.pgh.pa.us>2024-03-04 14:49:36 -0500
commite5bc9454e527b1cba97553531d8d4992892fdeef (patch)
tree09d1e2d8ad951b23abee076e3cfc257546ae967a /src/backend/commands/extension.c
parentdc8f2d7c064ac2ba76e5821fd96fa3837076f0d2 (diff)
downloadpostgresql-e5bc9454e527b1cba97553531d8d4992892fdeef.tar.gz
postgresql-e5bc9454e527b1cba97553531d8d4992892fdeef.zip
Explicitly list dependent types as extension members in pg_depend.
Auto-generated array types, multirange types, and relation rowtypes are treated as dependent objects: they can't be dropped separately from the base object, nor can they have their own ownership or permissions. We previously felt that, for objects that are in an extension, only the base object needs to be listed as an extension member in pg_depend. While that's sufficient to prevent inappropriate drops, it results in undesirable answers if someone asks whether a dependent type belongs to the extension. It looks like the dependent type is just some random separately-created object that happens to depend on the base object. Notably, this results in postgres_fdw concluding that expressions involving an array type are not shippable to the remote server, even when the defining extension has been whitelisted. To fix, cause GenerateTypeDependencies to make extension dependencies for dependent types as well as their base objects, and adjust ExecAlterExtensionContentsStmt so that object addition and removal operations recurse to dependent types. The latter change means that pg_upgrade of a type-defining extension will end with the dependent type(s) now also listed as extension members, even if they were not that way in the source database. Normally we want pg_upgrade to precisely reproduce the source extension's state, but it seems desirable to make an exception here. This is arguably a bug fix, but we can't back-patch it since it causes changes in the expected contents of pg_depend. (Because it does, I've bumped catversion, even though there's no change in the immediate post-initdb catalog contents.) Tom Lane and David Geier Discussion: https://postgr.es/m/4a847c55-489f-4e8d-a664-fc6b1cbe306f@gmail.com
Diffstat (limited to 'src/backend/commands/extension.c')
-rw-r--r--src/backend/commands/extension.c83
1 files changed, 73 insertions, 10 deletions
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index af600d7c9ac..77d8c9e1862 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -129,6 +129,9 @@ static void ApplyExtensionUpdates(Oid extensionOid,
char *origSchemaName,
bool cascade,
bool is_create);
+static void ExecAlterExtensionContentsRecurse(AlterExtensionContentsStmt *stmt,
+ ObjectAddress extension,
+ ObjectAddress object);
static char *read_whole_file(const char *filename, int *length);
@@ -3292,7 +3295,6 @@ ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt,
ObjectAddress extension;
ObjectAddress object;
Relation relation;
- Oid oldExtension;
switch (stmt->objtype)
{
@@ -3347,6 +3349,38 @@ ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt,
check_object_ownership(GetUserId(), stmt->objtype, object,
stmt->object, relation);
+ /* Do the update, recursing to any dependent objects */
+ ExecAlterExtensionContentsRecurse(stmt, extension, object);
+
+ /* Finish up */
+ InvokeObjectPostAlterHook(ExtensionRelationId, extension.objectId, 0);
+
+ /*
+ * If get_object_address() opened the relation for us, we close it to keep
+ * the reference count correct - but we retain any locks acquired by
+ * get_object_address() until commit time, to guard against concurrent
+ * activity.
+ */
+ if (relation != NULL)
+ relation_close(relation, NoLock);
+
+ return extension;
+}
+
+/*
+ * ExecAlterExtensionContentsRecurse
+ * Subroutine for ExecAlterExtensionContentsStmt
+ *
+ * Do the bare alteration of object's membership in extension,
+ * without permission checks. Recurse to dependent objects, if any.
+ */
+static void
+ExecAlterExtensionContentsRecurse(AlterExtensionContentsStmt *stmt,
+ ObjectAddress extension,
+ ObjectAddress object)
+{
+ Oid oldExtension;
+
/*
* Check existing extension membership.
*/
@@ -3430,18 +3464,47 @@ ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt,
removeExtObjInitPriv(object.objectId, object.classId);
}
- InvokeObjectPostAlterHook(ExtensionRelationId, extension.objectId, 0);
-
/*
- * If get_object_address() opened the relation for us, we close it to keep
- * the reference count correct - but we retain any locks acquired by
- * get_object_address() until commit time, to guard against concurrent
- * activity.
+ * Recurse to any dependent objects; currently, this includes the array
+ * type of a base type, the multirange type associated with a range type,
+ * and the rowtype of a table.
*/
- if (relation != NULL)
- relation_close(relation, NoLock);
+ if (object.classId == TypeRelationId)
+ {
+ ObjectAddress depobject;
- return extension;
+ depobject.classId = TypeRelationId;
+ depobject.objectSubId = 0;
+
+ /* If it has an array type, update that too */
+ depobject.objectId = get_array_type(object.objectId);
+ if (OidIsValid(depobject.objectId))
+ ExecAlterExtensionContentsRecurse(stmt, extension, depobject);
+
+ /* If it is a range type, update the associated multirange too */
+ if (type_is_range(object.objectId))
+ {
+ depobject.objectId = get_range_multirange(object.objectId);
+ if (!OidIsValid(depobject.objectId))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(object.objectId))));
+ ExecAlterExtensionContentsRecurse(stmt, extension, depobject);
+ }
+ }
+ if (object.classId == RelationRelationId)
+ {
+ ObjectAddress depobject;
+
+ depobject.classId = TypeRelationId;
+ depobject.objectSubId = 0;
+
+ /* It might not have a rowtype, but if it does, update that */
+ depobject.objectId = get_rel_type_id(object.objectId);
+ if (OidIsValid(depobject.objectId))
+ ExecAlterExtensionContentsRecurse(stmt, extension, depobject);
+ }
}
/*