Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 33f5bf9

Browse files
committed
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.
1 parent e48b28b commit 33f5bf9

File tree

6 files changed

+104
-34
lines changed

6 files changed

+104
-34
lines changed

doc/src/sgml/ref/alter_table.sgml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!--
2-
$PostgreSQL: pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.78 2005/08/01 16:11:14 tgl Exp $
2+
$PostgreSQL: pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.79 2005/08/04 01:09:27 tgl Exp $
33
PostgreSQL documentation
44
-->
55

@@ -235,7 +235,7 @@ where <replaceable class="PARAMETER">action</replaceable> is one of:
235235
<term><literal>OWNER</literal></term>
236236
<listitem>
237237
<para>
238-
This form changes the owner of the table, index, sequence, or view to the
238+
This form changes the owner of the table, sequence, or view to the
239239
specified user.
240240
</para>
241241
</listitem>

src/backend/commands/tablecmds.c

Lines changed: 48 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.165 2005/08/01 04:03:55 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.166 2005/08/04 01:09:28 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -238,7 +238,7 @@ static void ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
238238
const char *colName, TypeName *typename);
239239
static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab);
240240
static void ATPostAlterTypeParse(char *cmd, List **wqueue);
241-
static void ATExecChangeOwner(Oid relationOid, Oid newOwnerId);
241+
static void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing);
242242
static void change_owner_recurse_to_sequences(Oid relationOid,
243243
Oid newOwnerId);
244244
static void ATExecClusterOn(Relation rel, const char *indexName);
@@ -2141,7 +2141,8 @@ ATExecCmd(AlteredTableInfo *tab, Relation rel, AlterTableCmd *cmd)
21412141
break;
21422142
case AT_ChangeOwner: /* ALTER OWNER */
21432143
ATExecChangeOwner(RelationGetRelid(rel),
2144-
get_roleid_checked(cmd->name));
2144+
get_roleid_checked(cmd->name),
2145+
false);
21452146
break;
21462147
case AT_ClusterOn: /* CLUSTER ON */
21472148
ATExecClusterOn(rel, cmd->name);
@@ -5238,9 +5239,15 @@ ATPostAlterTypeParse(char *cmd, List **wqueue)
52385239

52395240
/*
52405241
* ALTER TABLE OWNER
5242+
*
5243+
* recursing is true if we are recursing from a table to its indexes or
5244+
* toast table. We don't allow the ownership of those things to be
5245+
* changed separately from the parent table. Also, we can skip permission
5246+
* checks (this is necessary not just an optimization, else we'd fail to
5247+
* handle toast tables properly).
52415248
*/
52425249
static void
5243-
ATExecChangeOwner(Oid relationOid, Oid newOwnerId)
5250+
ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing)
52445251
{
52455252
Relation target_rel;
52465253
Relation class_rel;
@@ -5267,16 +5274,19 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId)
52675274
switch (tuple_class->relkind)
52685275
{
52695276
case RELKIND_RELATION:
5270-
case RELKIND_INDEX:
52715277
case RELKIND_VIEW:
52725278
case RELKIND_SEQUENCE:
5273-
case RELKIND_TOASTVALUE:
52745279
/* ok to change owner */
52755280
break;
5281+
case RELKIND_INDEX:
5282+
case RELKIND_TOASTVALUE:
5283+
if (recursing)
5284+
break;
5285+
/* FALL THRU */
52765286
default:
52775287
ereport(ERROR,
52785288
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
5279-
errmsg("\"%s\" is not a table, TOAST table, index, view, or sequence",
5289+
errmsg("\"%s\" is not a table, view, or sequence",
52805290
NameStr(tuple_class->relname))));
52815291
}
52825292

@@ -5293,23 +5303,28 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId)
52935303
Datum aclDatum;
52945304
bool isNull;
52955305
HeapTuple newtuple;
5296-
Oid namespaceOid = tuple_class->relnamespace;
5297-
AclResult aclresult;
5298-
5299-
/* Otherwise, must be owner of the existing object */
5300-
if (!pg_class_ownercheck(relationOid,GetUserId()))
5301-
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
5302-
RelationGetRelationName(target_rel));
53035306

