*** a/src/backend/catalog/namespace.c --- b/src/backend/catalog/namespace.c *************** *** 211,217 **** Datum pg_is_other_temp_schema(PG_FUNCTION_ARGS); /* ! * RangeVarGetRelid * Given a RangeVar describing an existing relation, * select the proper namespace and look up the relation OID. * --- 211,217 ---- /* ! * RangeVarGetRelidExtended * Given a RangeVar describing an existing relation, * select the proper namespace and look up the relation OID. * *************** *** 409,414 **** RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode, --- 409,427 ---- } /* + * RangeVarCallbackCheckOwner + * RangeVarGetRelidExtended() callback to check ownership alone. + */ + void + RangeVarCallbackCheckOwner(const RangeVar *relation, + Oid relId, Oid oldRelId, void *arg) + { + /* Nothing to do if the relation was not found. */ + if (OidIsValid(relId) && !pg_class_ownercheck(relId, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, relation->relname); + } + + /* * RangeVarGetCreationNamespace * Given a RangeVar describing a to-be-created relation, * choose which namespace to create it in. *** a/src/backend/commands/alter.c --- b/src/backend/commands/alter.c *************** *** 105,166 **** ExecRenameStmt(RenameStmt *stmt) case OBJECT_SEQUENCE: case OBJECT_VIEW: case OBJECT_INDEX: case OBJECT_COLUMN: case OBJECT_ATTRIBUTE: case OBJECT_TRIGGER: ! case OBJECT_FOREIGN_TABLE: ! { ! Oid relid; ! ! CheckRelationOwnership(stmt->relation, true); ! ! /* ! * Lock level used here should match what will be taken later, ! * in RenameRelation, renameatt, or renametrig. ! */ ! relid = RangeVarGetRelid(stmt->relation, AccessExclusiveLock, ! false); ! ! switch (stmt->renameType) ! { ! case OBJECT_TABLE: ! case OBJECT_SEQUENCE: ! case OBJECT_VIEW: ! case OBJECT_INDEX: ! case OBJECT_FOREIGN_TABLE: ! { ! /* ! * RENAME TABLE requires that we (still) hold ! * CREATE rights on the containing namespace, as ! * well as ownership of the table. ! */ ! Oid namespaceId = get_rel_namespace(relid); ! AclResult aclresult; ! ! aclresult = pg_namespace_aclcheck(namespaceId, ! GetUserId(), ! ACL_CREATE); ! if (aclresult != ACLCHECK_OK) ! aclcheck_error(aclresult, ACL_KIND_NAMESPACE, ! get_namespace_name(namespaceId)); ! ! RenameRelation(relid, stmt->newname, stmt->renameType); ! break; ! } ! case OBJECT_COLUMN: ! case OBJECT_ATTRIBUTE: ! renameatt(relid, stmt); ! break; ! case OBJECT_TRIGGER: ! renametrig(relid, ! stmt->subname, /* old att name */ ! stmt->newname); /* new att name */ ! break; ! default: ! /* can't happen */ ; ! } ! break; ! } case OBJECT_TSPARSER: RenameTSParser(stmt->object, stmt->newname); --- 105,122 ---- case OBJECT_SEQUENCE: case OBJECT_VIEW: case OBJECT_INDEX: + case OBJECT_FOREIGN_TABLE: + RenameRelation(stmt); + break; + case OBJECT_COLUMN: case OBJECT_ATTRIBUTE: + renameatt(stmt); + break; + case OBJECT_TRIGGER: ! renametrig(stmt); ! break; case OBJECT_TSPARSER: RenameTSParser(stmt->object, stmt->newname); *************** *** 235,241 **** ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt) case OBJECT_TABLE: case OBJECT_VIEW: case OBJECT_FOREIGN_TABLE: - CheckRelationOwnership(stmt->relation, true); AlterTableNamespace(stmt->relation, stmt->newschema, stmt->objectType, AccessExclusiveLock); break; --- 191,196 ---- *** a/src/backend/commands/cluster.c --- b/src/backend/commands/cluster.c *************** *** 1474,1501 **** finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, { Relation toastrel; Oid toastidx; - Oid toastnamespace; char NewToastName[NAMEDATALEN]; toastrel = relation_open(newrel->rd_rel->reltoastrelid, AccessShareLock); toastidx = toastrel->rd_rel->reltoastidxid; - toastnamespace = toastrel->rd_rel->relnamespace; relation_close(toastrel, AccessShareLock); /* rename the toast table ... */ snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u", OIDOldHeap); RenameRelationInternal(newrel->rd_rel->reltoastrelid, ! NewToastName, ! toastnamespace); /* ... and its index too */ snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u_index", OIDOldHeap); RenameRelationInternal(toastidx, ! NewToastName, ! toastnamespace); } relation_close(newrel, NoLock); } --- 1474,1497 ---- { Relation toastrel; Oid toastidx; char NewToastName[NAMEDATALEN]; toastrel = relation_open(newrel->rd_rel->reltoastrelid, AccessShareLock); toastidx = toastrel->rd_rel->reltoastidxid; relation_close(toastrel, AccessShareLock); /* rename the toast table ... */ snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u", OIDOldHeap); RenameRelationInternal(newrel->rd_rel->reltoastrelid, ! NewToastName); /* ... and its index too */ snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u_index", OIDOldHeap); RenameRelationInternal(toastidx, ! NewToastName); } relation_close(newrel, NoLock); } *** a/src/backend/commands/indexcmds.c --- b/src/backend/commands/indexcmds.c *************** *** 63,70 **** static void ComputeIndexAttrs(IndexInfo *indexInfo, static Oid GetIndexOpClass(List *opclass, Oid attrType, char *accessMethodName, Oid accessMethodId); static char *ChooseIndexNameAddition(List *colnames); - static void RangeVarCallbackForReindexTable(const RangeVar *relation, - Oid relId, Oid oldRelId, void *arg); static void RangeVarCallbackForReindexIndex(const RangeVar *relation, Oid relId, Oid oldRelId, void *arg); --- 63,68 ---- *************** *** 1806,1815 **** void ReindexTable(RangeVar *relation) { Oid heapOid; /* The lock level used here should match reindex_relation(). */ heapOid = RangeVarGetRelidExtended(relation, ShareLock, false, false, ! RangeVarCallbackForReindexTable, NULL); if (!reindex_relation(heapOid, REINDEX_REL_PROCESS_TOAST)) ereport(NOTICE, --- 1804,1826 ---- ReindexTable(RangeVar *relation) { Oid heapOid; + HeapTuple tuple; /* The lock level used here should match reindex_relation(). */ heapOid = RangeVarGetRelidExtended(relation, ShareLock, false, false, ! RangeVarCallbackCheckOwner, NULL); ! tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(heapOid)); ! if (!HeapTupleIsValid(tuple)) /* shouldn't happen */ ! elog(ERROR, "cache lookup failed for relation %u", heapOid); ! ! if (((Form_pg_class) GETSTRUCT(tuple))->relkind != RELKIND_RELATION && ! ((Form_pg_class) GETSTRUCT(tuple))->relkind != RELKIND_TOASTVALUE) ! ereport(ERROR, ! (errcode(ERRCODE_WRONG_OBJECT_TYPE), ! errmsg("\"%s\" is not a table", ! relation->relname))); ! ! ReleaseSysCache(tuple); if (!reindex_relation(heapOid, REINDEX_REL_PROCESS_TOAST)) ereport(NOTICE, *************** *** 1818,1854 **** ReindexTable(RangeVar *relation) } /* - * Check permissions on table before acquiring relation lock. - */ - static void - RangeVarCallbackForReindexTable(const RangeVar *relation, - Oid relId, Oid oldRelId, void *arg) - { - char relkind; - - /* Nothing to do if the relation was not found. */ - if (!OidIsValid(relId)) - return; - - /* - * If the relation does exist, check whether it's an index. But note - * that the relation might have been dropped between the time we did the - * name lookup and now. In that case, there's nothing to do. - */ - relkind = get_rel_relkind(relId); - if (!relkind) - return; - if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table", relation->relname))); - - /* Check permissions */ - if (!pg_class_ownercheck(relId, GetUserId())) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, relation->relname); - } - - /* * ReindexDatabase * Recreate indexes of a database. * --- 1829,1834 ---- *** a/src/backend/commands/tablecmds.c --- b/src/backend/commands/tablecmds.c *************** *** 2257,2264 **** renameatt_internal(Oid myrelid, * renameatt - changes the name of a attribute in a relation */ void ! renameatt(Oid myrelid, RenameStmt *stmt) { renameatt_internal(myrelid, stmt->subname, /* old att name */ stmt->newname, /* new att name */ --- 2257,2270 ---- * renameatt - changes the name of a attribute in a relation */ void ! renameatt(RenameStmt *stmt) { + Oid myrelid; + + /* lock level taken here should match renameatt_internal */ + myrelid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock, + false, false, + RangeVarCallbackCheckOwner, NULL); renameatt_internal(myrelid, stmt->subname, /* old att name */ stmt->newname, /* new att name */ *************** *** 2269,2295 **** renameatt(Oid myrelid, RenameStmt *stmt) } ! /* ! * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/FOREIGN TABLE RENAME ! * ! * Caller has already done permissions checks. ! */ void ! RenameRelation(Oid myrelid, const char *newrelname, ObjectType reltype) { Relation targetrelation; Oid namespaceId; char relkind; /* * Grab an exclusive lock on the target table, index, sequence or view, * which we will NOT release until end of transaction. * ! * Lock level used here should match ExecRenameStmt */ targetrelation = relation_open(myrelid, AccessExclusiveLock); namespaceId = RelationGetNamespace(targetrelation); relkind = targetrelation->rd_rel->relkind; /* --- 2275,2316 ---- } ! /* Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/FOREIGN TABLE RENAME */ void ! RenameRelation(RenameStmt *stmt) { + Oid myrelid; Relation targetrelation; Oid namespaceId; + AclResult aclresult; char relkind; + ObjectType reltype = stmt->renameType; /* * Grab an exclusive lock on the target table, index, sequence or view, * which we will NOT release until end of transaction. * ! * Lock level used here should match RenameRelationInternal, to avoid ! * lock escalation. */ + myrelid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock, + false, false, + RangeVarCallbackCheckOwner, NULL); targetrelation = relation_open(myrelid, AccessExclusiveLock); + if (!allowSystemTableMods && IsSystemRelation(targetrelation)) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied: \"%s\" is a system catalog", + RelationGetRelationName(targetrelation)))); + + /* Must (still) have CREATE rights on containing namespace. */ namespaceId = RelationGetNamespace(targetrelation); + aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_CREATE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceId)); + relkind = targetrelation->rd_rel->relkind; /* *************** *** 2328,2334 **** RenameRelation(Oid myrelid, const char *newrelname, ObjectType reltype) errhint("Use ALTER TYPE instead."))); /* Do the work */ ! RenameRelationInternal(myrelid, newrelname, namespaceId); /* * Close rel, but keep exclusive lock! --- 2349,2355 ---- errhint("Use ALTER TYPE instead."))); /* Do the work */ ! RenameRelationInternal(myrelid, stmt->newname); /* * Close rel, but keep exclusive lock! *************** *** 2346,2363 **** RenameRelation(Oid myrelid, const char *newrelname, ObjectType reltype) * sequence, AFAIK there's no need for it to be there. */ void ! RenameRelationInternal(Oid myrelid, const char *newrelname, Oid namespaceId) { Relation targetrelation; Relation relrelation; /* for RELATION relation */ HeapTuple reltup; Form_pg_class relform; /* * Grab an exclusive lock on the target table, index, sequence or view, * which we will NOT release until end of transaction. */ targetrelation = relation_open(myrelid, AccessExclusiveLock); /* * Find relation's pg_class tuple, and make sure newrelname isn't in use. --- 2367,2386 ---- * sequence, AFAIK there's no need for it to be there. */ void ! RenameRelationInternal(Oid myrelid, const char *newrelname) { Relation targetrelation; Relation relrelation; /* for RELATION relation */ HeapTuple reltup; Form_pg_class relform; + Oid namespaceId; /* * Grab an exclusive lock on the target table, index, sequence or view, * which we will NOT release until end of transaction. */ targetrelation = relation_open(myrelid, AccessExclusiveLock); + namespaceId = RelationGetNamespace(targetrelation); /* * Find relation's pg_class tuple, and make sure newrelname isn't in use. *************** *** 5376,5382 **** ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel, ereport(NOTICE, (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"", indexName, constraintName))); ! RenameRelation(index_oid, constraintName, OBJECT_INDEX); } /* Extra checks needed if making primary key */ --- 5399,5405 ---- ereport(NOTICE, (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"", indexName, constraintName))); ! RenameRelationInternal(index_oid, constraintName); } /* Extra checks needed if making primary key */ *************** *** 9316,9326 **** ATExecGenericOptions(Relation rel, List *options) } ! /* ! * Execute ALTER TABLE SET SCHEMA ! * ! * Note: caller must have checked ownership of the relation already ! */ void AlterTableNamespace(RangeVar *relation, const char *newschema, ObjectType stmttype, LOCKMODE lockmode) --- 9339,9345 ---- } ! /* Execute ALTER TABLE SET SCHEMA */ void AlterTableNamespace(RangeVar *relation, const char *newschema, ObjectType stmttype, LOCKMODE lockmode) *************** *** 9331,9339 **** AlterTableNamespace(RangeVar *relation, const char *newschema, Oid nspOid; Relation classRel; rel = relation_openrv(relation, lockmode); ! relid = RelationGetRelid(rel); oldNspOid = RelationGetNamespace(rel); /* Check relation type against type specified in the ALTER command */ --- 9350,9365 ---- Oid nspOid; Relation classRel; + relid = RangeVarGetRelidExtended(relation, lockmode, false, false, + RangeVarCallbackCheckOwner, NULL); rel = relation_openrv(relation, lockmode); ! if (!allowSystemTableMods && IsSystemRelation(rel)) ! ereport(ERROR, ! (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ! errmsg("permission denied: \"%s\" is a system catalog", ! RelationGetRelationName(rel)))); ! oldNspOid = RelationGetNamespace(rel); /* Check relation type against type specified in the ALTER command */ *** a/src/backend/commands/trigger.c --- b/src/backend/commands/trigger.c *************** *** 1165,1185 **** get_trigger_oid(Oid relid, const char *trigname, bool missing_ok) * update row in catalog */ void ! renametrig(Oid relid, ! const char *oldname, ! const char *newname) { Relation targetrel; Relation tgrel; HeapTuple tuple; SysScanDesc tgscan; ScanKeyData key[2]; /* ! * Grab an exclusive lock on the target table, which we will NOT release ! * until end of transaction. */ ! targetrel = heap_open(relid, AccessExclusiveLock); /* * Scan pg_trigger twice for existing triggers on relation. We do this in --- 1165,1195 ---- * update row in catalog */ void ! renametrig(RenameStmt *stmt) { Relation targetrel; Relation tgrel; HeapTuple tuple; SysScanDesc tgscan; ScanKeyData key[2]; + Oid relid; /* ! * Look up name, check permissions, and acquire lock (which we will NOT ! * release until end of transaction). */ ! relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock, ! false, false, ! RangeVarCallbackCheckOwner, NULL); ! ! /* Have lock already, so just need to build relcache entry. */ ! targetrel = heap_open(relid, NoLock); ! ! if (!allowSystemTableMods && IsSystemRelation(targetrel)) ! ereport(ERROR, ! (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ! errmsg("permission denied: \"%s\" is a system catalog", ! RelationGetRelationName(targetrel)))); /* * Scan pg_trigger twice for existing triggers on relation. We do this in *************** *** 1202,1215 **** renametrig(Oid relid, ScanKeyInit(&key[1], Anum_pg_trigger_tgname, BTEqualStrategyNumber, F_NAMEEQ, ! PointerGetDatum(newname)); tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true, SnapshotNow, 2, key); if (HeapTupleIsValid(tuple = systable_getnext(tgscan))) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("trigger \"%s\" for relation \"%s\" already exists", ! newname, RelationGetRelationName(targetrel)))); systable_endscan(tgscan); /* --- 1212,1225 ---- ScanKeyInit(&key[1], Anum_pg_trigger_tgname, BTEqualStrategyNumber, F_NAMEEQ, ! PointerGetDatum(stmt->newname)); tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true, SnapshotNow, 2, key); if (HeapTupleIsValid(tuple = systable_getnext(tgscan))) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("trigger \"%s\" for relation \"%s\" already exists", ! stmt->newname, RelationGetRelationName(targetrel)))); systable_endscan(tgscan); /* *************** *** 1222,1228 **** renametrig(Oid relid, ScanKeyInit(&key[1], Anum_pg_trigger_tgname, BTEqualStrategyNumber, F_NAMEEQ, ! PointerGetDatum(oldname)); tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true, SnapshotNow, 2, key); if (HeapTupleIsValid(tuple = systable_getnext(tgscan))) --- 1232,1238 ---- ScanKeyInit(&key[1], Anum_pg_trigger_tgname, BTEqualStrategyNumber, F_NAMEEQ, ! PointerGetDatum(stmt->subname)); tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true, SnapshotNow, 2, key); if (HeapTupleIsValid(tuple = systable_getnext(tgscan))) *************** *** 1232,1238 **** renametrig(Oid relid, */ tuple = heap_copytuple(tuple); /* need a modifiable copy */ ! namestrcpy(&((Form_pg_trigger) GETSTRUCT(tuple))->tgname, newname); simple_heap_update(tgrel, &tuple->t_self, tuple); --- 1242,1249 ---- */ tuple = heap_copytuple(tuple); /* need a modifiable copy */ ! namestrcpy(&((Form_pg_trigger) GETSTRUCT(tuple))->tgname, ! stmt->newname); simple_heap_update(tgrel, &tuple->t_self, tuple); *************** *** 1251,1257 **** renametrig(Oid relid, ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("trigger \"%s\" for table \"%s\" does not exist", ! oldname, RelationGetRelationName(targetrel)))); } systable_endscan(tgscan); --- 1262,1268 ---- ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("trigger \"%s\" for table \"%s\" does not exist", ! stmt->subname, RelationGetRelationName(targetrel)))); } systable_endscan(tgscan); *** a/src/backend/commands/typecmds.c --- b/src/backend/commands/typecmds.c *************** *** 3121,3128 **** RenameType(List *names, const char *newTypeName) * RenameRelationInternal will call RenameTypeInternal automatically. */ if (typTup->typtype == TYPTYPE_COMPOSITE) ! RenameRelationInternal(typTup->typrelid, newTypeName, ! typTup->typnamespace); else RenameTypeInternal(typeOid, newTypeName, typTup->typnamespace); --- 3121,3127 ---- * RenameRelationInternal will call RenameTypeInternal automatically. */ if (typTup->typtype == TYPTYPE_COMPOSITE) ! RenameRelationInternal(typTup->typrelid, newTypeName); else RenameTypeInternal(typeOid, newTypeName, typTup->typnamespace); *** a/src/include/catalog/namespace.h --- b/src/include/catalog/namespace.h *************** *** 57,62 **** extern Oid RangeVarGetRelidExtended(const RangeVar *relation, --- 57,64 ---- LOCKMODE lockmode, bool missing_ok, bool nowait, RangeVarGetRelidCallback callback, void *callback_arg); + extern void RangeVarCallbackCheckOwner(const RangeVar *relation, + Oid relId, Oid oldRelId, void *arg); extern Oid RangeVarGetCreationNamespace(const RangeVar *newRelation); extern Oid RangeVarGetAndCheckCreationNamespace(const RangeVar *newRelation); extern void RangeVarAdjustRelationPersistence(RangeVar *newRelation, Oid nspid); *** a/src/include/commands/tablecmds.h --- b/src/include/commands/tablecmds.h *************** *** 45,59 **** extern void ExecuteTruncate(TruncateStmt *stmt); extern void SetRelationHasSubclass(Oid relationId, bool relhassubclass); ! extern void renameatt(Oid myrelid, RenameStmt *stmt); ! extern void RenameRelation(Oid myrelid, ! const char *newrelname, ! ObjectType reltype); extern void RenameRelationInternal(Oid myrelid, ! const char *newrelname, ! Oid namespaceId); extern void find_composite_type_dependencies(Oid typeOid, Relation origRelation, --- 45,56 ---- extern void SetRelationHasSubclass(Oid relationId, bool relhassubclass); ! extern void renameatt(RenameStmt *stmt); ! extern void RenameRelation(RenameStmt *stmt); extern void RenameRelationInternal(Oid myrelid, ! const char *newrelname); extern void find_composite_type_dependencies(Oid typeOid, Relation origRelation, *** a/src/include/commands/trigger.h --- b/src/include/commands/trigger.h *************** *** 115,121 **** extern Oid CreateTrigger(CreateTrigStmt *stmt, const char *queryString, extern void RemoveTriggerById(Oid trigOid); extern Oid get_trigger_oid(Oid relid, const char *name, bool missing_ok); ! extern void renametrig(Oid relid, const char *oldname, const char *newname); extern void EnableDisableTrigger(Relation rel, const char *tgname, char fires_when, bool skip_system); --- 115,121 ---- extern void RemoveTriggerById(Oid trigOid); extern Oid get_trigger_oid(Oid relid, const char *name, bool missing_ok); ! extern void renametrig(RenameStmt *stmt); extern void EnableDisableTrigger(Relation rel, const char *tgname, char fires_when, bool skip_system);