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.c288
1 files changed, 236 insertions, 52 deletions
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index ec7e7c2b4ee..8df875b8a83 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -15,10 +15,12 @@
#include "postgres.h"
#include "access/htup_details.h"
+#include "access/sysattr.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/pg_largeobject.h"
+#include "catalog/pg_largeobject_metadata.h"
#include "catalog/pg_namespace.h"
#include "commands/alter.h"
#include "commands/collationcmds.h"
@@ -37,9 +39,11 @@
#include "miscadmin.h"
#include "tcop/utility.h"
#include "utils/builtins.h"
+#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/syscache.h"
+#include "utils/tqual.h"
/*
@@ -446,71 +450,19 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
switch (stmt->objectType)
{
- case OBJECT_AGGREGATE:
- AlterAggregateOwner(stmt->object, stmt->objarg, newowner);
- break;
-
- case OBJECT_COLLATION:
- AlterCollationOwner(stmt->object, newowner);
- break;
-
- case OBJECT_CONVERSION:
- AlterConversionOwner(stmt->object, newowner);
- break;
-
case OBJECT_DATABASE:
AlterDatabaseOwner(strVal(linitial(stmt->object)), newowner);
break;
- case OBJECT_FUNCTION:
- AlterFunctionOwner(stmt->object, stmt->objarg, newowner);
- break;
-
- case OBJECT_LANGUAGE:
- AlterLanguageOwner(strVal(linitial(stmt->object)), newowner);
- break;
-
- case OBJECT_LARGEOBJECT:
- LargeObjectAlterOwner(oidparse(linitial(stmt->object)), newowner);
- break;
-
- case OBJECT_OPERATOR:
- Assert(list_length(stmt->objarg) == 2);
- AlterOperatorOwner(stmt->object,
- (TypeName *) linitial(stmt->objarg),
- (TypeName *) lsecond(stmt->objarg),
- newowner);
- break;
-
- case OBJECT_OPCLASS:
- AlterOpClassOwner(stmt->object, stmt->addname, newowner);
- break;
-
- case OBJECT_OPFAMILY:
- AlterOpFamilyOwner(stmt->object, stmt->addname, newowner);
- break;
-
case OBJECT_SCHEMA:
AlterSchemaOwner(strVal(linitial(stmt->object)), newowner);
break;
- case OBJECT_TABLESPACE:
- AlterTableSpaceOwner(strVal(linitial(stmt->object)), newowner);
- break;
-
case OBJECT_TYPE:
case OBJECT_DOMAIN: /* same as TYPE */
AlterTypeOwner(stmt->object, newowner, stmt->objectType);
break;
- case OBJECT_TSDICTIONARY:
- AlterTSDictionaryOwner(stmt->object, newowner);
- break;
-
- case OBJECT_TSCONFIGURATION:
- AlterTSConfigurationOwner(stmt->object, newowner);
- break;
-
case OBJECT_FDW:
AlterForeignDataWrapperOwner(strVal(linitial(stmt->object)),
newowner);
@@ -524,8 +476,240 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
AlterEventTriggerOwner(strVal(linitial(stmt->object)), newowner);
break;
+ /* Generic cases */
+ case OBJECT_AGGREGATE:
+ case OBJECT_COLLATION:
+ case OBJECT_CONVERSION:
+ case OBJECT_FUNCTION:
+ case OBJECT_LANGUAGE:
+ case OBJECT_LARGEOBJECT:
+ case OBJECT_OPERATOR:
+ case OBJECT_OPCLASS:
+ case OBJECT_OPFAMILY:
+ case OBJECT_TABLESPACE:
+ case OBJECT_TSDICTIONARY:
+ case OBJECT_TSCONFIGURATION:
+ {
+ Relation catalog;
+ Relation relation;
+ Oid classId;
+ ObjectAddress address;
+
+ address = get_object_address(stmt->objectType,
+ stmt->object,
+ stmt->objarg,
+ &relation,
+ AccessExclusiveLock,
+ false);
+ Assert(relation == NULL);
+ classId = address.classId;
+
+ /*
+ * XXX - get_object_address returns Oid of pg_largeobject
+ * catalog for OBJECT_LARGEOBJECT because of historical
+ * reasons. Fix up it here.
+ */
+ if (classId == LargeObjectRelationId)
+ classId = LargeObjectMetadataRelationId;
+
+ catalog = heap_open(classId, RowExclusiveLock);
+
+ AlterObjectOwner_internal(catalog, address.objectId, newowner);
+ heap_close(catalog, RowExclusiveLock);
+ }
+ break;
+
default:
elog(ERROR, "unrecognized AlterOwnerStmt type: %d",
(int) stmt->objectType);
}
}
+
+/*
+ * Return a copy of the tuple for the object with the given object OID, from
+ * the given catalog (which must have been opened by the caller and suitably
+ * locked). NULL is returned if the OID is not found.
+ *
+ * We try a syscache first, if available.
+ *
+ * XXX this function seems general in possible usage. Given sufficient callers
+ * elsewhere, we should consider moving it to a more appropriate place.
+ */
+static HeapTuple
+get_catalog_object_by_oid(Relation catalog, Oid objectId)
+{
+ HeapTuple tuple;
+ Oid classId = RelationGetRelid(catalog);
+ int oidCacheId = get_object_catcache_oid(classId);
+
+ if (oidCacheId > 0)
+ {
+ tuple = SearchSysCacheCopy1(oidCacheId, ObjectIdGetDatum(objectId));
+ if (!HeapTupleIsValid(tuple)) /* should not happen */
+ return NULL;
+ }
+ else
+ {
+ Oid oidIndexId = get_object_oid_index(classId);
+ SysScanDesc scan;
+ ScanKeyData skey;
+
+ Assert(OidIsValid(oidIndexId));
+
+ ScanKeyInit(&skey,
+ ObjectIdAttributeNumber,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(objectId));
+
+ scan = systable_beginscan(catalog, oidIndexId, true,
+ SnapshotNow, 1, &skey);
+ tuple = systable_getnext(scan);
+ if (!HeapTupleIsValid(tuple))
+ {
+ systable_endscan(scan);
+ return NULL;
+ }
+ tuple = heap_copytuple(tuple);
+
+ systable_endscan(scan);
+ }
+
+ return tuple;
+}
+
+/*
+ * Generic function to change the ownership of a given object, for simple
+ * cases (won't work for tables, nor other cases where we need to do more than
+ * change the ownership column of a single catalog entry).
+ *
+ * rel: catalog relation containing object (RowExclusiveLock'd by caller)
+ * objectId: OID of object to change the ownership of
+ * new_ownerId: OID of new object owner
+ */
+void
+AlterObjectOwner_internal(Relation rel, Oid objectId, Oid new_ownerId)
+{
+ Oid classId = RelationGetRelid(rel);
+ AttrNumber Anum_owner = get_object_attnum_owner(classId);
+ AttrNumber Anum_namespace = get_object_attnum_namespace(classId);
+ AttrNumber Anum_acl = get_object_attnum_acl(classId);
+ AttrNumber Anum_name = get_object_attnum_name(classId);
+ HeapTuple oldtup;
+ Datum datum;
+ bool isnull;
+ Oid old_ownerId;
+ Oid namespaceId = InvalidOid;
+
+ oldtup = get_catalog_object_by_oid(rel, objectId);
+ if (oldtup == NULL)
+ elog(ERROR, "cache lookup failed for object %u of catalog \"%s\"",
+ objectId, RelationGetRelationName(rel));
+
+ datum = heap_getattr(oldtup, Anum_owner,
+ RelationGetDescr(rel), &isnull);
+ Assert(!isnull);
+ old_ownerId = DatumGetObjectId(datum);
+
+ if (Anum_namespace != InvalidAttrNumber)
+ {
+ datum = heap_getattr(oldtup, Anum_namespace,
+ RelationGetDescr(rel), &isnull);
+ Assert(!isnull);
+ namespaceId = DatumGetObjectId(datum);
+ }
+
+ if (old_ownerId != new_ownerId)
+ {
+ AttrNumber nattrs;
+ HeapTuple newtup;
+ Datum *values;
+ bool *nulls;
+ bool *replaces;
+
+ /* Superusers can bypass permission checks */
+ if (!superuser())
+ {
+ AclObjectKind aclkind = get_object_aclkind(classId);
+
+ /* must be owner */
+ if (!has_privs_of_role(GetUserId(), old_ownerId))
+ {
+ char *objname;
+ char namebuf[NAMEDATALEN];
+
+ if (Anum_name != InvalidAttrNumber)
+ {
+ datum = heap_getattr(oldtup, Anum_name,
+ RelationGetDescr(rel), &isnull);
+ Assert(!isnull);
+ objname = NameStr(*DatumGetName(datum));
+ }
+ else
+ {
+ snprintf(namebuf, sizeof(namebuf), "%u",
+ HeapTupleGetOid(oldtup));
+ objname = namebuf;
+ }
+ aclcheck_error(ACLCHECK_NOT_OWNER, aclkind, objname);
+ }
+ /* Must be able to become new owner */
+ check_is_member_of_role(GetUserId(), new_ownerId);
+
+ /* New owner must have CREATE privilege on namespace */
+ if (OidIsValid(namespaceId))
+ {
+ AclResult aclresult;
+
+ aclresult = pg_namespace_aclcheck(namespaceId, new_ownerId,
+ ACL_CREATE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, aclkind,
+ get_namespace_name(namespaceId));
+ }
+ }
+
+ /* Build a modified tuple */
+ nattrs = RelationGetNumberOfAttributes(rel);
+ values = palloc0(nattrs * sizeof(Datum));
+ nulls = palloc0(nattrs * sizeof(bool));
+ replaces = palloc0(nattrs * sizeof(bool));
+ values[Anum_owner - 1] = ObjectIdGetDatum(new_ownerId);
+ replaces[Anum_owner - 1] = true;
+
+ /*
+ * Determine the modified ACL for the new owner. This is only
+ * necessary when the ACL is non-null.
+ */
+ if (Anum_acl != InvalidAttrNumber)
+ {
+ datum = heap_getattr(oldtup,
+ Anum_acl, RelationGetDescr(rel), &isnull);
+ if (!isnull)
+ {
+ Acl *newAcl;
+
+ newAcl = aclnewowner(DatumGetAclP(datum),
+ old_ownerId, new_ownerId);
+ values[Anum_acl - 1] = PointerGetDatum(newAcl);
+ replaces[Anum_acl - 1] = true;
+ }
+ }
+
+ newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel),
+ values, nulls, replaces);
+
+ /* Perform actual update */
+ simple_heap_update(rel, &newtup->t_self, newtup);
+ CatalogUpdateIndexes(rel, newtup);
+
+ /* Update owner dependency reference */
+ if (classId == LargeObjectMetadataRelationId)
+ classId = LargeObjectRelationId;
+ changeDependencyOnOwner(classId, HeapTupleGetOid(newtup), new_ownerId);
+
+ /* Release memory */
+ pfree(values);
+ pfree(nulls);
+ pfree(replaces);
+ }
+}