5304-
/* Must be able to become new owner */
5305-
check_is_member_of_role(GetUserId(), newOwnerId);
5306-
5307-
/* New owner must have CREATE privilege on namespace */
5308-
aclresult = pg_namespace_aclcheck(namespaceOid, newOwnerId,
5309-
ACL_CREATE);
5310-
if (aclresult != ACLCHECK_OK)
5311-
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
5312-
get_namespace_name(namespaceOid));
5307+
/* skip permission checks when recursing to index or toast table */
5308+
if (!recursing)
5309+
{
5310+
Oid namespaceOid = tuple_class->relnamespace;
5311+
AclResult aclresult;
5312+
5313+
/* Otherwise, must be owner of the existing object */
5314+
if (!pg_class_ownercheck(relationOid,GetUserId()))
5315+
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
5316+
RelationGetRelationName(target_rel));
5317+
5318+
/* Must be able to become new owner */
5319+
check_is_member_of_role(GetUserId(), newOwnerId);
5320+
5321+
/* New owner must have CREATE privilege on namespace */
5322+
aclresult = pg_namespace_aclcheck(namespaceOid, newOwnerId,
5323+
ACL_CREATE);
5324+
if (aclresult != ACLCHECK_OK)
5325+
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
5326+
get_namespace_name(namespaceOid));
5327+
}
53135328

53145329
memset(repl_null, ' ', sizeof(repl_null));
53155330
memset(repl_repl, ' ', sizeof(repl_repl));
@@ -5342,6 +5357,12 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId)
53425357
/* Update owner dependency reference */
53435358
changeDependencyOnOwner(RelationRelationId, relationOid, newOwnerId);
53445359

5360+
/*
5361+
* Also change the ownership of the table's rowtype, if it has one
5362+
*/
5363+
if (tuple_class->relkind != RELKIND_INDEX)
5364+
AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
5365+
53455366
/*
53465367
* If we are operating on a table, also change the ownership of
53475368
* any indexes and sequences that belong to the table, as well as
@@ -5358,7 +5379,7 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId)
53585379

53595380
/* For each index, recursively change its ownership */
53605381
foreach(i, index_oid_list)
5361-
ATExecChangeOwner(lfirst_oid(i), newOwnerId);
5382+
ATExecChangeOwner(lfirst_oid(i), newOwnerId, true);
53625383

53635384
list_free(index_oid_list);
53645385
}
@@ -5367,7 +5388,8 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId)
53675388
{
53685389
/* If it has a toast table, recurse to change its ownership */
53695390
if (tuple_class->reltoastrelid != InvalidOid)
5370-
ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId);
5391+
ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
5392+
true);
53715393

53725394
/* If it has dependent sequences, recurse to change them too */
53735395
change_owner_recurse_to_sequences(relationOid, newOwnerId);
@@ -5437,7 +5459,7 @@ change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId)
54375459
}
54385460

54395461
/* We don't need to close the sequence while we alter it. */
5440-
ATExecChangeOwner(depForm->objid, newOwnerId);
5462+
ATExecChangeOwner(depForm->objid, newOwnerId, false);
54415463

54425464
/* Now we can close it. Keep the lock till end of transaction. */
54435465
relation_close(seqRel, NoLock);

src/backend/commands/typecmds.c

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.77 2005/08/01 04:03:55 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.78 2005/08/04 01:09:28 tgl Exp $
1212
*
1313
* DESCRIPTION
1414
* The "DefineFoo" routines take the parse tree and pick out the
@@ -2057,7 +2057,8 @@ AlterTypeOwner(List *names, Oid newOwnerId)
20572057
* free-standing composite type, and not a table's underlying type. We
20582058
* want people to use ALTER TABLE not ALTER TYPE for that case.
20592059
*/
2060-
if (typTup->typtype == 'c' && get_rel_relkind(typTup->typrelid) != 'c')
2060+
if (typTup->typtype == 'c' &&
2061+
get_rel_relkind(typTup->typrelid) != RELKIND_COMPOSITE_TYPE)
20612062
ereport(ERROR,
20622063
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
20632064
errmsg("\"%s\" is a table's row type",
@@ -2102,6 +2103,45 @@ AlterTypeOwner(List *names, Oid newOwnerId)
21022103
heap_close(rel, RowExclusiveLock);
21032104
}
21042105

2106+
/*
2107+
* AlterTypeOwnerInternal - change type owner unconditionally
2108+
*
2109+
* This is currently only used to propagate ALTER TABLE OWNER to the
2110+
* table's rowtype. It assumes the caller has done all needed checks.
2111+
*/
2112+
void
2113+
AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId)
2114+
{
2115+
Relation rel;
2116+
HeapTuple tup;
2117+
Form_pg_type typTup;
2118+
2119+
rel = heap_open(TypeRelationId, RowExclusiveLock);
2120+
2121+
tup = SearchSysCacheCopy(TYPEOID,
2122+
ObjectIdGetDatum(typeOid),
2123+
0, 0, 0);
2124+
if (!HeapTupleIsValid(tup))
2125+
elog(ERROR, "cache lookup failed for type %u", typeOid);
2126+
typTup = (Form_pg_type) GETSTRUCT(tup);
2127+
2128+
/*
2129+
* Modify the owner --- okay to scribble on typTup because it's a
2130+
* copy
2131+
*/
2132+
typTup->typowner = newOwnerId;
2133+
2134+
simple_heap_update(rel, &tup->t_self, tup);
2135+
2136+
CatalogUpdateIndexes(rel, tup);
2137+
2138+
/* Update owner dependency reference */
2139+
changeDependencyOnOwner(TypeRelationId, typeOid, newOwnerId);
2140+
2141+
/* Clean up */
2142+
heap_close(rel, RowExclusiveLock);
2143+
}
2144+
21052145
/*
21062146
* Execute ALTER TYPE SET SCHEMA
21072147
*/

