aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/src/sgml/ref/alter_table.sgml4
-rw-r--r--src/backend/commands/tablecmds.c74
-rw-r--r--src/backend/commands/typecmds.c44
-rw-r--r--src/include/commands/typecmds.h3
-rw-r--r--src/test/regress/expected/dependency.out8
-rw-r--r--src/test/regress/sql/dependency.sql5
6 files changed, 104 insertions, 34 deletions
diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml
index 26dabbb79ec..8e1d2984249 100644
--- a/doc/src/sgml/ref/alter_table.sgml
+++ b/doc/src/sgml/ref/alter_table.sgml
@@ -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
-->
@@ -235,7 +235,7 @@ where <replaceable class="PARAMETER">action</replaceable> is one of:
<term><literal>OWNER</literal></term>
<listitem>
<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.
</para>
</listitem>
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 7d9a73917dd..aaf9a2ce743 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8,7 +8,7 @@
*
*
* 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);
static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab);
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,
Oid newOwnerId);
static void ATExecClusterOn(Relation rel, const char *indexName);
@@ -2141,7 +2141,8 @@ ATExecCmd(AlteredTableInfo *tab, Relation rel, AlterTableCmd *cmd)
break;
case AT_ChangeOwner: /* ALTER OWNER */
ATExecChangeOwner(RelationGetRelid(rel),
- get_roleid_checked(cmd->name));
+ get_roleid_checked(cmd->name),
+ false);
break;
case AT_ClusterOn: /* CLUSTER ON */
ATExecClusterOn(rel, cmd->name);
@@ -5238,9 +5239,15 @@ ATPostAlterTypeParse(char *cmd, List **wqueue)
/*
* 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
-ATExecChangeOwner(Oid relationOid, Oid newOwnerId)
+ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing)
{
Relation target_rel;
Relation class_rel;
@@ -5267,16 +5274,19 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId)
switch (tuple_class->relkind)
{
case RELKIND_RELATION:
- case RELKIND_INDEX:
case RELKIND_VIEW:
case RELKIND_SEQUENCE:
- case RELKIND_TOASTVALUE:
/* ok to change owner */
break;
+ case RELKIND_INDEX:
+ case RELKIND_TOASTVALUE:
+ if (recursing)
+ break;
+ /* FALL THRU */
default:
ereport(ERROR,
(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))));
}
@@ -5293,23 +5303,28 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId)
Datum aclDatum;
bool isNull;
HeapTuple newtuple;
- Oid namespaceOid = tuple_class->relnamespace;
- AclResult aclresult;
-
- /* Otherwise, must be owner of the existing object */
- if (!pg_class_ownercheck(relationOid,GetUserId()))
- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
- RelationGetRelationName(target_rel));
- /* Must be able to become new owner */
- check_is_member_of_role(GetUserId(), newOwnerId);
-
- /* New owner must have CREATE privilege on namespace */
- aclresult = pg_namespace_aclcheck(namespaceOid, newOwnerId,
- ACL_CREATE);
- if (aclresult != ACLCHECK_OK)
- aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
- get_namespace_name(namespaceOid));
+ /* skip permission checks when recursing to index or toast table */
+ if (!recursing)
+ {
+ Oid namespaceOid = tuple_class->relnamespace;
+ AclResult aclresult;
+
+ /* Otherwise, must be owner of the existing object */
+ if (!pg_class_ownercheck(relationOid,GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
+ RelationGetRelationName(target_rel));
+
+ /* Must be able to become new owner */
+ check_is_member_of_role(GetUserId(), newOwnerId);
+
+ /* New owner must have CREATE privilege on namespace */
+ aclresult = pg_namespace_aclcheck(namespaceOid, newOwnerId,
+ ACL_CREATE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
+ get_namespace_name(namespaceOid));
+ }
memset(repl_null, ' ', sizeof(repl_null));
memset(repl_repl, ' ', sizeof(repl_repl));
@@ -5343,6 +5358,12 @@ ATExecChangeOwner(Oid relationOid, Oid 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
* any indexes and sequences that belong to the table, as well as
* the table's toast table (if it has one)
@@ -5358,7 +5379,7 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId)
/* For each index, recursively change its ownership */
foreach(i, index_oid_list)
- ATExecChangeOwner(lfirst_oid(i), newOwnerId);
+ ATExecChangeOwner(lfirst_oid(i), newOwnerId, true);
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 (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 */
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. */
- ATExecChangeOwner(depForm->objid, newOwnerId);
+ ATExecChangeOwner(depForm->objid, newOwnerId, false);
/* Now we can close it. Keep the lock till end of transaction. */
relation_close(seqRel, NoLock);
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 80d394b2933..31e43cd4281 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -8,7 +8,7 @@
*
*
* 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
* 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
* 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,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is a table's row type",
@@ -2103,6 +2104,45 @@ AlterTypeOwner(List *names, Oid newOwnerId)
}
/*
+ * 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
*/
void
diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h
index a070a27a292..d53cf672a65 100644
--- a/src/include/commands/typecmds.h
+++ b/src/include/commands/typecmds.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* 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 void AlterTypeOwner(List *names, Oid newOwnerId);
+extern void AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId);
extern void AlterTypeNamespace(List *names, const char *newschema);
extern void AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
bool errorOnTableType);
diff --git a/src/test/regress/expected/dependency.out b/src/test/regress/expected/dependency.out
index 4ee3e8b6a8f..2c31e581bfe 100644
--- a/src/test/regress/expected/dependency.out
+++ b/src/test/regress/expected/dependency.out
@@ -5,7 +5,9 @@ CREATE USER regression_user;
CREATE USER regression_user2;
CREATE USER regression_user3;
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 ALL ON TABLE deptest TO regression_user, regression_user2;
-- can't drop neither because they have privileges somewhere
@@ -30,10 +32,12 @@ DROP USER regression_user;
REVOKE ALL ON deptest FROM regression_user2;
DROP USER regression_user2;
-- 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;
DROP USER regression_user3;
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
DROP TABLE deptest;
DROP USER regression_user3;
diff --git a/src/test/regress/sql/dependency.sql b/src/test/regress/sql/dependency.sql
index 6d52b62dee1..3e4a232ea71 100644
--- a/src/test/regress/sql/dependency.sql
+++ b/src/test/regress/sql/dependency.sql
@@ -7,7 +7,7 @@ CREATE USER regression_user2;
CREATE USER regression_user3;
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 ALL ON TABLE deptest TO regression_user, regression_user2;
@@ -33,6 +33,9 @@ REVOKE ALL ON deptest FROM regression_user2;
DROP USER regression_user2;
-- 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;
DROP USER regression_user3;