ALTER TABLE OWNER must change the ownership of the table's rowtype too.
This was not especially critical before, but it is now that we track ownership dependencies --- the dependency for the rowtype *must* shift to the new owner. Spotted by Bernd Helmle. Also fix a problem introduced by recent change to allow non-superusers to do ALTER OWNER in some cases: if the table had a toast table, ALTER OWNER failed *even for superusers*, because the test being applied would conclude that the new would-be owner had no create rights on pg_toast. A side-effect of the fix is to disallow changing the ownership of indexes or toast tables separately from their parent table, which seems a good idea on the whole.
This commit is contained in:
parent
e48b28b688
commit
33f5bf9700
@ -1,5 +1,5 @@
|
|||||||
<!--
|
<!--
|
||||||
$PostgreSQL: pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.78 2005/08/01 16:11:14 tgl Exp $
|
$PostgreSQL: pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.79 2005/08/04 01:09:27 tgl Exp $
|
||||||
PostgreSQL documentation
|
PostgreSQL documentation
|
||||||
-->
|
-->
|
||||||
|
|
||||||
@ -235,7 +235,7 @@ where <replaceable class="PARAMETER">action</replaceable> is one of:
|
|||||||
<term><literal>OWNER</literal></term>
|
<term><literal>OWNER</literal></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
This form changes the owner of the table, index, sequence, or view to the
|
This form changes the owner of the table, sequence, or view to the
|
||||||
specified user.
|
specified user.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.165 2005/08/01 04:03:55 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.166 2005/08/04 01:09:28 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -238,7 +238,7 @@ static void ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
|
|||||||
const char *colName, TypeName *typename);
|
const char *colName, TypeName *typename);
|
||||||
static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab);
|
static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab);
|
||||||
static void ATPostAlterTypeParse(char *cmd, List **wqueue);
|
static void ATPostAlterTypeParse(char *cmd, List **wqueue);
|
||||||
static void ATExecChangeOwner(Oid relationOid, Oid newOwnerId);
|
static void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing);
|
||||||
static void change_owner_recurse_to_sequences(Oid relationOid,
|
static void change_owner_recurse_to_sequences(Oid relationOid,
|
||||||
Oid newOwnerId);
|
Oid newOwnerId);
|
||||||
static void ATExecClusterOn(Relation rel, const char *indexName);
|
static void ATExecClusterOn(Relation rel, const char *indexName);
|
||||||
@ -2141,7 +2141,8 @@ ATExecCmd(AlteredTableInfo *tab, Relation rel, AlterTableCmd *cmd)
|
|||||||
break;
|
break;
|
||||||
case AT_ChangeOwner: /* ALTER OWNER */
|
case AT_ChangeOwner: /* ALTER OWNER */
|
||||||
ATExecChangeOwner(RelationGetRelid(rel),
|
ATExecChangeOwner(RelationGetRelid(rel),
|
||||||
get_roleid_checked(cmd->name));
|
get_roleid_checked(cmd->name),
|
||||||
|
false);
|
||||||
break;
|
break;
|
||||||
case AT_ClusterOn: /* CLUSTER ON */
|
case AT_ClusterOn: /* CLUSTER ON */
|
||||||
ATExecClusterOn(rel, cmd->name);
|
ATExecClusterOn(rel, cmd->name);
|
||||||
@ -5238,9 +5239,15 @@ ATPostAlterTypeParse(char *cmd, List **wqueue)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* ALTER TABLE OWNER
|
* ALTER TABLE OWNER
|
||||||
|
*
|
||||||
|
* recursing is true if we are recursing from a table to its indexes or
|
||||||
|
* toast table. We don't allow the ownership of those things to be
|
||||||
|
* changed separately from the parent table. Also, we can skip permission
|
||||||
|
* checks (this is necessary not just an optimization, else we'd fail to
|
||||||
|
* handle toast tables properly).
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
ATExecChangeOwner(Oid relationOid, Oid newOwnerId)
|
ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing)
|
||||||
{
|
{
|
||||||
Relation target_rel;
|
Relation target_rel;
|
||||||
Relation class_rel;
|
Relation class_rel;
|
||||||
@ -5267,16 +5274,19 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId)
|
|||||||
switch (tuple_class->relkind)
|
switch (tuple_class->relkind)
|
||||||
{
|
{
|
||||||
case RELKIND_RELATION:
|
case RELKIND_RELATION:
|
||||||
case RELKIND_INDEX:
|
|
||||||
case RELKIND_VIEW:
|
case RELKIND_VIEW:
|
||||||
case RELKIND_SEQUENCE:
|
case RELKIND_SEQUENCE:
|
||||||
case RELKIND_TOASTVALUE:
|
|
||||||
/* ok to change owner */
|
/* ok to change owner */
|
||||||
break;
|
break;
|
||||||
|
case RELKIND_INDEX:
|
||||||
|
case RELKIND_TOASTVALUE:
|
||||||
|
if (recursing)
|
||||||
|
break;
|
||||||
|
/* FALL THRU */
|
||||||
default:
|
default:
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||||
errmsg("\"%s\" is not a table, TOAST table, index, view, or sequence",
|
errmsg("\"%s\" is not a table, view, or sequence",
|
||||||
NameStr(tuple_class->relname))));
|
NameStr(tuple_class->relname))));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5293,23 +5303,28 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId)
|
|||||||
Datum aclDatum;
|
Datum aclDatum;
|
||||||
bool isNull;
|
bool isNull;
|
||||||
HeapTuple newtuple;
|
HeapTuple newtuple;
|
||||||
Oid namespaceOid = tuple_class->relnamespace;
|
|
||||||
AclResult aclresult;
|
|
||||||
|
|
||||||
/* Otherwise, must be owner of the existing object */
|
/* skip permission checks when recursing to index or toast table */
|
||||||
if (!pg_class_ownercheck(relationOid,GetUserId()))
|
if (!recursing)
|
||||||
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
|
{
|
||||||
RelationGetRelationName(target_rel));
|
Oid namespaceOid = tuple_class->relnamespace;
|
||||||
|
AclResult aclresult;
|
||||||
|
|
||||||
/* Must be able to become new owner */
|
/* Otherwise, must be owner of the existing object */
|
||||||
check_is_member_of_role(GetUserId(), newOwnerId);
|
if (!pg_class_ownercheck(relationOid,GetUserId()))
|
||||||
|
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
|
||||||
|
RelationGetRelationName(target_rel));
|
||||||
|
|
||||||
/* New owner must have CREATE privilege on namespace */
|
/* Must be able to become new owner */
|
||||||
aclresult = pg_namespace_aclcheck(namespaceOid, newOwnerId,
|
check_is_member_of_role(GetUserId(), newOwnerId);
|
||||||
ACL_CREATE);
|
|
||||||
if (aclresult != ACLCHECK_OK)
|
/* New owner must have CREATE privilege on namespace */
|
||||||
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
|
aclresult = pg_namespace_aclcheck(namespaceOid, newOwnerId,
|
||||||
get_namespace_name(namespaceOid));
|
ACL_CREATE);
|
||||||
|
if (aclresult != ACLCHECK_OK)
|
||||||
|
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
|
||||||
|
get_namespace_name(namespaceOid));
|
||||||
|
}
|
||||||
|
|
||||||
memset(repl_null, ' ', sizeof(repl_null));
|
memset(repl_null, ' ', sizeof(repl_null));
|
||||||
memset(repl_repl, ' ', sizeof(repl_repl));
|
memset(repl_repl, ' ', sizeof(repl_repl));
|
||||||
@ -5342,6 +5357,12 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId)
|
|||||||
/* Update owner dependency reference */
|
/* Update owner dependency reference */
|
||||||
changeDependencyOnOwner(RelationRelationId, relationOid, newOwnerId);
|
changeDependencyOnOwner(RelationRelationId, relationOid, newOwnerId);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Also change the ownership of the table's rowtype, if it has one
|
||||||
|
*/
|
||||||
|
if (tuple_class->relkind != RELKIND_INDEX)
|
||||||
|
AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we are operating on a table, also change the ownership of
|
* If we are operating on a table, also change the ownership of
|
||||||
* any indexes and sequences that belong to the table, as well as
|
* any indexes and sequences that belong to the table, as well as
|
||||||
@ -5358,7 +5379,7 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId)
|
|||||||
|
|
||||||
/* For each index, recursively change its ownership */
|
/* For each index, recursively change its ownership */
|
||||||
foreach(i, index_oid_list)
|
foreach(i, index_oid_list)
|
||||||
ATExecChangeOwner(lfirst_oid(i), newOwnerId);
|
ATExecChangeOwner(lfirst_oid(i), newOwnerId, true);
|
||||||
|
|
||||||
list_free(index_oid_list);
|
list_free(index_oid_list);
|
||||||
}
|
}
|
||||||
@ -5367,7 +5388,8 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId)
|
|||||||
{
|
{
|
||||||
/* If it has a toast table, recurse to change its ownership */
|
/* If it has a toast table, recurse to change its ownership */
|
||||||
if (tuple_class->reltoastrelid != InvalidOid)
|
if (tuple_class->reltoastrelid != InvalidOid)
|
||||||
ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId);
|
ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
|
||||||
|
true);
|
||||||
|
|
||||||
/* If it has dependent sequences, recurse to change them too */
|
/* If it has dependent sequences, recurse to change them too */
|
||||||
change_owner_recurse_to_sequences(relationOid, newOwnerId);
|
change_owner_recurse_to_sequences(relationOid, newOwnerId);
|
||||||
@ -5437,7 +5459,7 @@ change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* We don't need to close the sequence while we alter it. */
|
/* We don't need to close the sequence while we alter it. */
|
||||||
ATExecChangeOwner(depForm->objid, newOwnerId);
|
ATExecChangeOwner(depForm->objid, newOwnerId, false);
|
||||||
|
|
||||||
/* Now we can close it. Keep the lock till end of transaction. */
|
/* Now we can close it. Keep the lock till end of transaction. */
|
||||||
relation_close(seqRel, NoLock);
|
relation_close(seqRel, NoLock);
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.77 2005/08/01 04:03:55 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.78 2005/08/04 01:09:28 tgl Exp $
|
||||||
*
|
*
|
||||||
* DESCRIPTION
|
* DESCRIPTION
|
||||||
* The "DefineFoo" routines take the parse tree and pick out the
|
* The "DefineFoo" routines take the parse tree and pick out the
|
||||||
@ -2057,7 +2057,8 @@ AlterTypeOwner(List *names, Oid newOwnerId)
|
|||||||
* free-standing composite type, and not a table's underlying type. We
|
* free-standing composite type, and not a table's underlying type. We
|
||||||
* want people to use ALTER TABLE not ALTER TYPE for that case.
|
* want people to use ALTER TABLE not ALTER TYPE for that case.
|
||||||
*/
|
*/
|
||||||
if (typTup->typtype == 'c' && get_rel_relkind(typTup->typrelid) != 'c')
|
if (typTup->typtype == 'c' &&
|
||||||
|
get_rel_relkind(typTup->typrelid) != RELKIND_COMPOSITE_TYPE)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||||
errmsg("\"%s\" is a table's row type",
|
errmsg("\"%s\" is a table's row type",
|
||||||
@ -2102,6 +2103,45 @@ AlterTypeOwner(List *names, Oid newOwnerId)
|
|||||||
heap_close(rel, RowExclusiveLock);
|
heap_close(rel, RowExclusiveLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AlterTypeOwnerInternal - change type owner unconditionally
|
||||||
|
*
|
||||||
|
* This is currently only used to propagate ALTER TABLE OWNER to the
|
||||||
|
* table's rowtype. It assumes the caller has done all needed checks.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId)
|
||||||
|
{
|
||||||
|
Relation rel;
|
||||||
|
HeapTuple tup;
|
||||||
|
Form_pg_type typTup;
|
||||||
|
|
||||||
|
rel = heap_open(TypeRelationId, RowExclusiveLock);
|
||||||
|
|
||||||
|
tup = SearchSysCacheCopy(TYPEOID,
|
||||||
|
ObjectIdGetDatum(typeOid),
|
||||||
|
0, 0, 0);
|
||||||
|
if (!HeapTupleIsValid(tup))
|
||||||
|
elog(ERROR, "cache lookup failed for type %u", typeOid);
|
||||||
|
typTup = (Form_pg_type) GETSTRUCT(tup);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Modify the owner --- okay to scribble on typTup because it's a
|
||||||
|
* copy
|
||||||
|
*/
|
||||||
|
typTup->typowner = newOwnerId;
|
||||||
|
|
||||||
|
simple_heap_update(rel, &tup->t_self, tup);
|
||||||
|
|
||||||
|
CatalogUpdateIndexes(rel, tup);
|
||||||
|
|
||||||
|
/* Update owner dependency reference */
|
||||||
|
changeDependencyOnOwner(TypeRelationId, typeOid, newOwnerId);
|
||||||
|
|
||||||
|
/* Clean up */
|
||||||
|
heap_close(rel, RowExclusiveLock);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Execute ALTER TYPE SET SCHEMA
|
* Execute ALTER TYPE SET SCHEMA
|
||||||
*/
|
*/
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/commands/typecmds.h,v 1.12 2005/08/01 04:03:58 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/commands/typecmds.h,v 1.13 2005/08/04 01:09:29 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -35,6 +35,7 @@ extern void AlterDomainDropConstraint(List *names, const char *constrName,
|
|||||||
extern List *GetDomainConstraints(Oid typeOid);
|
extern List *GetDomainConstraints(Oid typeOid);
|
||||||
|
|
||||||
extern void AlterTypeOwner(List *names, Oid newOwnerId);
|
extern void AlterTypeOwner(List *names, Oid newOwnerId);
|
||||||
|
extern void AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId);
|
||||||
extern void AlterTypeNamespace(List *names, const char *newschema);
|
extern void AlterTypeNamespace(List *names, const char *newschema);
|
||||||
extern void AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
|
extern void AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
|
||||||
bool errorOnTableType);
|
bool errorOnTableType);
|
||||||
|
@ -5,7 +5,9 @@ CREATE USER regression_user;
|
|||||||
CREATE USER regression_user2;
|
CREATE USER regression_user2;
|
||||||
CREATE USER regression_user3;
|
CREATE USER regression_user3;
|
||||||
CREATE GROUP regression_group;
|
CREATE GROUP regression_group;
|
||||||
CREATE TABLE deptest ();
|
CREATE TABLE deptest (f1 serial primary key, f2 text);
|
||||||
|
NOTICE: CREATE TABLE will create implicit sequence "deptest_f1_seq" for serial column "deptest.f1"
|
||||||
|
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "deptest_pkey" for table "deptest"
|
||||||
GRANT SELECT ON TABLE deptest TO GROUP regression_group;
|
GRANT SELECT ON TABLE deptest TO GROUP regression_group;
|
||||||
GRANT ALL ON TABLE deptest TO regression_user, regression_user2;
|
GRANT ALL ON TABLE deptest TO regression_user, regression_user2;
|
||||||
-- can't drop neither because they have privileges somewhere
|
-- can't drop neither because they have privileges somewhere
|
||||||
@ -30,10 +32,12 @@ DROP USER regression_user;
|
|||||||
REVOKE ALL ON deptest FROM regression_user2;
|
REVOKE ALL ON deptest FROM regression_user2;
|
||||||
DROP USER regression_user2;
|
DROP USER regression_user2;
|
||||||
-- can't drop the owner of an object
|
-- can't drop the owner of an object
|
||||||
|
-- the error message detail here would include a pg_toast_nnn name that
|
||||||
|
-- is not constant, so suppress it
|
||||||
|
\set VERBOSITY terse
|
||||||
ALTER TABLE deptest OWNER TO regression_user3;
|
ALTER TABLE deptest OWNER TO regression_user3;
|
||||||
DROP USER regression_user3;
|
DROP USER regression_user3;
|
||||||
ERROR: role "regression_user3" cannot be dropped because some objects depend on it
|
ERROR: role "regression_user3" cannot be dropped because some objects depend on it
|
||||||
DETAIL: owner of table deptest
|
|
||||||
-- if we drop the object, we can drop the user too
|
-- if we drop the object, we can drop the user too
|
||||||
DROP TABLE deptest;
|
DROP TABLE deptest;
|
||||||
DROP USER regression_user3;
|
DROP USER regression_user3;
|
||||||
|
@ -7,7 +7,7 @@ CREATE USER regression_user2;
|
|||||||
CREATE USER regression_user3;
|
CREATE USER regression_user3;
|
||||||
CREATE GROUP regression_group;
|
CREATE GROUP regression_group;
|
||||||
|
|
||||||
CREATE TABLE deptest ();
|
CREATE TABLE deptest (f1 serial primary key, f2 text);
|
||||||
|
|
||||||
GRANT SELECT ON TABLE deptest TO GROUP regression_group;
|
GRANT SELECT ON TABLE deptest TO GROUP regression_group;
|
||||||
GRANT ALL ON TABLE deptest TO regression_user, regression_user2;
|
GRANT ALL ON TABLE deptest TO regression_user, regression_user2;
|
||||||
@ -33,6 +33,9 @@ REVOKE ALL ON deptest FROM regression_user2;
|
|||||||
DROP USER regression_user2;
|
DROP USER regression_user2;
|
||||||
|
|
||||||
-- can't drop the owner of an object
|
-- can't drop the owner of an object
|
||||||
|
-- the error message detail here would include a pg_toast_nnn name that
|
||||||
|
-- is not constant, so suppress it
|
||||||
|
\set VERBOSITY terse
|
||||||
ALTER TABLE deptest OWNER TO regression_user3;
|
ALTER TABLE deptest OWNER TO regression_user3;
|
||||||
DROP USER regression_user3;
|
DROP USER regression_user3;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user