aboutsummaryrefslogtreecommitdiff
path: root/src/backend/catalog/pg_depend.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/catalog/pg_depend.c')
-rw-r--r--src/backend/catalog/pg_depend.c74
1 files changed, 69 insertions, 5 deletions
diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c
index 9ffadbbc181..71c7cef2260 100644
--- a/src/backend/catalog/pg_depend.c
+++ b/src/backend/catalog/pg_depend.c
@@ -124,15 +124,23 @@ recordMultipleDependencies(const ObjectAddress *depender,
/*
* If we are executing a CREATE EXTENSION operation, mark the given object
- * as being a member of the extension. Otherwise, do nothing.
+ * as being a member of the extension, or check that it already is one.
+ * Otherwise, do nothing.
*
* This must be called during creation of any user-definable object type
* that could be a member of an extension.
*
- * If isReplace is true, the object already existed (or might have already
- * existed), so we must check for a pre-existing extension membership entry.
- * Passing false is a guarantee that the object is newly created, and so
- * could not already be a member of any extension.
+ * isReplace must be true if the object already existed, and false if it is
+ * newly created. In the former case we insist that it already be a member
+ * of the current extension. In the latter case we can skip checking whether
+ * it is already a member of any extension.
+ *
+ * Note: isReplace = true is typically used when updating a object in
+ * CREATE OR REPLACE and similar commands. We used to allow the target
+ * object to not already be an extension member, instead silently absorbing
+ * it into the current extension. However, this was both error-prone
+ * (extensions might accidentally overwrite free-standing objects) and
+ * a security hazard (since the object would retain its previous ownership).
*/
void
recordDependencyOnCurrentExtension(const ObjectAddress *object,
@@ -150,6 +158,12 @@ recordDependencyOnCurrentExtension(const ObjectAddress *object,
{
Oid oldext;
+ /*
+ * Side note: these catalog lookups are safe only because the
+ * object is a pre-existing one. In the not-isReplace case, the
+ * caller has most likely not yet done a CommandCounterIncrement
+ * that would make the new object visible.
+ */
oldext = getExtensionOfObject(object->classId, object->objectId);
if (OidIsValid(oldext))
{
@@ -163,6 +177,13 @@ recordDependencyOnCurrentExtension(const ObjectAddress *object,
getObjectDescription(object),
get_extension_name(oldext))));
}
+ /* It's a free-standing object, so reject */
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("%s is not a member of extension \"%s\"",
+ getObjectDescription(object),
+ get_extension_name(CurrentExtensionObject)),
+ errdetail("An extension is not allowed to replace an object that it does not own.")));
}
/* OK, record it as a member of CurrentExtensionObject */
@@ -175,6 +196,49 @@ recordDependencyOnCurrentExtension(const ObjectAddress *object,
}
/*
+ * If we are executing a CREATE EXTENSION operation, check that the given
+ * object is a member of the extension, and throw an error if it isn't.
+ * Otherwise, do nothing.
+ *
+ * This must be called whenever a CREATE IF NOT EXISTS operation (for an
+ * object type that can be an extension member) has found that an object of
+ * the desired name already exists. It is insecure for an extension to use
+ * IF NOT EXISTS except when the conflicting object is already an extension
+ * member; otherwise a hostile user could substitute an object with arbitrary
+ * properties.
+ */
+void
+checkMembershipInCurrentExtension(const ObjectAddress *object)
+{
+ /*
+ * This is actually the same condition tested in
+ * recordDependencyOnCurrentExtension; but we want to issue a
+ * differently-worded error, and anyway it would be pretty confusing to
+ * call recordDependencyOnCurrentExtension in these circumstances.
+ */
+
+ /* Only whole objects can be extension members */
+ Assert(object->objectSubId == 0);
+
+ if (creating_extension)
+ {
+ Oid oldext;
+
+ oldext = getExtensionOfObject(object->classId, object->objectId);
+ /* If already a member of this extension, OK */
+ if (oldext == CurrentExtensionObject)
+ return;
+ /* Else complain */
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("%s is not a member of extension \"%s\"",
+ getObjectDescription(object),
+ get_extension_name(CurrentExtensionObject)),
+ errdetail("An extension may only use CREATE ... IF NOT EXISTS to skip object creation if the conflicting object is one that it already owns.")));
+ }
+}
+
+/*
* deleteDependencyRecordsFor -- delete all records with given depender
* classId/objectId. Returns the number of records deleted.
*