aboutsummaryrefslogtreecommitdiff
path: root/src/backend/commands/alter.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/commands/alter.c')
-rw-r--r--src/backend/commands/alter.c196
1 files changed, 160 insertions, 36 deletions
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index 1c6ae0243e4..2c9340accf1 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -23,6 +23,7 @@
#include "commands/conversioncmds.h"
#include "commands/dbcommands.h"
#include "commands/defrem.h"
+#include "commands/extension.h"
#include "commands/proclang.h"
#include "commands/schemacmds.h"
#include "commands/tablecmds.h"
@@ -188,6 +189,10 @@ ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt)
AlterConversionNamespace(stmt->object, stmt->newschema);
break;
+ case OBJECT_EXTENSION:
+ AlterExtensionNamespace(stmt->object, stmt->newschema);
+ break;
+
case OBJECT_FUNCTION:
AlterFunctionNamespace(stmt->object, stmt->objarg, false,
stmt->newschema);
@@ -242,87 +247,204 @@ ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt)
}
/*
+ * Change an object's namespace given its classOid and object Oid.
+ *
+ * Objects that don't have a namespace should be ignored.
+ *
+ * This function is currently used only by ALTER EXTENSION SET SCHEMA,
+ * so it only needs to cover object types that can be members of an
+ * extension, and it doesn't have to deal with certain special cases
+ * such as not wanting to process array types --- those should never
+ * be direct members of an extension anyway.
+ *
+ * Returns the OID of the object's previous namespace, or InvalidOid if
+ * object doesn't have a schema.
+ */
+Oid
+AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid)
+{
+ Oid oldNspOid = InvalidOid;
+ ObjectAddress dep;
+
+ dep.classId = classId;
+ dep.objectId = objid;
+ dep.objectSubId = 0;
+
+ switch (getObjectClass(&dep))
+ {
+ case OCLASS_CLASS:
+ {
+ Relation rel;
+ Relation classRel;
+
+ rel = relation_open(objid, AccessExclusiveLock);
+ oldNspOid = RelationGetNamespace(rel);
+
+ classRel = heap_open(RelationRelationId, RowExclusiveLock);
+
+ AlterRelationNamespaceInternal(classRel,
+ objid,
+ oldNspOid,
+ nspOid,
+ true);
+
+ heap_close(classRel, RowExclusiveLock);
+
+ relation_close(rel, NoLock);
+ break;
+ }
+
+ case OCLASS_PROC:
+ oldNspOid = AlterFunctionNamespace_oid(objid, nspOid);
+ break;
+
+ case OCLASS_TYPE:
+ oldNspOid = AlterTypeNamespace_oid(objid, nspOid);
+ break;
+
+ case OCLASS_CONVERSION:
+ oldNspOid = AlterConversionNamespace_oid(objid, nspOid);
+ break;
+
+ case OCLASS_OPERATOR:
+ oldNspOid = AlterOperatorNamespace_oid(objid, nspOid);
+ break;
+
+ case OCLASS_OPCLASS:
+ oldNspOid = AlterOpClassNamespace_oid(objid, nspOid);
+ break;
+
+ case OCLASS_OPFAMILY:
+ oldNspOid = AlterOpFamilyNamespace_oid(objid, nspOid);
+ break;
+
+ case OCLASS_TSPARSER:
+ oldNspOid = AlterTSParserNamespace_oid(objid, nspOid);
+ break;
+
+ case OCLASS_TSDICT:
+ oldNspOid = AlterTSDictionaryNamespace_oid(objid, nspOid);
+ break;
+
+ case OCLASS_TSTEMPLATE:
+ oldNspOid = AlterTSTemplateNamespace_oid(objid, nspOid);
+ break;
+
+ case OCLASS_TSCONFIG:
+ oldNspOid = AlterTSConfigurationNamespace_oid(objid, nspOid);
+ break;
+
+ default:
+ break;
+ }
+
+ return oldNspOid;
+}
+
+/*
* Generic function to change the namespace of a given object, for simple
- * cases (won't work for tables or functions, objects which have more than 2
- * key-attributes to use when searching for their syscache entries --- we
- * don't want nor need to get this generic here).
+ * cases (won't work for tables, nor other cases where we need to do more
+ * than change the namespace column of a single catalog entry).
*
* The AlterFooNamespace() calls just above will call a function whose job
* is to lookup the arguments for the generic function here.
*
- * Relation must already by open, it's the responsibility of the caller to
- * close it.
+ * rel: catalog relation containing object (RowExclusiveLock'd by caller)
+ * oidCacheId: syscache that indexes this catalog by OID
+ * nameCacheId: syscache that indexes this catalog by name and namespace
+ * (pass -1 if there is none)
+ * objid: OID of object to change the namespace of
+ * nspOid: OID of new namespace
+ * Anum_name: column number of catalog's name column
+ * Anum_namespace: column number of catalog's namespace column
+ * Anum_owner: column number of catalog's owner column, or -1 if none
+ * acl_kind: ACL type for object, or -1 if none assigned
+ *
+ * If the object does not have an owner or permissions, pass -1 for
+ * Anum_owner and acl_kind. In this case the calling user must be superuser.
+ *
+ * Returns the OID of the object's previous namespace.
*/
-void
-AlterObjectNamespace(Relation rel, int cacheId,
- Oid classId, Oid objid, Oid nspOid,
+Oid
+AlterObjectNamespace(Relation rel, int oidCacheId, int nameCacheId,
+ Oid objid, Oid nspOid,
int Anum_name, int Anum_namespace, int Anum_owner,
- AclObjectKind acl_kind,
- bool superuser_only)
+ AclObjectKind acl_kind)
{
+ Oid classId = RelationGetRelid(rel);
Oid oldNspOid;
Datum name, namespace;
bool isnull;
- HeapTuple tup, newtup = NULL;
+ HeapTuple tup, newtup;
Datum *values;
bool *nulls;
bool *replaces;
- tup = SearchSysCacheCopy1(cacheId, ObjectIdGetDatum(objid));
+ tup = SearchSysCacheCopy1(oidCacheId, ObjectIdGetDatum(objid));
if (!HeapTupleIsValid(tup)) /* should not happen */
- elog(ERROR, "cache lookup failed for object %u: %s",
- objid, getObjectDescriptionOids(classId, objid));
+ elog(ERROR, "cache lookup failed for object %u of catalog \"%s\"",
+ objid, RelationGetRelationName(rel));
- name = heap_getattr(tup, Anum_name, rel->rd_att, &isnull);
- namespace = heap_getattr(tup, Anum_namespace, rel->rd_att, &isnull);
+ name = heap_getattr(tup, Anum_name, RelationGetDescr(rel), &isnull);
+ Assert(!isnull);
+ namespace = heap_getattr(tup, Anum_namespace, RelationGetDescr(rel), &isnull);
+ Assert(!isnull);
oldNspOid = DatumGetObjectId(namespace);
/* Check basic namespace related issues */
CheckSetNamespace(oldNspOid, nspOid, classId, objid);
- /* check for duplicate name (more friendly than unique-index failure) */
- if (SearchSysCacheExists2(cacheId, name, ObjectIdGetDatum(nspOid)))
- ereport(ERROR,
- (errcode(ERRCODE_DUPLICATE_OBJECT),
- errmsg("%s already exists in schema \"%s\"",
- getObjectDescriptionOids(classId, objid),
- get_namespace_name(nspOid))));
-
- /* Superusers can always do it */
+ /* Permission checks ... superusers can always do it */
if (!superuser())
{
Datum owner;
Oid ownerId;
AclResult aclresult;
- if (superuser_only)
+ /* Fail if object does not have an explicit owner */
+ if (Anum_owner <= 0)
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
(errmsg("must be superuser to SET SCHEMA of %s",
getObjectDescriptionOids(classId, objid)))));
/* Otherwise, must be owner of the existing object */
- owner = heap_getattr(tup, Anum_owner, rel->rd_att, &isnull);
+ owner = heap_getattr(tup, Anum_owner, RelationGetDescr(rel), &isnull);
+ Assert(!isnull);
ownerId = DatumGetObjectId(owner);
if (!has_privs_of_role(GetUserId(), ownerId))
aclcheck_error(ACLCHECK_NOT_OWNER, acl_kind,
NameStr(*(DatumGetName(name))));
- /* owner must have CREATE privilege on namespace */
- aclresult = pg_namespace_aclcheck(oldNspOid, GetUserId(), ACL_CREATE);
+ /* User must have CREATE privilege on new namespace */
+ aclresult = pg_namespace_aclcheck(nspOid, GetUserId(), ACL_CREATE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
- get_namespace_name(oldNspOid));
+ get_namespace_name(nspOid));
}
- /* Prepare to update tuple */
- values = palloc0(rel->rd_att->natts * sizeof(Datum));
- nulls = palloc0(rel->rd_att->natts * sizeof(bool));
- replaces = palloc0(rel->rd_att->natts * sizeof(bool));
- values[Anum_namespace - 1] = nspOid;
+ /*
+ * Check for duplicate name (more friendly than unique-index failure).
+ * Since this is just a friendliness check, we can just skip it in cases
+ * where there isn't a suitable syscache available.
+ */
+ if (nameCacheId >= 0 &&
+ SearchSysCacheExists2(nameCacheId, name, ObjectIdGetDatum(nspOid)))
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("%s already exists in schema \"%s\"",
+ getObjectDescriptionOids(classId, objid),
+ get_namespace_name(nspOid))));
+
+ /* Build modified tuple */
+ values = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(Datum));
+ nulls = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(bool));
+ replaces = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(bool));
+ values[Anum_namespace - 1] = ObjectIdGetDatum(nspOid);
replaces[Anum_namespace - 1] = true;
- newtup = heap_modify_tuple(tup, rel->rd_att, values, nulls, replaces);
+ newtup = heap_modify_tuple(tup, RelationGetDescr(rel),
+ values, nulls, replaces);
/* Perform actual update */
simple_heap_update(rel, &tup->t_self, newtup);
@@ -336,6 +458,8 @@ AlterObjectNamespace(Relation rel, int cacheId,
/* update dependencies to point to the new schema */
changeDependencyFor(classId, objid,
NamespaceRelationId, oldNspOid, nspOid);
+
+ return oldNspOid;
}