src/include/commands/typecmds.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
88
* Portions Copyright (c) 1994, Regents of the University of California
99
*
10-
* $PostgreSQL: pgsql/src/include/commands/typecmds.h,v 1.12 2005/08/01 04:03:58 tgl Exp $
10+
* $PostgreSQL: pgsql/src/include/commands/typecmds.h,v 1.13 2005/08/04 01:09:29 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -35,6 +35,7 @@ extern void AlterDomainDropConstraint(List *names, const char *constrName,
3535
extern List *GetDomainConstraints(Oid typeOid);
3636

3737
extern void AlterTypeOwner(List *names, Oid newOwnerId);
38+
extern void AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId);
3839
extern void AlterTypeNamespace(List *names, const char *newschema);
3940
extern void AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
4041
bool errorOnTableType);

src/test/regress/expected/dependency.out

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ CREATE USER regression_user;
55
CREATE USER regression_user2;
66
CREATE USER regression_user3;
77
CREATE GROUP regression_group;
8-
CREATE TABLE deptest ();
8+
CREATE TABLE deptest (f1 serial primary key, f2 text);
9+
NOTICE: CREATE TABLE will create implicit sequence "deptest_f1_seq" for serial column "deptest.f1"
10+
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "deptest_pkey" for table "deptest"
911
GRANT SELECT ON TABLE deptest TO GROUP regression_group;
1012
GRANT ALL ON TABLE deptest TO regression_user, regression_user2;
1113
-- can't drop neither because they have privileges somewhere
@@ -30,10 +32,12 @@ DROP USER regression_user;
3032
REVOKE ALL ON deptest FROM regression_user2;
3133
DROP USER regression_user2;
3234
-- can't drop the owner of an object
35+
-- the error message detail here would include a pg_toast_nnn name that
36+
-- is not constant, so suppress it
37+
\set VERBOSITY terse
3338
ALTER TABLE deptest OWNER TO regression_user3;
3439
DROP USER regression_user3;
3540
ERROR: role "regression_user3" cannot be dropped because some objects depend on it
36-
DETAIL: owner of table deptest
3741
-- if we drop the object, we can drop the user too
3842
DROP TABLE deptest;
3943
DROP USER regression_user3;

src/test/regress/sql/dependency.sql

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ CREATE USER regression_user2;
77
CREATE USER regression_user3;
88
CREATE GROUP regression_group;
99

10-
CREATE TABLE deptest ();
10+
CREATE TABLE deptest (f1 serial primary key, f2 text);
1111

1212
GRANT SELECT ON TABLE deptest TO GROUP regression_group;
1313
GRANT ALL ON TABLE deptest TO regression_user, regression_user2;
@@ -33,6 +33,9 @@ REVOKE ALL ON deptest FROM regression_user2;
3333
DROP USER regression_user2;
3434

3535
-- can't drop the owner of an object
36+
-- the error message detail here would include a pg_toast_nnn name that
37+
-- is not constant, so suppress it
38+
\set VERBOSITY terse
3639
ALTER TABLE deptest OWNER TO regression_user3;
3740
DROP USER regression_user3;
3841

0 commit comments

Comments
 (0)