diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml index c08b09b67d4b13404d8d26543f13fb5b094d6563..07dd0b0ee7453e8b6963480d2fed100450e4ff26 100644 --- a/doc/src/sgml/ref/allfiles.sgml +++ b/doc/src/sgml/ref/allfiles.sgml @@ -1,5 +1,5 @@ <!-- -$PostgreSQL: pgsql/doc/src/sgml/ref/allfiles.sgml,v 1.66 2005/07/29 15:13:11 momjian Exp $ +$PostgreSQL: pgsql/doc/src/sgml/ref/allfiles.sgml,v 1.67 2005/11/21 12:49:30 alvherre Exp $ PostgreSQL documentation Complete list of usable sgml source files in this directory. --> @@ -70,6 +70,7 @@ Complete list of usable sgml source files in this directory. <!entity dropLanguage system "drop_language.sgml"> <!entity dropOperator system "drop_operator.sgml"> <!entity dropOperatorClass system "drop_opclass.sgml"> +<!entity dropOwned system "drop_owned.sgml"> <!entity dropRole system "drop_role.sgml"> <!entity dropRule system "drop_rule.sgml"> <!entity dropSchema system "drop_schema.sgml"> @@ -93,6 +94,7 @@ Complete list of usable sgml source files in this directory. <!entity notify system "notify.sgml"> <!entity prepare system "prepare.sgml"> <!entity prepareTransaction system "prepare_transaction.sgml"> +<!entity reassignOwned system "reassign_owned.sgml"> <!entity reindex system "reindex.sgml"> <!entity releaseSavepoint system "release_savepoint.sgml"> <!entity reset system "reset.sgml"> diff --git a/doc/src/sgml/ref/drop_owned.sgml b/doc/src/sgml/ref/drop_owned.sgml new file mode 100644 index 0000000000000000000000000000000000000000..dc8b01fe984bf45cc54165b251a0a8652627033e --- /dev/null +++ b/doc/src/sgml/ref/drop_owned.sgml @@ -0,0 +1,99 @@ +<!-- +$PostgreSQL: pgsql/doc/src/sgml/ref/drop_owned.sgml,v 1.1 2005/11/21 12:49:30 alvherre Exp $ +PostgreSQL documentation +--> + +<refentry id="SQL-DROP-OWNED"> + <refmeta> + <refentrytitle id="SQL-DROP-OWNED-TITLE">DROP OWNED</refentrytitle> + <refmiscinfo>SQL - Language Statements</refmiscinfo> + </refmeta> + + <refnamediv> + <refname>DROP OWNED</refname> + <refpurpose>remove database objects owned by a database role</refpurpose> + </refnamediv> + + <indexterm zone="sql-drop-owned"> + <primary>DROP OWNED</primary> + </indexterm> + + <refsynopsisdiv> +<synopsis> +DROP OWNED <replaceable class="PARAMETER">name</replaceable> [, ...] [ RESTRICT | CASCADE ] +</synopsis> + </refsynopsisdiv> + + <refsect1> + <title>Description</title> + + <para> + The <command>DROP OWNED</command> instructs the system to drop those + database objects owned by one of the given roles which reside on the + current database. All privileges granted to the given roles will also be + revoked. + </para> + + <para> + If <literal>CASCADE</> is specified, <command>DROP OWNED</command> + will behave like a <command>DROP ... CASCADE</command> was issued + for each object, that is, objects dependent on the ones owned by the + given users will be dropped as well. + </para> + </refsect1> + + <refsect1> + <title>Notes</title> + <para> + The <command>DROP OWNED</command> command is mostly used in preparation to + drop the roles. It may be necessary to issue the command in more than one + database. + </para> + + <para> + Using the <literal>CASCADE</literal> option may make the command recurse to + objects owned by other users. + </para> + + <para> + See the <command>REASSIGN OWNED</command> command for an alternative that + gives the objects away to another role. + </para> + </refsect1> + + <refsect1> + <title>Compatibility</title> + + <para> + The <command>DROP OWNED</command> statement is a + <productname>PostgreSQL</productname> extension. + </para> + </refsect1> + + <refsect1> + <title>See Also</title> + + <simplelist type="inline"> + <member><xref linkend="sql-reassign-owned" endterm="sql-reassign-owned-title"></member> + <member><xref linkend="sql-droprole" endterm="sql-droprole-title"></member> + </simplelist> + </refsect1> + +</refentry> + +<!-- Keep this comment at the end of the file +Local variables: +mode: sgml +sgml-omittag:nil +sgml-shorttag:t +sgml-minimize-attributes:nil +sgml-always-quote-attributes:t +sgml-indent-step:1 +sgml-indent-data:t +sgml-parent-document:nil +sgml-default-dtd-file:"../reference.ced" +sgml-exposed-tags:nil +sgml-local-catalogs:"/usr/lib/sgml/catalog" +sgml-local-ecat-files:nil +End: +--> diff --git a/doc/src/sgml/ref/reassign_owned.sgml b/doc/src/sgml/ref/reassign_owned.sgml new file mode 100644 index 0000000000000000000000000000000000000000..a54e4c82698d86c1269b8b00b298a4a20c7087e5 --- /dev/null +++ b/doc/src/sgml/ref/reassign_owned.sgml @@ -0,0 +1,89 @@ +<!-- +$PostgreSQL: pgsql/doc/src/sgml/ref/reassign_owned.sgml,v 1.1 2005/11/21 12:49:30 alvherre Exp $ +PostgreSQL documentation +--> + +<refentry id="SQL-REASSIGN-OWNED"> + <refmeta> + <refentrytitle id="SQL-REASSIGN-OWNED-TITLE">REASSIGN OWNED</refentrytitle> + <refmiscinfo>SQL - Language Statements</refmiscinfo> + </refmeta> + + <refnamediv> + <refname>REASSIGN OWNED</refname> + <refpurpose>change ownership of database objects owned by a database role</refpurpose> + </refnamediv> + + <indexterm zone="sql-reassign-owned"> + <primary>REASSIGN OWNED</primary> + </indexterm> + + <refsynopsisdiv> +<synopsis> +REASSIGN OWNED <replaceable class="PARAMETER">old_role</replaceable> [, ...] TO <replaceable class="PARAMETER">new_role</replaceable> +</synopsis> + </refsynopsisdiv> + + <refsect1> + <title>Description</title> + + <para> + The <command>REASSIGN OWNED</command> instructs the system to change + the ownership of the database objects owned by one of the old_roles, + to new_role. + </para> + </refsect1> + + <refsect1> + <title>Notes</title> + + <para> + The <command>REASSIGN OWNED</command> command is mostly used in preparation to + drop the roles. See the <command>DROP OWNED</command> command for an + alternative that drops the objects. + </para> + + <para> + The <command>REASSIGN OWNED</command> command does not affect the privileges + granted to the old_roles in objects not owned by them. Use + <command>DROP OWNED</command> to remove them. + </para> + + </refsect1> + + <refsect1> + <title>Compatibility</title> + + <para> + The <command>REASSIGN OWNED</command> statement is a + <productname>PostgreSQL</productname> extension. + </para> + </refsect1> + + <refsect1> + <title>See Also</title> + + <simplelist type="inline"> + <member><xref linkend="sql-drop-owned" endterm="sql-drop-owned-title"></member> + <member><xref linkend="sql-droprole" endterm="sql-droprole-title"></member> + </simplelist> + </refsect1> + +</refentry> + +<!-- Keep this comment at the end of the file +Local variables: +mode: sgml +sgml-omittag:nil +sgml-shorttag:t +sgml-minimize-attributes:nil +sgml-always-quote-attributes:t +sgml-indent-step:1 +sgml-indent-data:t +sgml-parent-document:nil +sgml-default-dtd-file:"../reference.ced" +sgml-exposed-tags:nil +sgml-local-catalogs:"/usr/lib/sgml/catalog" +sgml-local-ecat-files:nil +End: +--> diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml index 0191ec8b3c31b1b7c2f66532722b0c7c51d10904..f320a132d04b9789f6dbcb33fa6e0a709c728c39 100644 --- a/doc/src/sgml/reference.sgml +++ b/doc/src/sgml/reference.sgml @@ -1,5 +1,5 @@ <!-- reference.sgml -$PostgreSQL: pgsql/doc/src/sgml/reference.sgml,v 1.56 2005/07/29 15:13:11 momjian Exp $ +$PostgreSQL: pgsql/doc/src/sgml/reference.sgml,v 1.57 2005/11/21 12:49:30 alvherre Exp $ PostgreSQL Reference Manual --> @@ -102,6 +102,7 @@ PostgreSQL Reference Manual &dropLanguage; &dropOperator; &dropOperatorClass; + &dropOwned; &dropRole; &dropRule; &dropSchema; @@ -125,6 +126,7 @@ PostgreSQL Reference Manual ¬ify; &prepare; &prepareTransaction; + &reassignOwned; &reindex; &releaseSavepoint; &reset; diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c index 15a197af81b123d9725ee1f802634171fe52d768..b0bc8e28eaf790345f3a6b9a34da4eb5c07afbfb 100644 --- a/src/backend/catalog/aclchk.c +++ b/src/backend/catalog/aclchk.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.120 2005/10/15 02:49:12 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.121 2005/11/21 12:49:30 alvherre Exp $ * * NOTES * See acl.h. @@ -17,6 +17,7 @@ */ #include "postgres.h" +#include "access/genam.h" #include "access/heapam.h" #include "catalog/catalog.h" #include "catalog/dependency.h" @@ -41,12 +42,25 @@ #include "utils/syscache.h" -static void ExecuteGrantStmt_Relation(GrantStmt *stmt); -static void ExecuteGrantStmt_Database(GrantStmt *stmt); -static void ExecuteGrantStmt_Function(GrantStmt *stmt); -static void ExecuteGrantStmt_Language(GrantStmt *stmt); -static void ExecuteGrantStmt_Namespace(GrantStmt *stmt); -static void ExecuteGrantStmt_Tablespace(GrantStmt *stmt); +static void ExecGrant_Relation(bool is_grant, List *objects, bool all_privs, + AclMode privileges, List *grantees, bool grant_option, + DropBehavior behavior); +static void ExecGrant_Database(bool is_grant, List *objects, bool all_privs, + AclMode privileges, List *grantees, bool grant_option, + DropBehavior behavior); +static void ExecGrant_Function(bool is_grant, List *objects, bool all_privs, + AclMode privileges, List *grantees, bool grant_option, + DropBehavior behavior); +static void ExecGrant_Language(bool is_grant, List *objects, bool all_privs, + AclMode privileges, List *grantees, bool grant_option, + DropBehavior behavior); +static void ExecGrant_Namespace(bool is_grant, List *objects, bool all_privs, + AclMode privileges, List *grantees, bool grant_option, + DropBehavior behavior); +static void ExecGrant_Tablespace(bool is_grant, List *objects, bool all_privs, + AclMode privileges, List *grantees, bool grant_option, + DropBehavior behavior); +static List *objectNamesToOids(GrantObjectType objtype, List *objnames); static AclMode string_to_privilege(const char *privname); static const char *privilege_to_string(AclMode privilege); @@ -96,15 +110,10 @@ merge_acl_with_grant(Acl *old_acl, bool is_grant, foreach(j, grantees) { - PrivGrantee *grantee = (PrivGrantee *) lfirst(j); - AclItem aclitem; + AclItem aclitem; Acl *newer_acl; - if (grantee->rolname) - aclitem. ai_grantee = get_roleid_checked(grantee->rolname); - - else - aclitem. ai_grantee = ACL_ID_PUBLIC; + aclitem.ai_grantee = lfirst_oid(j); /* * Grant options can only be granted to individual roles, not PUBLIC. @@ -151,71 +160,307 @@ merge_acl_with_grant(Acl *old_acl, bool is_grant, void ExecuteGrantStmt(GrantStmt *stmt) { + List *objects; + List *grantees = NIL; + AclMode privileges; + ListCell *cell; + bool all_privs; + AclMode all_privileges = (AclMode) 0; + char *errormsg = NULL; + + /* + * Convert the PrivGrantee list into an Oid list. Note that at this point + * we insert an ACL_ID_PUBLIC into the list if an empty role name is + * detected (which is what the grammar uses if PUBLIC is found), so + * downstream there shouldn't be any additional work needed to support this + * case. + */ + foreach(cell, stmt->grantees) + { + PrivGrantee *grantee = (PrivGrantee *) lfirst(cell); + + if (grantee->rolname == NULL) + grantees = lappend_oid(grantees, ACL_ID_PUBLIC); + else + grantees = lappend_oid(grantees, + get_roleid_checked(grantee->rolname)); + } + + /* + * Convert stmt->privileges, a textual list, into an AclMode bitmask + * appropiate for the given object class. + */ switch (stmt->objtype) { case ACL_OBJECT_RELATION: - ExecuteGrantStmt_Relation(stmt); + all_privileges = ACL_ALL_RIGHTS_RELATION; + errormsg = _("invalid privilege type %s for table"); break; case ACL_OBJECT_DATABASE: - ExecuteGrantStmt_Database(stmt); + all_privileges = ACL_ALL_RIGHTS_DATABASE; + errormsg = _("invalid privilege type %s for database"); break; case ACL_OBJECT_FUNCTION: - ExecuteGrantStmt_Function(stmt); + all_privileges = ACL_ALL_RIGHTS_FUNCTION; + errormsg = _("invalid privilege type %s for function"); break; case ACL_OBJECT_LANGUAGE: - ExecuteGrantStmt_Language(stmt); + all_privileges = ACL_ALL_RIGHTS_LANGUAGE; + errormsg = _("invalid privilege type %s for language"); break; case ACL_OBJECT_NAMESPACE: - ExecuteGrantStmt_Namespace(stmt); + all_privileges = ACL_ALL_RIGHTS_NAMESPACE; + errormsg = _("invalid privilege type %s for namespace"); break; case ACL_OBJECT_TABLESPACE: - ExecuteGrantStmt_Tablespace(stmt); + all_privileges = ACL_ALL_RIGHTS_TABLESPACE; + errormsg = _("invalid privilege type %s for tablespace"); break; default: elog(ERROR, "unrecognized GrantStmt.objtype: %d", (int) stmt->objtype); } -} - - -static void -ExecuteGrantStmt_Relation(GrantStmt *stmt) -{ - AclMode privileges; - bool all_privs; - ListCell *i; if (stmt->privileges == NIL) { all_privs = true; - privileges = ACL_ALL_RIGHTS_RELATION; + privileges = all_privileges; } else { all_privs = false; privileges = ACL_NO_RIGHTS; - foreach(i, stmt->privileges) + foreach(cell, stmt->privileges) { - char *privname = strVal(lfirst(i)); + char *privname = strVal(lfirst(cell)); AclMode priv = string_to_privilege(privname); - if (priv & ~((AclMode) ACL_ALL_RIGHTS_RELATION)) + if (priv & ~((AclMode) all_privileges)) ereport(ERROR, (errcode(ERRCODE_INVALID_GRANT_OPERATION), - errmsg("invalid privilege type %s for table", + errmsg(errormsg, privilege_to_string(priv)))); + privileges |= priv; } } - foreach(i, stmt->objects) + /* Turn the list of object names into an Oid list */ + objects = objectNamesToOids(stmt->objtype, stmt->objects); + + ExecGrantStmt_oids(stmt->is_grant, stmt->objtype, objects, all_privs, + privileges, grantees, stmt->grant_option, + stmt->behavior); +} + +/* + * ExecGrantStmt_oids + * + * "Internal" entrypoint for granting and revoking privileges. The arguments + * it receives are lists of Oids or have been otherwise converted from text + * format to internal format. + */ +void +ExecGrantStmt_oids(bool is_grant, GrantObjectType objtype, List *objects, + bool all_privs, AclMode privileges, List *grantees, + bool grant_option, DropBehavior behavior) +{ + switch (objtype) { - RangeVar *relvar = (RangeVar *) lfirst(i); - Oid relOid; - Relation relation; - HeapTuple tuple; - Form_pg_class pg_class_tuple; + case ACL_OBJECT_RELATION: + ExecGrant_Relation(is_grant, objects, all_privs, privileges, + grantees, grant_option, behavior); + break; + case ACL_OBJECT_DATABASE: + ExecGrant_Database(is_grant, objects, all_privs, privileges, + grantees, grant_option, behavior); + break; + case ACL_OBJECT_FUNCTION: + ExecGrant_Function(is_grant, objects, all_privs, privileges, + grantees, grant_option, behavior); + break; + case ACL_OBJECT_LANGUAGE: + ExecGrant_Language(is_grant, objects, all_privs, privileges, + grantees, grant_option, behavior); + break; + case ACL_OBJECT_NAMESPACE: + ExecGrant_Namespace(is_grant, objects, all_privs, + privileges, grantees, grant_option, + behavior); + break; + case ACL_OBJECT_TABLESPACE: + ExecGrant_Tablespace(is_grant, objects, all_privs, + privileges, grantees, grant_option, + behavior); + break; + default: + elog(ERROR, "unrecognized GrantStmt.objtype: %d", + (int) objtype); + } +} + +/* + * objectNamesToOids + * + * Turn a list of object names of a given type into an Oid list. + */ +static List * +objectNamesToOids(GrantObjectType objtype, List *objnames) +{ + List *objects = NIL; + ListCell *cell; + + Assert(objnames != NIL); + + switch (objtype) + { + case ACL_OBJECT_RELATION: + foreach(cell, objnames) + { + Oid relOid; + RangeVar *relvar = (RangeVar *) lfirst(cell); + + relOid = RangeVarGetRelid(relvar, false); + objects = lappend_oid(objects, relOid); + } + break; + case ACL_OBJECT_DATABASE: + foreach(cell, objnames) + { + char *dbname = strVal(lfirst(cell)); + ScanKeyData entry[1]; + HeapScanDesc scan; + HeapTuple tuple; + Relation relation; + + relation = heap_open(DatabaseRelationId, AccessShareLock); + + /* + * There's no syscache for pg_database, so we must + * look the hard way. + */ + ScanKeyInit(&entry[0], + Anum_pg_database_datname, + BTEqualStrategyNumber, F_NAMEEQ, + CStringGetDatum(dbname)); + scan = heap_beginscan(relation, SnapshotNow, 1, entry); + tuple = heap_getnext(scan, ForwardScanDirection); + if (!HeapTupleIsValid(tuple)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_DATABASE), + errmsg("database \"%s\" does not exist", dbname))); + objects = lappend_oid(objects, HeapTupleGetOid(tuple)); + + heap_close(relation, AccessShareLock); + + heap_endscan(scan); + } + break; + case ACL_OBJECT_FUNCTION: + foreach(cell, objnames) + { + FuncWithArgs *func = (FuncWithArgs *) lfirst(cell); + Oid funcid; + + funcid = LookupFuncNameTypeNames(func->funcname, + func->funcargs, false); + objects = lappend_oid(objects, funcid); + } + break; + case ACL_OBJECT_LANGUAGE: + foreach(cell, objnames) + { + char *langname = strVal(lfirst(cell)); + HeapTuple tuple; + + tuple = SearchSysCache(LANGNAME, + PointerGetDatum(langname), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("language \"%s\" does not exist", langname))); + + objects = lappend_oid(objects, HeapTupleGetOid(tuple)); + + ReleaseSysCache(tuple); + } + break; + case ACL_OBJECT_NAMESPACE: + foreach (cell, objnames) + { + char *nspname = strVal(lfirst(cell)); + HeapTuple tuple; + + tuple = SearchSysCache(NAMESPACENAME, + CStringGetDatum(nspname), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_SCHEMA), + errmsg("schema \"%s\" does not exist", nspname))); + + objects = lappend_oid(objects, HeapTupleGetOid(tuple)); + + ReleaseSysCache(tuple); + } + break; + case ACL_OBJECT_TABLESPACE: + foreach (cell, objnames) + { + char *spcname = strVal(lfirst(cell)); + ScanKeyData entry[1]; + HeapScanDesc scan; + HeapTuple tuple; + Relation relation; + + relation = heap_open(TableSpaceRelationId, AccessShareLock); + + ScanKeyInit(&entry[0], + Anum_pg_tablespace_spcname, + BTEqualStrategyNumber, F_NAMEEQ, + CStringGetDatum(spcname)); + + scan = heap_beginscan(relation, SnapshotNow, 1, entry); + tuple = heap_getnext(scan, ForwardScanDirection); + if (!HeapTupleIsValid(tuple)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("tablespace \"%s\" does not exist", spcname))); + + objects = lappend_oid(objects, HeapTupleGetOid(tuple)); + + heap_endscan(scan); + + heap_close(relation, AccessShareLock); + } + break; + default: + elog(ERROR, "unrecognized GrantStmt.objtype: %d", + (int) objtype); + } + + return objects; +} + +static void +ExecGrant_Relation(bool is_grant, List *objects, bool all_privs, + AclMode privileges, List *grantees, bool grant_option, + DropBehavior behavior) +{ + Relation relation; + ListCell *cell; + + if (all_privs && privileges == ACL_NO_RIGHTS) + privileges = ACL_ALL_RIGHTS_RELATION; + + relation = heap_open(RelationRelationId, RowExclusiveLock); + + foreach (cell, objects) + { + Oid relOid = lfirst_oid(cell); Datum aclDatum; + Form_pg_class pg_class_tuple; bool isNull; AclMode avail_goptions; AclMode this_privileges; @@ -223,6 +468,7 @@ ExecuteGrantStmt_Relation(GrantStmt *stmt) Acl *new_acl; Oid grantorId; Oid ownerId; + HeapTuple tuple; HeapTuple newtuple; Datum values[Natts_pg_class]; char nulls[Natts_pg_class]; @@ -232,9 +478,6 @@ ExecuteGrantStmt_Relation(GrantStmt *stmt) Oid *oldmembers; Oid *newmembers; - /* open pg_class */ - relation = heap_open(RelationRelationId, RowExclusiveLock); - relOid = RangeVarGetRelid(relvar, false); tuple = SearchSysCache(RELOID, ObjectIdGetDatum(relOid), 0, 0, 0); @@ -247,15 +490,14 @@ ExecuteGrantStmt_Relation(GrantStmt *stmt) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is an index", - relvar->relname))); + NameStr(pg_class_tuple->relname)))); /* Composite types aren't tables either */ if (pg_class_tuple->relkind == RELKIND_COMPOSITE_TYPE) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is a composite type", - relvar->relname))); - + NameStr(pg_class_tuple->relname)))); /* * Get owner ID and working copy of existing ACL. If there's no ACL, * substitute the proper default. @@ -285,7 +527,7 @@ ExecuteGrantStmt_Relation(GrantStmt *stmt) ACL_ALL_RIGHTS_RELATION | ACL_GRANT_OPTION_FOR(ACL_ALL_RIGHTS_RELATION), ACLMASK_ANY) == ACL_NO_RIGHTS) aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS, - relvar->relname); + NameStr(pg_class_tuple->relname)); } /* @@ -297,7 +539,7 @@ ExecuteGrantStmt_Relation(GrantStmt *stmt) * GRANT case.) */ this_privileges = privileges & ACL_OPTION_TO_PRIVS(avail_goptions); - if (stmt->is_grant) + if (is_grant) { if (this_privileges == 0) ereport(WARNING, @@ -328,9 +570,9 @@ ExecuteGrantStmt_Relation(GrantStmt *stmt) */ noldmembers = aclmembers(old_acl, &oldmembers); - new_acl = merge_acl_with_grant(old_acl, stmt->is_grant, - stmt->grant_option, stmt->behavior, - stmt->grantees, this_privileges, + new_acl = merge_acl_with_grant(old_acl, is_grant, + grant_option, behavior, + grantees, this_privileges, grantorId, ownerId); nnewmembers = aclmembers(new_acl, &newmembers); @@ -352,7 +594,7 @@ ExecuteGrantStmt_Relation(GrantStmt *stmt) /* Update the shared dependency ACL info */ updateAclDependencies(RelationRelationId, relOid, - ownerId, stmt->is_grant, + ownerId, is_grant, noldmembers, oldmembers, nnewmembers, newmembers); @@ -360,50 +602,29 @@ ExecuteGrantStmt_Relation(GrantStmt *stmt) pfree(new_acl); - heap_close(relation, RowExclusiveLock); - /* prevent error when processing duplicate objects */ CommandCounterIncrement(); } + + heap_close(relation, RowExclusiveLock); } static void -ExecuteGrantStmt_Database(GrantStmt *stmt) +ExecGrant_Database(bool is_grant, List *objects, bool all_privs, + AclMode privileges, List *grantees, bool grant_option, + DropBehavior behavior) { - AclMode privileges; - bool all_privs; - ListCell *i; + Relation relation; + ListCell *cell; - if (stmt->privileges == NIL) - { - all_privs = true; + if (all_privs && privileges == ACL_NO_RIGHTS) privileges = ACL_ALL_RIGHTS_DATABASE; - } - else - { - all_privs = false; - privileges = ACL_NO_RIGHTS; - foreach(i, stmt->privileges) - { - char *privname = strVal(lfirst(i)); - AclMode priv = string_to_privilege(privname); - if (priv & ~((AclMode) ACL_ALL_RIGHTS_DATABASE)) - ereport(ERROR, - (errcode(ERRCODE_INVALID_GRANT_OPERATION), - errmsg("invalid privilege type %s for database", - privilege_to_string(priv)))); - privileges |= priv; - } - } + relation = heap_open(DatabaseRelationId, RowExclusiveLock); - foreach(i, stmt->objects) + foreach (cell, objects) { - char *dbname = strVal(lfirst(i)); - Relation relation; - ScanKeyData entry[1]; - HeapScanDesc scan; - HeapTuple tuple; + Oid datId = lfirst_oid(cell); Form_pg_database pg_database_tuple; Datum aclDatum; bool isNull; @@ -421,18 +642,23 @@ ExecuteGrantStmt_Database(GrantStmt *stmt) int nnewmembers; Oid *oldmembers; Oid *newmembers; + ScanKeyData entry[1]; + SysScanDesc scan; + HeapTuple tuple; - relation = heap_open(DatabaseRelationId, RowExclusiveLock); + /* There's no syscache for pg_database, so must look the hard way */ ScanKeyInit(&entry[0], - Anum_pg_database_datname, - BTEqualStrategyNumber, F_NAMEEQ, - CStringGetDatum(dbname)); - scan = heap_beginscan(relation, SnapshotNow, 1, entry); - tuple = heap_getnext(scan, ForwardScanDirection); + ObjectIdAttributeNumber, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(datId)); + scan = systable_beginscan(relation, DatabaseOidIndexId, true, + SnapshotNow, 1, entry); + + tuple = systable_getnext(scan); + if (!HeapTupleIsValid(tuple)) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_DATABASE), - errmsg("database \"%s\" does not exist", dbname))); + elog(ERROR, "could not find tuple for database %u", datId); + pg_database_tuple = (Form_pg_database) GETSTRUCT(tuple); /* @@ -476,7 +702,7 @@ ExecuteGrantStmt_Database(GrantStmt *stmt) * GRANT case.) */ this_privileges = privileges & ACL_OPTION_TO_PRIVS(avail_goptions); - if (stmt->is_grant) + if (is_grant) { if (this_privileges == 0) ereport(WARNING, @@ -507,9 +733,9 @@ ExecuteGrantStmt_Database(GrantStmt *stmt) */ noldmembers = aclmembers(old_acl, &oldmembers); - new_acl = merge_acl_with_grant(old_acl, stmt->is_grant, - stmt->grant_option, stmt->behavior, - stmt->grantees, this_privileges, + new_acl = merge_acl_with_grant(old_acl, is_grant, + grant_option, behavior, + grantees, this_privileges, grantorId, ownerId); nnewmembers = aclmembers(new_acl, &newmembers); @@ -522,7 +748,8 @@ ExecuteGrantStmt_Database(GrantStmt *stmt) replaces[Anum_pg_database_datacl - 1] = 'r'; values[Anum_pg_database_datacl - 1] = PointerGetDatum(new_acl); - newtuple = heap_modifytuple(tuple, RelationGetDescr(relation), values, nulls, replaces); + newtuple = heap_modifytuple(tuple, RelationGetDescr(relation), values, + nulls, replaces); simple_heap_update(relation, &newtuple->t_self, newtuple); @@ -531,57 +758,37 @@ ExecuteGrantStmt_Database(GrantStmt *stmt) /* Update the shared dependency ACL info */ updateAclDependencies(DatabaseRelationId, HeapTupleGetOid(tuple), - ownerId, stmt->is_grant, + ownerId, is_grant, noldmembers, oldmembers, nnewmembers, newmembers); - pfree(new_acl); - - heap_endscan(scan); + systable_endscan(scan); - heap_close(relation, RowExclusiveLock); + pfree(new_acl); /* prevent error when processing duplicate objects */ CommandCounterIncrement(); } + + heap_close(relation, RowExclusiveLock); } static void -ExecuteGrantStmt_Function(GrantStmt *stmt) +ExecGrant_Function(bool is_grant, List *objects, bool all_privs, + AclMode privileges, List *grantees, bool grant_option, + DropBehavior behavior) { - AclMode privileges; - bool all_privs; - ListCell *i; + Relation relation; + ListCell *cell; - if (stmt->privileges == NIL) - { - all_privs = true; + if (all_privs && privileges == ACL_NO_RIGHTS) privileges = ACL_ALL_RIGHTS_FUNCTION; - } - else - { - all_privs = false; - privileges = ACL_NO_RIGHTS; - foreach(i, stmt->privileges) - { - char *privname = strVal(lfirst(i)); - AclMode priv = string_to_privilege(privname); - if (priv & ~((AclMode) ACL_ALL_RIGHTS_FUNCTION)) - ereport(ERROR, - (errcode(ERRCODE_INVALID_GRANT_OPERATION), - errmsg("invalid privilege type %s for function", - privilege_to_string(priv)))); - privileges |= priv; - } - } + relation = heap_open(ProcedureRelationId, RowExclusiveLock); - foreach(i, stmt->objects) + foreach (cell, objects) { - FuncWithArgs *func = (FuncWithArgs *) lfirst(i); - Oid oid; - Relation relation; - HeapTuple tuple; + Oid funcId = lfirst_oid(cell); Form_pg_proc pg_proc_tuple; Datum aclDatum; bool isNull; @@ -591,6 +798,7 @@ ExecuteGrantStmt_Function(GrantStmt *stmt) Acl *new_acl; Oid grantorId; Oid ownerId; + HeapTuple tuple; HeapTuple newtuple; Datum values[Natts_pg_proc]; char nulls[Natts_pg_proc]; @@ -600,14 +808,12 @@ ExecuteGrantStmt_Function(GrantStmt *stmt) Oid *oldmembers; Oid *newmembers; - oid = LookupFuncNameTypeNames(func->funcname, func->funcargs, false); - - relation = heap_open(ProcedureRelationId, RowExclusiveLock); tuple = SearchSysCache(PROCOID, - ObjectIdGetDatum(oid), + ObjectIdGetDatum(funcId), 0, 0, 0); if (!HeapTupleIsValid(tuple)) - elog(ERROR, "cache lookup failed for function %u", oid); + elog(ERROR, "cache lookup failed for function %u", funcId); + pg_proc_tuple = (Form_pg_proc) GETSTRUCT(tuple); /* @@ -634,7 +840,7 @@ ExecuteGrantStmt_Function(GrantStmt *stmt) */ if (avail_goptions == ACL_NO_RIGHTS) { - if (pg_proc_aclmask(oid, + if (pg_proc_aclmask(funcId, grantorId, ACL_ALL_RIGHTS_FUNCTION | ACL_GRANT_OPTION_FOR(ACL_ALL_RIGHTS_FUNCTION), ACLMASK_ANY) == ACL_NO_RIGHTS) @@ -651,7 +857,7 @@ ExecuteGrantStmt_Function(GrantStmt *stmt) * GRANT case.) */ this_privileges = privileges & ACL_OPTION_TO_PRIVS(avail_goptions); - if (stmt->is_grant) + if (is_grant) { if (this_privileges == 0) ereport(WARNING, @@ -682,9 +888,9 @@ ExecuteGrantStmt_Function(GrantStmt *stmt) */ noldmembers = aclmembers(old_acl, &oldmembers); - new_acl = merge_acl_with_grant(old_acl, stmt->is_grant, - stmt->grant_option, stmt->behavior, - stmt->grantees, this_privileges, + new_acl = merge_acl_with_grant(old_acl, is_grant, + grant_option, behavior, + grantees, this_privileges, grantorId, ownerId); nnewmembers = aclmembers(new_acl, &newmembers); @@ -697,7 +903,8 @@ ExecuteGrantStmt_Function(GrantStmt *stmt) replaces[Anum_pg_proc_proacl - 1] = 'r'; values[Anum_pg_proc_proacl - 1] = PointerGetDatum(new_acl); - newtuple = heap_modifytuple(tuple, RelationGetDescr(relation), values, nulls, replaces); + newtuple = heap_modifytuple(tuple, RelationGetDescr(relation), values, + nulls, replaces); simple_heap_update(relation, &newtuple->t_self, newtuple); @@ -705,8 +912,8 @@ ExecuteGrantStmt_Function(GrantStmt *stmt) CatalogUpdateIndexes(relation, newtuple); /* Update the shared dependency ACL info */ - updateAclDependencies(ProcedureRelationId, oid, - ownerId, stmt->is_grant, + updateAclDependencies(ProcedureRelationId, funcId, + ownerId, is_grant, noldmembers, oldmembers, nnewmembers, newmembers); @@ -714,48 +921,29 @@ ExecuteGrantStmt_Function(GrantStmt *stmt) pfree(new_acl); - heap_close(relation, RowExclusiveLock); - /* prevent error when processing duplicate objects */ CommandCounterIncrement(); } + + heap_close(relation, RowExclusiveLock); } static void -ExecuteGrantStmt_Language(GrantStmt *stmt) +ExecGrant_Language(bool is_grant, List *objects, bool all_privs, + AclMode privileges, List *grantees, bool grant_option, + DropBehavior behavior) { - AclMode privileges; - bool all_privs; - ListCell *i; + Relation relation; + ListCell *cell; - if (stmt->privileges == NIL) - { - all_privs = true; + if (all_privs && privileges == ACL_NO_RIGHTS) privileges = ACL_ALL_RIGHTS_LANGUAGE; - } - else - { - all_privs = false; - privileges = ACL_NO_RIGHTS; - foreach(i, stmt->privileges) - { - char *privname = strVal(lfirst(i)); - AclMode priv = string_to_privilege(privname); - if (priv & ~((AclMode) ACL_ALL_RIGHTS_LANGUAGE)) - ereport(ERROR, - (errcode(ERRCODE_INVALID_GRANT_OPERATION), - errmsg("invalid privilege type %s for language", - privilege_to_string(priv)))); - privileges |= priv; - } - } + relation = heap_open(LanguageRelationId, RowExclusiveLock); - foreach(i, stmt->objects) + foreach (cell, objects) { - char *langname = strVal(lfirst(i)); - Relation relation; - HeapTuple tuple; + Oid langid = lfirst_oid(cell); Form_pg_language pg_language_tuple; Datum aclDatum; bool isNull; @@ -765,6 +953,7 @@ ExecuteGrantStmt_Language(GrantStmt *stmt) Acl *new_acl; Oid grantorId; Oid ownerId; + HeapTuple tuple; HeapTuple newtuple; Datum values[Natts_pg_language]; char nulls[Natts_pg_language]; @@ -774,21 +963,20 @@ ExecuteGrantStmt_Language(GrantStmt *stmt) Oid *oldmembers; Oid *newmembers; - relation = heap_open(LanguageRelationId, RowExclusiveLock); - tuple = SearchSysCache(LANGNAME, - PointerGetDatum(langname), + tuple = SearchSysCache(LANGOID, + ObjectIdGetDatum(langid), 0, 0, 0); if (!HeapTupleIsValid(tuple)) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("language \"%s\" does not exist", langname))); + elog(ERROR, "cache lookup failed for language %u", langid); + pg_language_tuple = (Form_pg_language) GETSTRUCT(tuple); if (!pg_language_tuple->lanpltrusted) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("language \"%s\" is not trusted", langname), - errhint("Only superusers may use untrusted languages."))); + errmsg("language \"%s\" is not trusted", + NameStr(pg_language_tuple->lanname)), + errhint("Only superusers may use untrusted languages."))); /* * Get owner ID and working copy of existing ACL. If there's no ACL, @@ -834,7 +1022,7 @@ ExecuteGrantStmt_Language(GrantStmt *stmt) * GRANT case.) */ this_privileges = privileges & ACL_OPTION_TO_PRIVS(avail_goptions); - if (stmt->is_grant) + if (is_grant) { if (this_privileges == 0) ereport(WARNING, @@ -865,9 +1053,9 @@ ExecuteGrantStmt_Language(GrantStmt *stmt) */ noldmembers = aclmembers(old_acl, &oldmembers); - new_acl = merge_acl_with_grant(old_acl, stmt->is_grant, - stmt->grant_option, stmt->behavior, - stmt->grantees, this_privileges, + new_acl = merge_acl_with_grant(old_acl, is_grant, + grant_option, behavior, + grantees, this_privileges, grantorId, ownerId); nnewmembers = aclmembers(new_acl, &newmembers); @@ -880,7 +1068,8 @@ ExecuteGrantStmt_Language(GrantStmt *stmt) replaces[Anum_pg_language_lanacl - 1] = 'r'; values[Anum_pg_language_lanacl - 1] = PointerGetDatum(new_acl); - newtuple = heap_modifytuple(tuple, RelationGetDescr(relation), values, nulls, replaces); + newtuple = heap_modifytuple(tuple, RelationGetDescr(relation), values, + nulls, replaces); simple_heap_update(relation, &newtuple->t_self, newtuple); @@ -889,7 +1078,7 @@ ExecuteGrantStmt_Language(GrantStmt *stmt) /* Update the shared dependency ACL info */ updateAclDependencies(LanguageRelationId, HeapTupleGetOid(tuple), - ownerId, stmt->is_grant, + ownerId, is_grant, noldmembers, oldmembers, nnewmembers, newmembers); @@ -897,48 +1086,29 @@ ExecuteGrantStmt_Language(GrantStmt *stmt) pfree(new_acl); - heap_close(relation, RowExclusiveLock); - /* prevent error when processing duplicate objects */ CommandCounterIncrement(); } + + heap_close(relation, RowExclusiveLock); } static void -ExecuteGrantStmt_Namespace(GrantStmt *stmt) +ExecGrant_Namespace(bool is_grant, List *objects, bool all_privs, + AclMode privileges, List *grantees, bool grant_option, + DropBehavior behavior) { - AclMode privileges; - bool all_privs; - ListCell *i; + Relation relation; + ListCell *cell; - if (stmt->privileges == NIL) - { - all_privs = true; + if (all_privs && privileges == ACL_NO_RIGHTS) privileges = ACL_ALL_RIGHTS_NAMESPACE; - } - else - { - all_privs = false; - privileges = ACL_NO_RIGHTS; - foreach(i, stmt->privileges) - { - char *privname = strVal(lfirst(i)); - AclMode priv = string_to_privilege(privname); - if (priv & ~((AclMode) ACL_ALL_RIGHTS_NAMESPACE)) - ereport(ERROR, - (errcode(ERRCODE_INVALID_GRANT_OPERATION), - errmsg("invalid privilege type %s for schema", - privilege_to_string(priv)))); - privileges |= priv; - } - } + relation = heap_open(NamespaceRelationId, RowExclusiveLock); - foreach(i, stmt->objects) + foreach(cell, objects) { - char *nspname = strVal(lfirst(i)); - Relation relation; - HeapTuple tuple; + Oid nspid = lfirst_oid(cell); Form_pg_namespace pg_namespace_tuple; Datum aclDatum; bool isNull; @@ -948,6 +1118,7 @@ ExecuteGrantStmt_Namespace(GrantStmt *stmt) Acl *new_acl; Oid grantorId; Oid ownerId; + HeapTuple tuple; HeapTuple newtuple; Datum values[Natts_pg_namespace]; char nulls[Natts_pg_namespace]; @@ -957,14 +1128,12 @@ ExecuteGrantStmt_Namespace(GrantStmt *stmt) Oid *oldmembers; Oid *newmembers; - relation = heap_open(NamespaceRelationId, RowExclusiveLock); - tuple = SearchSysCache(NAMESPACENAME, - CStringGetDatum(nspname), + tuple = SearchSysCache(NAMESPACEOID, + ObjectIdGetDatum(nspid), 0, 0, 0); if (!HeapTupleIsValid(tuple)) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_SCHEMA), - errmsg("schema \"%s\" does not exist", nspname))); + elog(ERROR, "cache lookup failed for namespace %u", nspid); + pg_namespace_tuple = (Form_pg_namespace) GETSTRUCT(tuple); /* @@ -997,7 +1166,7 @@ ExecuteGrantStmt_Namespace(GrantStmt *stmt) ACL_ALL_RIGHTS_NAMESPACE | ACL_GRANT_OPTION_FOR(ACL_ALL_RIGHTS_NAMESPACE), ACLMASK_ANY) == ACL_NO_RIGHTS) aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_NAMESPACE, - nspname); + NameStr(pg_namespace_tuple->nspname)); } /* @@ -1009,7 +1178,7 @@ ExecuteGrantStmt_Namespace(GrantStmt *stmt) * GRANT case.) */ this_privileges = privileges & ACL_OPTION_TO_PRIVS(avail_goptions); - if (stmt->is_grant) + if (is_grant) { if (this_privileges == 0) ereport(WARNING, @@ -1040,9 +1209,9 @@ ExecuteGrantStmt_Namespace(GrantStmt *stmt) */ noldmembers = aclmembers(old_acl, &oldmembers); - new_acl = merge_acl_with_grant(old_acl, stmt->is_grant, - stmt->grant_option, stmt->behavior, - stmt->grantees, this_privileges, + new_acl = merge_acl_with_grant(old_acl, is_grant, + grant_option, behavior, + grantees, this_privileges, grantorId, ownerId); nnewmembers = aclmembers(new_acl, &newmembers); @@ -1055,7 +1224,8 @@ ExecuteGrantStmt_Namespace(GrantStmt *stmt) replaces[Anum_pg_namespace_nspacl - 1] = 'r'; values[Anum_pg_namespace_nspacl - 1] = PointerGetDatum(new_acl); - newtuple = heap_modifytuple(tuple, RelationGetDescr(relation), values, nulls, replaces); + newtuple = heap_modifytuple(tuple, RelationGetDescr(relation), values, + nulls, replaces); simple_heap_update(relation, &newtuple->t_self, newtuple); @@ -1064,7 +1234,7 @@ ExecuteGrantStmt_Namespace(GrantStmt *stmt) /* Update the shared dependency ACL info */ updateAclDependencies(NamespaceRelationId, HeapTupleGetOid(tuple), - ownerId, stmt->is_grant, + ownerId, is_grant, noldmembers, oldmembers, nnewmembers, newmembers); @@ -1072,50 +1242,29 @@ ExecuteGrantStmt_Namespace(GrantStmt *stmt) pfree(new_acl); - heap_close(relation, RowExclusiveLock); - /* prevent error when processing duplicate objects */ CommandCounterIncrement(); } + + heap_close(relation, RowExclusiveLock); } static void -ExecuteGrantStmt_Tablespace(GrantStmt *stmt) +ExecGrant_Tablespace(bool is_grant, List *objects, bool all_privs, + AclMode privileges, List *grantees, bool grant_option, + DropBehavior behavior) { - AclMode privileges; - bool all_privs; - ListCell *i; + Relation relation; + ListCell *cell; - if (stmt->privileges == NIL) - { - all_privs = true; + if (all_privs && privileges == ACL_NO_RIGHTS) privileges = ACL_ALL_RIGHTS_TABLESPACE; - } - else - { - all_privs = false; - privileges = ACL_NO_RIGHTS; - foreach(i, stmt->privileges) - { - char *privname = strVal(lfirst(i)); - AclMode priv = string_to_privilege(privname); - if (priv & ~((AclMode) ACL_ALL_RIGHTS_TABLESPACE)) - ereport(ERROR, - (errcode(ERRCODE_INVALID_GRANT_OPERATION), - errmsg("invalid privilege type %s for tablespace", - privilege_to_string(priv)))); - privileges |= priv; - } - } + relation = heap_open(TableSpaceRelationId, RowExclusiveLock); - foreach(i, stmt->objects) + foreach(cell, objects) { - char *spcname = strVal(lfirst(i)); - Relation relation; - ScanKeyData entry[1]; - HeapScanDesc scan; - HeapTuple tuple; + Oid tblId = lfirst_oid(cell); Form_pg_tablespace pg_tablespace_tuple; Datum aclDatum; bool isNull; @@ -1133,18 +1282,21 @@ ExecuteGrantStmt_Tablespace(GrantStmt *stmt) int nnewmembers; Oid *oldmembers; Oid *newmembers; + ScanKeyData entry[1]; + SysScanDesc scan; + HeapTuple tuple; - relation = heap_open(TableSpaceRelationId, RowExclusiveLock); + /* There's no syscache for pg_tablespace, so must look the hard way */ ScanKeyInit(&entry[0], - Anum_pg_tablespace_spcname, - BTEqualStrategyNumber, F_NAMEEQ, - CStringGetDatum(spcname)); - scan = heap_beginscan(relation, SnapshotNow, 1, entry); - tuple = heap_getnext(scan, ForwardScanDirection); + ObjectIdAttributeNumber, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(tblId)); + scan = systable_beginscan(relation, TablespaceOidIndexId, true, + SnapshotNow, 1, entry); + tuple = systable_getnext(scan); if (!HeapTupleIsValid(tuple)) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("tablespace \"%s\" does not exist", spcname))); + elog(ERROR, "cache lookup failed for tablespace %u", tblId); + pg_tablespace_tuple = (Form_pg_tablespace) GETSTRUCT(tuple); /* @@ -1176,7 +1328,7 @@ ExecuteGrantStmt_Tablespace(GrantStmt *stmt) ACL_ALL_RIGHTS_TABLESPACE | ACL_GRANT_OPTION_FOR(ACL_ALL_RIGHTS_TABLESPACE), ACLMASK_ANY) == ACL_NO_RIGHTS) aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_TABLESPACE, - spcname); + NameStr(pg_tablespace_tuple->spcname)); } /* @@ -1188,7 +1340,7 @@ ExecuteGrantStmt_Tablespace(GrantStmt *stmt) * GRANT case.) */ this_privileges = privileges & ACL_OPTION_TO_PRIVS(avail_goptions); - if (stmt->is_grant) + if (is_grant) { if (this_privileges == 0) ereport(WARNING, @@ -1219,9 +1371,9 @@ ExecuteGrantStmt_Tablespace(GrantStmt *stmt) */ noldmembers = aclmembers(old_acl, &oldmembers); - new_acl = merge_acl_with_grant(old_acl, stmt->is_grant, - stmt->grant_option, stmt->behavior, - stmt->grantees, this_privileges, + new_acl = merge_acl_with_grant(old_acl, is_grant, + grant_option, behavior, + grantees, this_privileges, grantorId, ownerId); nnewmembers = aclmembers(new_acl, &newmembers); @@ -1234,7 +1386,8 @@ ExecuteGrantStmt_Tablespace(GrantStmt *stmt) replaces[Anum_pg_tablespace_spcacl - 1] = 'r'; values[Anum_pg_tablespace_spcacl - 1] = PointerGetDatum(new_acl); - newtuple = heap_modifytuple(tuple, RelationGetDescr(relation), values, nulls, replaces); + newtuple = heap_modifytuple(tuple, RelationGetDescr(relation), values, + nulls, replaces); simple_heap_update(relation, &newtuple->t_self, newtuple); @@ -1242,19 +1395,20 @@ ExecuteGrantStmt_Tablespace(GrantStmt *stmt) CatalogUpdateIndexes(relation, newtuple); /* Update the shared dependency ACL info */ - updateAclDependencies(TableSpaceRelationId, HeapTupleGetOid(tuple), - ownerId, stmt->is_grant, + updateAclDependencies(TableSpaceRelationId, tblId, + ownerId, is_grant, noldmembers, oldmembers, nnewmembers, newmembers); - pfree(new_acl); + systable_endscan(scan); - heap_endscan(scan); - heap_close(relation, RowExclusiveLock); + pfree(new_acl); /* prevent error when processing duplicate objects */ CommandCounterIncrement(); } + + heap_close(relation, RowExclusiveLock); } @@ -1537,7 +1691,7 @@ pg_database_aclmask(Oid db_oid, Oid roleid, AclMode result; Relation pg_database; ScanKeyData entry[1]; - HeapScanDesc scan; + SysScanDesc scan; HeapTuple tuple; Datum aclDatum; bool isNull; @@ -1558,8 +1712,9 @@ pg_database_aclmask(Oid db_oid, Oid roleid, ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(db_oid)); - scan = heap_beginscan(pg_database, SnapshotNow, 1, entry); - tuple = heap_getnext(scan, ForwardScanDirection); + scan = systable_beginscan(pg_database, DatabaseOidIndexId, true, + SnapshotNow, 1, entry); + tuple = systable_getnext(scan); if (!HeapTupleIsValid(tuple)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_DATABASE), @@ -1588,7 +1743,7 @@ pg_database_aclmask(Oid db_oid, Oid roleid, if (acl && (Pointer) acl != DatumGetPointer(aclDatum)) pfree(acl); - heap_endscan(scan); + systable_endscan(scan); heap_close(pg_database, AccessShareLock); return result; @@ -1801,7 +1956,7 @@ pg_tablespace_aclmask(Oid spc_oid, Oid roleid, AclMode result; Relation pg_tablespace; ScanKeyData entry[1]; - HeapScanDesc scan; + SysScanDesc scan; HeapTuple tuple; Datum aclDatum; bool isNull; @@ -1829,8 +1984,9 @@ pg_tablespace_aclmask(Oid spc_oid, Oid roleid, ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(spc_oid)); - scan = heap_beginscan(pg_tablespace, SnapshotNow, 1, entry); - tuple = heap_getnext(scan, ForwardScanDirection); + scan = systable_beginscan(pg_tablespace, TablespaceOidIndexId, true, + SnapshotNow, 1, entry); + tuple = systable_getnext(scan); if (!HeapTupleIsValid(tuple)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), @@ -1859,7 +2015,7 @@ pg_tablespace_aclmask(Oid spc_oid, Oid roleid, if (acl && (Pointer) acl != DatumGetPointer(aclDatum)) pfree(acl); - heap_endscan(scan); + systable_endscan(scan); heap_close(pg_tablespace, AccessShareLock); return result; @@ -2091,7 +2247,7 @@ pg_tablespace_ownercheck(Oid spc_oid, Oid roleid) { Relation pg_tablespace; ScanKeyData entry[1]; - HeapScanDesc scan; + SysScanDesc scan; HeapTuple spctuple; Oid spcowner; @@ -2105,9 +2261,10 @@ pg_tablespace_ownercheck(Oid spc_oid, Oid roleid) ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(spc_oid)); - scan = heap_beginscan(pg_tablespace, SnapshotNow, 1, entry); + scan = systable_beginscan(pg_tablespace, TablespaceOidIndexId, true, + SnapshotNow, 1, entry); - spctuple = heap_getnext(scan, ForwardScanDirection); + spctuple = systable_getnext(scan); if (!HeapTupleIsValid(spctuple)) ereport(ERROR, @@ -2116,7 +2273,7 @@ pg_tablespace_ownercheck(Oid spc_oid, Oid roleid) spcowner = ((Form_pg_tablespace) GETSTRUCT(spctuple))->spcowner; - heap_endscan(scan); + systable_endscan(scan); heap_close(pg_tablespace, AccessShareLock); return has_privs_of_role(roleid, spcowner); @@ -2159,7 +2316,7 @@ pg_database_ownercheck(Oid db_oid, Oid roleid) { Relation pg_database; ScanKeyData entry[1]; - HeapScanDesc scan; + SysScanDesc scan; HeapTuple dbtuple; Oid dba; @@ -2173,9 +2330,10 @@ pg_database_ownercheck(Oid db_oid, Oid roleid) ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(db_oid)); - scan = heap_beginscan(pg_database, SnapshotNow, 1, entry); + scan = systable_beginscan(pg_database, DatabaseOidIndexId, true, + SnapshotNow, 1, entry); - dbtuple = heap_getnext(scan, ForwardScanDirection); + dbtuple = systable_getnext(scan); if (!HeapTupleIsValid(dbtuple)) ereport(ERROR, @@ -2184,7 +2342,7 @@ pg_database_ownercheck(Oid db_oid, Oid roleid) dba = ((Form_pg_database) GETSTRUCT(dbtuple))->datdba; - heap_endscan(scan); + systable_endscan(scan); heap_close(pg_database, AccessShareLock); return has_privs_of_role(roleid, dba); diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c index c8f9e53212d1be5498ac7b7f99c83e4b7508ec54..848183dae5dc558448987d7a80ba86ad703f2d6d 100644 --- a/src/backend/catalog/pg_depend.c +++ b/src/backend/catalog/pg_depend.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/pg_depend.c,v 1.15 2005/10/15 02:49:14 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/pg_depend.c,v 1.16 2005/11/21 12:49:30 alvherre Exp $ * *------------------------------------------------------------------------- */ @@ -162,6 +162,58 @@ deleteDependencyRecordsFor(Oid classId, Oid objectId) return count; } +/* + * objectIsInternalDependency -- return whether the specified object + * is listed as an internal dependency for some other object. + * + * This is used to implement DROP/REASSIGN OWNED. We cannot invoke + * performDeletion blindly, because it may try to drop or modify an internal- + * dependent object before the "main" object, so we need to skip the first + * object and expect it to be automatically dropped when the main object is + * dropped. + */ +bool +objectIsInternalDependency(Oid classId, Oid objectId) +{ + Relation depRel; + ScanKeyData key[2]; + SysScanDesc scan; + HeapTuple tup; + bool isdep = false; + + depRel = heap_open(DependRelationId, AccessShareLock); + + ScanKeyInit(&key[0], + Anum_pg_depend_classid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(classId)); + ScanKeyInit(&key[1], + Anum_pg_depend_objid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(objectId)); + + scan = systable_beginscan(depRel, DependDependerIndexId, true, + SnapshotNow, 2, key); + + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup); + + if (depForm->deptype == DEPENDENCY_INTERNAL) + { + /* No need to keep scanning */ + isdep = true; + break; + } + } + + systable_endscan(scan); + + heap_close(depRel, AccessShareLock); + + return isdep; +} + /* * Adjust dependency record(s) to point to a different object of the same type * diff --git a/src/backend/catalog/pg_shdepend.c b/src/backend/catalog/pg_shdepend.c index 4cce7ba13cff36c37f8367b2c3fdef31a41a380a..6bd69cdef6c1b85ba98c3cb69d4991b65e5fe29f 100644 --- a/src/backend/catalog/pg_shdepend.c +++ b/src/backend/catalog/pg_shdepend.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/pg_shdepend.c,v 1.3 2005/10/15 02:49:14 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/pg_shdepend.c,v 1.4 2005/11/21 12:49:30 alvherre Exp $ * *------------------------------------------------------------------------- */ @@ -16,11 +16,24 @@ #include "access/genam.h" #include "access/heapam.h" +#include "utils/acl.h" #include "catalog/dependency.h" #include "catalog/indexing.h" #include "catalog/pg_authid.h" +#include "catalog/pg_conversion.h" #include "catalog/pg_database.h" +#include "catalog/pg_language.h" +#include "catalog/pg_namespace.h" +#include "catalog/pg_operator.h" +#include "catalog/pg_proc.h" #include "catalog/pg_shdepend.h" +#include "catalog/pg_tablespace.h" +#include "catalog/pg_type.h" +#include "commands/conversioncmds.h" +#include "commands/defrem.h" +#include "commands/schemacmds.h" +#include "commands/tablecmds.h" +#include "commands/typecmds.h" #include "lib/stringinfo.h" #include "miscadmin.h" #include "utils/fmgroids.h" @@ -1042,3 +1055,251 @@ isSharedObjectPinned(Oid classId, Oid objectId, Relation sdepRel) return result; } + +/* + * shdepDropOwned + * + * Drop the objects owned by any one of the given RoleIds. If a role has + * access to an object, the grant will be removed as well (but the object + * will not, of course.) + */ +void +shdepDropOwned(List *roleids, DropBehavior behavior) +{ + Relation sdepRel; + ListCell *cell; + + sdepRel = heap_open(SharedDependRelationId, AccessExclusiveLock); + + /* + * For each role, find the dependent objects and drop them using the + * regular (non-shared) dependency management. + */ + foreach(cell, roleids) + { + Oid roleid = lfirst_oid(cell); + ScanKeyData key[2]; + SysScanDesc scan; + HeapTuple tuple; + + /* Doesn't work for pinned objects */ + if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel)) + { + ObjectAddress obj; + + obj.classId = AuthIdRelationId; + obj.objectId = roleid; + obj.objectSubId = 0; + + ereport(ERROR, + (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST), + errmsg("cannot drop objects owned by %s because they are " + "required by the database system", + getObjectDescription(&obj)))); + } + + ScanKeyInit(&key[0], + Anum_pg_shdepend_refclassid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(AuthIdRelationId)); + ScanKeyInit(&key[1], + Anum_pg_shdepend_refobjid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(roleid)); + + scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true, + SnapshotNow, 2, key); + + while ((tuple = systable_getnext(scan)) != NULL) + { + Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tuple); + + /* We only operate on objects on the current database */ + if (sdepForm->dbid != MyDatabaseId) + continue; + + switch (sdepForm->deptype) + { + ObjectAddress obj; + GrantObjectType objtype; + + /* Shouldn't happen */ + case SHARED_DEPENDENCY_PIN: + case SHARED_DEPENDENCY_INVALID: + elog(ERROR, "unexpected dependency type"); + break; + case SHARED_DEPENDENCY_ACL: + switch (sdepForm->classid) + { + case RelationRelationId: + objtype = ACL_OBJECT_RELATION; + break; + case DatabaseRelationId: + objtype = ACL_OBJECT_DATABASE; + break; + case ProcedureRelationId: + objtype = ACL_OBJECT_FUNCTION; + break; + case LanguageRelationId: + objtype = ACL_OBJECT_LANGUAGE; + break; + case NamespaceRelationId: + objtype = ACL_OBJECT_NAMESPACE; + break; + case TableSpaceRelationId: + objtype = ACL_OBJECT_TABLESPACE; + break; + default: + elog(ERROR, "unexpected object type %d", + sdepForm->classid); + /* keep compiler quiet */ + objtype = (GrantObjectType) 0; + break; + } + + ExecGrantStmt_oids(false, objtype, + list_make1_oid(sdepForm->objid), true, + ACL_NO_RIGHTS, list_make1_oid(roleid), + false, DROP_CASCADE); + break; + case SHARED_DEPENDENCY_OWNER: + /* + * If there's a regular (non-shared) dependency on this + * object marked with DEPENDENCY_INTERNAL, skip this + * object. We will drop the referencer object instead. + */ + if (objectIsInternalDependency(sdepForm->classid, sdepForm->objid)) + continue; + + /* Drop the object */ + obj.classId = sdepForm->classid; + obj.objectId = sdepForm->objid; + obj.objectSubId = 0; + performDeletion(&obj, behavior); + break; + } + } + + systable_endscan(scan); + } + + heap_close(sdepRel, AccessExclusiveLock); +} + +/* + * shdepReassignOwned + * + * Change the owner of objects owned by any of the roles in roleids to + * newrole. Grants are not touched. + */ +void +shdepReassignOwned(List *roleids, Oid newrole) +{ + Relation sdepRel; + ListCell *cell; + + sdepRel = heap_open(SharedDependRelationId, AccessShareLock); + + foreach(cell, roleids) + { + SysScanDesc scan; + ScanKeyData key[2]; + HeapTuple tuple; + Oid roleid = lfirst_oid(cell); + + /* Refuse to work on pinned roles */ + if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel)) + { + ObjectAddress obj; + + obj.classId = AuthIdRelationId; + obj.objectId = roleid; + obj.objectSubId = 0; + + ereport(ERROR, + (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST), + errmsg("cannot drop objects owned by %s because they are " + "required by the database system", + getObjectDescription(&obj)))); + /* + * There's no need to tell the whole truth, which is that we + * didn't track these dependencies at all ... + */ + } + + ScanKeyInit(&key[0], + Anum_pg_shdepend_refclassid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(AuthIdRelationId)); + ScanKeyInit(&key[1], + Anum_pg_shdepend_refobjid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(roleid)); + + scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true, + SnapshotNow, 2, key); + + while ((tuple = systable_getnext(scan)) != NULL) + { + Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tuple); + + /* We only operate on objects on the current database */ + if (sdepForm->dbid != MyDatabaseId) + continue; + + /* Unexpected because we checked for pins above */ + if (sdepForm->deptype == SHARED_DEPENDENCY_PIN) + elog(ERROR, "unexpected shared pin"); + + /* We leave non-owner dependencies alone */ + if (sdepForm->deptype != SHARED_DEPENDENCY_OWNER) + continue; + + /* + * If there's a regular (non-shared) dependency on this + * object marked with DEPENDENCY_INTERNAL, skip this + * object. We will alter the referencer object instead. + */ + if (objectIsInternalDependency(sdepForm->classid, sdepForm->objid)) + continue; + + /* Issue the appropiate ALTER OWNER call */ + switch (sdepForm->classid) + { + case ConversionRelationId: + AlterConversionOwner_oid(sdepForm->objid, newrole); + break; + + case TypeRelationId: + AlterTypeOwnerInternal(sdepForm->objid, newrole); + break; + + case OperatorRelationId: + AlterOperatorOwner_oid(sdepForm->objid, newrole); + break; + + case NamespaceRelationId: + AlterSchemaOwner_oid(sdepForm->objid, newrole); + break; + + case RelationRelationId: + ATExecChangeOwner(sdepForm->objid, newrole, false); + break; + + case ProcedureRelationId: + AlterFunctionOwner_oid(sdepForm->objid, newrole); + break; + + default: + elog(ERROR, "unexpected classid %d", sdepForm->classid); + break; + } + /* Make sure the next iteration will see my changes */ + CommandCounterIncrement(); + } + + systable_endscan(scan); + } + + heap_close(sdepRel, AccessShareLock); +} diff --git a/src/backend/commands/conversioncmds.c b/src/backend/commands/conversioncmds.c index 97abc9fc77410e29b5dc26de479dee0ddc37efc1..42bb0853a10b9e13c671333300426dac291e1f09 100644 --- a/src/backend/commands/conversioncmds.c +++ b/src/backend/commands/conversioncmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/conversioncmds.c,v 1.24 2005/11/19 17:39:44 adunstan Exp $ + * $PostgreSQL: pgsql/src/backend/commands/conversioncmds.c,v 1.25 2005/11/21 12:49:30 alvherre Exp $ * *------------------------------------------------------------------------- */ @@ -30,6 +30,8 @@ #include "utils/lsyscache.h" #include "utils/syscache.h" +static void AlterConversionOwner_internal(Relation rel, Oid conversionOid, + Oid newOwnerId); /* * CREATE CONVERSION @@ -185,16 +187,13 @@ RenameConversion(List *name, const char *newname) } /* - * Change conversion owner + * Change conversion owner, by name */ void AlterConversionOwner(List *name, Oid newOwnerId) { Oid conversionOid; - HeapTuple tup; Relation rel; - Form_pg_conversion convForm; - AclResult aclresult; rel = heap_open(ConversionRelationId, RowExclusiveLock); @@ -205,6 +204,40 @@ AlterConversionOwner(List *name, Oid newOwnerId) errmsg("conversion \"%s\" does not exist", NameListToString(name)))); + AlterConversionOwner_internal(rel, conversionOid, newOwnerId); + + heap_close(rel, NoLock); +} + +/* + * Change conversion owner, by oid + */ +void +AlterConversionOwner_oid(Oid conversionOid, Oid newOwnerId) +{ + Relation rel; + + rel = heap_open(ConversionRelationId, RowExclusiveLock); + + AlterConversionOwner_internal(rel, conversionOid, newOwnerId); + + heap_close(rel, NoLock); +} + +/* + * AlterConversionOwner_internal + * + * Internal routine for changing the owner. rel must be pg_conversion, already + * open and suitably locked; it will not be closed. + */ +static void +AlterConversionOwner_internal(Relation rel, Oid conversionOid, Oid newOwnerId) +{ + Form_pg_conversion convForm; + HeapTuple tup; + + Assert(RelationGetRelid(rel) == ConversionRelationId); + tup = SearchSysCacheCopy(CONOID, ObjectIdGetDatum(conversionOid), 0, 0, 0); @@ -219,13 +252,15 @@ AlterConversionOwner(List *name, Oid newOwnerId) */ if (convForm->conowner != newOwnerId) { + AclResult aclresult; + /* Superusers can always do it */ if (!superuser()) { /* Otherwise, must be owner of the existing object */ if (!pg_conversion_ownercheck(HeapTupleGetOid(tup), GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION, - NameListToString(name)); + NameStr(convForm->conname)); /* Must be able to become new owner */ check_is_member_of_role(GetUserId(), newOwnerId); @@ -253,6 +288,5 @@ AlterConversionOwner(List *name, Oid newOwnerId) newOwnerId); } - heap_close(rel, NoLock); heap_freetuple(tup); } diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c index f4d6164775ee48e636aa78c819b9755c1a16a96d..77f892fe501f8e77e3df70cdd0e45a287ebdfb67 100644 --- a/src/backend/commands/functioncmds.c +++ b/src/backend/commands/functioncmds.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.69 2005/10/15 02:49:15 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.70 2005/11/21 12:49:31 alvherre Exp $ * * DESCRIPTION * These routines take the parse tree and pick out the @@ -55,6 +55,7 @@ #include "utils/lsyscache.h" #include "utils/syscache.h" +static void AlterFunctionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId); /* * Examine the RETURNS clause of the CREATE FUNCTION statement @@ -853,16 +854,14 @@ RenameFunction(List *name, List *argtypes, const char *newname) } /* - * Change function owner + * Change function owner by name and args */ void AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId) { + Relation rel; Oid procOid; HeapTuple tup; - Form_pg_proc procForm; - Relation rel; - AclResult aclresult; rel = heap_open(ProcedureRelationId, RowExclusiveLock); @@ -873,15 +872,53 @@ AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId) 0, 0, 0); if (!HeapTupleIsValid(tup)) /* should not happen */ elog(ERROR, "cache lookup failed for function %u", procOid); - procForm = (Form_pg_proc) GETSTRUCT(tup); - if (procForm->proisagg) + if (((Form_pg_proc) GETSTRUCT(tup))->proisagg) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is an aggregate function", NameListToString(name)), errhint("Use ALTER AGGREGATE to change owner of aggregate functions."))); + AlterFunctionOwner_internal(rel, tup, newOwnerId); + + heap_close(rel, NoLock); +} + +/* + * Change function owner by Oid + */ +void +AlterFunctionOwner_oid(Oid procOid, Oid newOwnerId) +{ + Relation rel; + HeapTuple tup; + + rel = heap_open(ProcedureRelationId, RowExclusiveLock); + + tup = SearchSysCache(PROCOID, + ObjectIdGetDatum(procOid), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) /* should not happen */ + elog(ERROR, "cache lookup failed for function %u", procOid); + AlterFunctionOwner_internal(rel, tup, newOwnerId); + + heap_close(rel, NoLock); +} + +static void +AlterFunctionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) +{ + Form_pg_proc procForm; + AclResult aclresult; + Oid procOid; + + Assert(RelationGetRelid(rel) == ProcedureRelationId); + Assert(tup->t_tableOid == ProcedureRelationId); + + procForm = (Form_pg_proc) GETSTRUCT(tup); + procOid = HeapTupleGetOid(tup); + /* * If the new owner is the same as the existing owner, consider the * command to have succeeded. This is for dump restoration purposes. @@ -902,7 +939,7 @@ AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId) /* Otherwise, must be owner of the existing object */ if (!pg_proc_ownercheck(procOid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, - NameListToString(name)); + NameStr(procForm->proname)); /* Must be able to become new owner */ check_is_member_of_role(GetUserId(), newOwnerId); @@ -937,7 +974,8 @@ AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId) repl_val[Anum_pg_proc_proacl - 1] = PointerGetDatum(newAcl); } - newtuple = heap_modifytuple(tup, RelationGetDescr(rel), repl_val, repl_null, repl_repl); + newtuple = heap_modifytuple(tup, RelationGetDescr(rel), repl_val, + repl_null, repl_repl); simple_heap_update(rel, &newtuple->t_self, newtuple); CatalogUpdateIndexes(rel, newtuple); @@ -949,7 +987,6 @@ AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId) } ReleaseSysCache(tup); - heap_close(rel, NoLock); } /* diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c index ea8afcfccbf8bbf249609169b5bc176e85a7b9da..10745032de2f3d460014dbce74bcca44b82ed4b0 100644 --- a/src/backend/commands/opclasscmds.c +++ b/src/backend/commands/opclasscmds.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/opclasscmds.c,v 1.38 2005/10/15 02:49:15 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/opclasscmds.c,v 1.39 2005/11/21 12:49:31 alvherre Exp $ * *------------------------------------------------------------------------- */ @@ -58,6 +58,8 @@ static Oid assignProcSubtype(Oid amoid, Oid typeoid, Oid procOid); static void addClassMember(List **list, OpClassMember *member, bool isProc); static void storeOperators(Oid opclassoid, List *operators); static void storeProcedures(Oid opclassoid, List *procedures); +static void AlterOpClassOwner_internal(Relation rel, HeapTuple tuple, + Oid newOwnerId); /* @@ -879,20 +881,39 @@ RenameOpClass(List *name, const char *access_method, const char *newname) } /* - * Change opclass owner + * Change opclass owner by oid + */ +void +AlterOpClassOwner_oid(Oid opcOid, Oid newOwnerId) +{ + Relation rel; + HeapTuple tup; + + rel = heap_open(OperatorClassRelationId, RowExclusiveLock); + + tup = SearchSysCacheCopy(CLAOID, + ObjectIdGetDatum(opcOid), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) /* shouldn't happen */ + elog(ERROR, "cache lookup failed for opclass %u", opcOid); + + AlterOpClassOwner_internal(rel, tup, newOwnerId); + + heap_freetuple(tup); + heap_close(rel, NoLock); +} + +/* + * Change opclass owner by name */ void AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId) { - Oid opcOid; Oid amOid; - Oid namespaceOid; - char *schemaname; - char *opcname; - HeapTuple tup; Relation rel; - AclResult aclresult; - Form_pg_opclass opcForm; + HeapTuple tup; + char *opcname; + char *schemaname; amOid = GetSysCacheOid(AMNAME, CStringGetDatum(access_method), @@ -912,6 +933,8 @@ AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId) if (schemaname) { + Oid namespaceOid; + namespaceOid = LookupExplicitNamespace(schemaname); tup = SearchSysCacheCopy(CLAAMNAMENSP, @@ -924,11 +947,11 @@ AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId) (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("operator class \"%s\" does not exist for access method \"%s\"", opcname, access_method))); - - opcOid = HeapTupleGetOid(tup); } else { + Oid opcOid; + opcOid = OpclassnameGetOpcid(amOid, opcname); if (!OidIsValid(opcOid)) ereport(ERROR, @@ -941,10 +964,32 @@ AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId) 0, 0, 0); if (!HeapTupleIsValid(tup)) /* should not happen */ elog(ERROR, "cache lookup failed for opclass %u", opcOid); - namespaceOid = ((Form_pg_opclass) GETSTRUCT(tup))->opcnamespace; } + + AlterOpClassOwner_internal(rel, tup, newOwnerId); + + heap_freetuple(tup); + heap_close(rel, NoLock); +} + +/* + * The first parameter is pg_opclass, opened and suitably locked. The second + * parameter is the tuple from pg_opclass we want to modify. + */ +static void +AlterOpClassOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) +{ + Oid namespaceOid; + AclResult aclresult; + Form_pg_opclass opcForm; + + Assert(tup->t_tableOid == OperatorClassRelationId); + Assert(RelationGetRelid(rel) == OperatorClassRelationId); + opcForm = (Form_pg_opclass) GETSTRUCT(tup); + namespaceOid = opcForm->opcnamespace; + /* * If the new owner is the same as the existing owner, consider the * command to have succeeded. This is for dump restoration purposes. @@ -957,7 +1002,7 @@ AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId) /* Otherwise, must be owner of the existing object */ if (!pg_opclass_ownercheck(HeapTupleGetOid(tup), GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPCLASS, - NameListToString(name)); + NameStr(opcForm->opcname)); /* Must be able to become new owner */ check_is_member_of_role(GetUserId(), newOwnerId); @@ -980,9 +1025,7 @@ AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId) CatalogUpdateIndexes(rel, tup); /* Update owner dependency reference */ - changeDependencyOnOwner(OperatorClassRelationId, opcOid, newOwnerId); + changeDependencyOnOwner(OperatorClassRelationId, HeapTupleGetOid(tup), + newOwnerId); } - - heap_close(rel, NoLock); - heap_freetuple(tup); } diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c index 07877962e3f152bdcc9f54e62beae8b74ddc5bdc..bcc2abe5fb718898f7103eca42594f44221d17e3 100644 --- a/src/backend/commands/operatorcmds.c +++ b/src/backend/commands/operatorcmds.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/operatorcmds.c,v 1.26 2005/10/15 02:49:15 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/operatorcmds.c,v 1.27 2005/11/21 12:49:31 alvherre Exp $ * * DESCRIPTION * The "DefineFoo" routines take the parse tree and pick out the @@ -48,6 +48,8 @@ #include "utils/syscache.h" +static void AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId); + /* * DefineOperator * this function extracts all the information from the @@ -260,6 +262,18 @@ RemoveOperatorById(Oid operOid) heap_close(relation, RowExclusiveLock); } +void +AlterOperatorOwner_oid(Oid operOid, Oid newOwnerId) +{ + Relation rel; + + rel = heap_open(OperatorRelationId, RowExclusiveLock); + + AlterOperatorOwner_internal(rel, operOid, newOwnerId); + + heap_close(rel, NoLock); +} + /* * change operator owner */ @@ -268,16 +282,27 @@ AlterOperatorOwner(List *name, TypeName *typeName1, TypeName *typeName2, Oid newOwnerId) { Oid operOid; - HeapTuple tup; Relation rel; - AclResult aclresult; - Form_pg_operator oprForm; rel = heap_open(OperatorRelationId, RowExclusiveLock); operOid = LookupOperNameTypeNames(name, typeName1, typeName2, false); + AlterOperatorOwner_internal(rel, operOid, newOwnerId); + + heap_close(rel, NoLock); +} + +static void +AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId) +{ + HeapTuple tup; + AclResult aclresult; + Form_pg_operator oprForm; + + Assert(RelationGetRelid(rel) == OperatorRelationId); + tup = SearchSysCacheCopy(OPEROID, ObjectIdGetDatum(operOid), 0, 0, 0); @@ -298,7 +323,7 @@ AlterOperatorOwner(List *name, TypeName *typeName1, TypeName *typeName2, /* Otherwise, must be owner of the existing object */ if (!pg_oper_ownercheck(operOid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER, - NameListToString(name)); + NameStr(oprForm->oprname)); /* Must be able to become new owner */ check_is_member_of_role(GetUserId(), newOwnerId); @@ -325,7 +350,5 @@ AlterOperatorOwner(List *name, TypeName *typeName1, TypeName *typeName2, changeDependencyOnOwner(OperatorRelationId, operOid, newOwnerId); } - heap_close(rel, NoLock); heap_freetuple(tup); - } diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c index caa336d2c455395f12b4a830e96e6203caba0e56..7a7b930ce055c0496f5622a8d195fd3798c41709 100644 --- a/src/backend/commands/schemacmds.c +++ b/src/backend/commands/schemacmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/schemacmds.c,v 1.36 2005/11/19 17:39:44 adunstan Exp $ + * $PostgreSQL: pgsql/src/backend/commands/schemacmds.c,v 1.37 2005/11/21 12:49:31 alvherre Exp $ * *------------------------------------------------------------------------- */ @@ -31,6 +31,8 @@ #include "utils/syscache.h" +static void AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId); + /* * CREATE SCHEMA */ @@ -277,6 +279,28 @@ RenameSchema(const char *oldname, const char *newname) heap_freetuple(tup); } +void +AlterSchemaOwner_oid(Oid oid, Oid newOwnerId) +{ + HeapTuple tup; + Relation rel; + + rel = heap_open(NamespaceRelationId, RowExclusiveLock); + + tup = SearchSysCache(NAMESPACEOID, + ObjectIdGetDatum(oid), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "cache lookup failed for schema %u", oid); + + AlterSchemaOwner_internal(tup, rel, newOwnerId); + + ReleaseSysCache(tup); + + heap_close(rel, RowExclusiveLock); +} + + /* * Change schema owner */ @@ -285,7 +309,6 @@ AlterSchemaOwner(const char *name, Oid newOwnerId) { HeapTuple tup; Relation rel; - Form_pg_namespace nspForm; rel = heap_open(NamespaceRelationId, RowExclusiveLock); @@ -296,6 +319,22 @@ AlterSchemaOwner(const char *name, Oid newOwnerId) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_SCHEMA), errmsg("schema \"%s\" does not exist", name))); + + AlterSchemaOwner_internal(tup, rel, newOwnerId); + + ReleaseSysCache(tup); + + heap_close(rel, RowExclusiveLock); +} + +static void +AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId) +{ + Form_pg_namespace nspForm; + + Assert(tup->t_tableOid == NamespaceRelationId); + Assert(RelationGetRelid(rel) == NamespaceRelationId); + nspForm = (Form_pg_namespace) GETSTRUCT(tup); /* @@ -316,7 +355,7 @@ AlterSchemaOwner(const char *name, Oid newOwnerId) /* Otherwise, must be owner of the existing object */ if (!pg_namespace_ownercheck(HeapTupleGetOid(tup), GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE, - name); + NameStr(nspForm->nspname)); /* Must be able to become new owner */ check_is_member_of_role(GetUserId(), newOwnerId); @@ -369,6 +408,4 @@ AlterSchemaOwner(const char *name, Oid newOwnerId) newOwnerId); } - ReleaseSysCache(tup); - heap_close(rel, NoLock); } diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index abec1a835d1b6352846dcc08dd7fcf1cc3041f0e..09161e0d4b914567413b0e34a668ccafb3b7865b 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.174 2005/10/15 02:49:15 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.175 2005/11/21 12:49:31 alvherre Exp $ * *------------------------------------------------------------------------- */ @@ -236,7 +236,6 @@ 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, bool recursing); static void change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId); static void ATExecClusterOn(Relation rel, const char *indexName); @@ -5264,7 +5263,7 @@ ATPostAlterTypeParse(char *cmd, List **wqueue) * checks (this is necessary not just an optimization, else we'd fail to * handle toast tables properly). */ -static void +void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing) { Relation target_rel; diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 475c251b2fc17100e8572f4ce1ebe385aacccf80..30044f10bf129b8c4160ddf68ab3d466d584fc0c 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.83 2005/11/19 17:39:44 adunstan Exp $ + * $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.84 2005/11/21 12:49:31 alvherre Exp $ * * DESCRIPTION * The "DefineFoo" routines take the parse tree and pick out the @@ -2122,7 +2122,8 @@ 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. + * table's rowtype, and to implement REASSIGN OWNED BY. It assumes the + * caller has done all needed checks. */ void AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId) diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c index 91befbc6aba3966277b216d59f406876c37780de..9ac3c8a97ef5a6805a1ea11c7ceac7c3ccb62514 100644 --- a/src/backend/commands/user.c +++ b/src/backend/commands/user.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/backend/commands/user.c,v 1.164 2005/11/04 17:25:15 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/user.c,v 1.165 2005/11/21 12:49:31 alvherre Exp $ * *------------------------------------------------------------------------- */ @@ -1118,6 +1118,67 @@ GrantRole(GrantRoleStmt *stmt) auth_file_update_needed(); } +/* + * DropOwnedObjects + * + * Drop the objects owned by a given list of roles. + */ +void +DropOwnedObjects(DropOwnedStmt *stmt) +{ + List *role_ids = roleNamesToIds(stmt->roles); + ListCell *cell; + + /* Check privileges */ + foreach (cell, role_ids) + { + Oid roleid = lfirst_oid(cell); + + if (!has_privs_of_role(GetUserId(), roleid)) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied to drop objects"))); + } + + /* Ok, do it */ + shdepDropOwned(role_ids, stmt->behavior); +} + +/* + * ReassignOwnedObjects + * + * Give the objects owned by a given list of roles away to another user. + */ +void +ReassignOwnedObjects(ReassignOwnedStmt *stmt) +{ + List *role_ids = roleNamesToIds(stmt->roles); + ListCell *cell; + Oid newrole; + + /* Check privileges */ + foreach (cell, role_ids) + { + Oid roleid = lfirst_oid(cell); + + if (!has_privs_of_role(GetUserId(), roleid)) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied to reassign objects"))); + } + + /* Must have privileges on the receiving side too */ + newrole = get_roleid_checked(stmt->newrole); + + if (!has_privs_of_role(GetUserId(), newrole)) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied to reassign objects"))); + + /* Ok, do it */ + shdepReassignOwned(role_ids, newrole); +} + /* * roleNamesToIds * diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 1b3c6076fc049fb4388d5e5ea1e55483693ff36e..bfce72b4057bde211d0786a8c3ef15ed39fd6141 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.318 2005/11/20 23:24:12 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.319 2005/11/21 12:49:31 alvherre Exp $ * *------------------------------------------------------------------------- */ @@ -2595,6 +2595,27 @@ _copyDeallocateStmt(DeallocateStmt *from) return newnode; } +static DropOwnedStmt * +_copyDropOwnedStmt(DropOwnedStmt *from) +{ + DropOwnedStmt *newnode = makeNode(DropOwnedStmt); + + COPY_NODE_FIELD(roles); + COPY_SCALAR_FIELD(behavior); + + return newnode; +} + +static ReassignOwnedStmt * +_copyReassignOwnedStmt(ReassignOwnedStmt *from) +{ + ReassignOwnedStmt *newnode = makeNode(ReassignOwnedStmt); + + COPY_NODE_FIELD(roles); + COPY_SCALAR_FIELD(newrole); + + return newnode; +} /* **************************************************************** * pg_list.h copy functions @@ -3146,6 +3167,12 @@ copyObject(void *from) case T_DeallocateStmt: retval = _copyDeallocateStmt(from); break; + case T_DropOwnedStmt: + retval = _copyDropOwnedStmt(from); + break; + case T_ReassignOwnedStmt: + retval = _copyReassignOwnedStmt(from); + break; case T_A_Expr: retval = _copyAExpr(from); diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index bc9c8f3f2a5ebf65d7bc0541d2a0b91f20256980..fa27e5871b986a06ee9cd74cd6175d4db4c49bde 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -18,7 +18,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.255 2005/11/20 23:24:12 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.256 2005/11/21 12:49:31 alvherre Exp $ * *------------------------------------------------------------------------- */ @@ -1469,10 +1469,23 @@ _equalDeallocateStmt(DeallocateStmt *a, DeallocateStmt *b) return true; } +static bool +_equalDropOwnedStmt(DropOwnedStmt *a, DropOwnedStmt *b) +{ + COMPARE_NODE_FIELD(roles); + COMPARE_SCALAR_FIELD(behavior); -/* - * stuff from parsenodes.h - */ + return true; +} + +static bool +_equalReassignOwnedStmt(ReassignOwnedStmt *a, ReassignOwnedStmt *b) +{ + COMPARE_NODE_FIELD(roles); + COMPARE_NODE_FIELD(newrole); + + return true; +} static bool _equalAExpr(A_Expr *a, A_Expr *b) @@ -2188,6 +2201,13 @@ equal(void *a, void *b) case T_DeallocateStmt: retval = _equalDeallocateStmt(a, b); break; + case T_DropOwnedStmt: + retval = _equalDropOwnedStmt(a, b); + break; + + case T_ReassignOwnedStmt: + retval = _equalReassignOwnedStmt(a, b); + break; case T_A_Expr: retval = _equalAExpr(a, b); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index c91100abe4e0cf0ea30d996dbbff9ed47bef9902..15b079ca6ff417aa2c0eb734ef272b8a9ebb229b 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.513 2005/11/19 17:39:44 adunstan Exp $ + * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.514 2005/11/21 12:49:31 alvherre Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -153,6 +153,7 @@ static void doNegateFloat(Value *v); VariableResetStmt VariableSetStmt VariableShowStmt ViewStmt CheckPointStmt CreateConversionStmt DeallocateStmt PrepareStmt ExecuteStmt + DropOwnedStmt ReassignOwnedStmt %type <node> select_no_parens select_with_parens select_clause simple_select @@ -382,7 +383,7 @@ static void doNegateFloat(Value *v); NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF NUMERIC OBJECT_P OF OFF OFFSET OIDS OLD ON ONLY OPERATOR OPTION OR - ORDER OUT_P OUTER_P OVERLAPS OVERLAY OWNER + ORDER OUT_P OUTER_P OVERLAPS OVERLAY OWNED OWNER PARTIAL PASSWORD PLACING POSITION PRECISION PRESERVE PREPARE PREPARED PRIMARY @@ -390,7 +391,7 @@ static void doNegateFloat(Value *v); QUOTE - READ REAL RECHECK REFERENCES REINDEX RELATIVE_P RELEASE RENAME + READ REAL REASSIGN RECHECK REFERENCES REINDEX RELATIVE_P RELEASE RENAME REPEATABLE REPLACE RESET RESTART RESTRICT RETURNS REVOKE RIGHT ROLE ROLLBACK ROW ROWS RULE @@ -533,6 +534,7 @@ stmt : | DropCastStmt | DropGroupStmt | DropOpClassStmt + | DropOwnedStmt | DropPLangStmt | DropRuleStmt | DropStmt @@ -553,6 +555,7 @@ stmt : | LockStmt | NotifyStmt | PrepareStmt + | ReassignOwnedStmt | ReindexStmt | RemoveAggrStmt | RemoveFuncStmt @@ -2813,6 +2816,33 @@ DropOpClassStmt: } ; +/***************************************************************************** + * + * QUERY: + * + * DROP OWNED BY username [, username ...] [ RESTRICT | CASCADE ] + * REASSIGN OWNED BY username [, username ...] TO username + * + *****************************************************************************/ +DropOwnedStmt: + DROP OWNED BY name_list opt_drop_behavior + { + DropOwnedStmt *n = makeNode(DropOwnedStmt); + n->roles = $4; + n->behavior = $5; + $$ = (Node *)n; + } + ; + +ReassignOwnedStmt: + REASSIGN OWNED BY name_list TO name + { + ReassignOwnedStmt *n = makeNode(ReassignOwnedStmt); + n->roles = $4; + n->newrole = $6; + $$ = (Node *)n; + } + ; /***************************************************************************** * @@ -8222,6 +8252,7 @@ unreserved_keyword: | OIDS | OPERATOR | OPTION + | OWNED | OWNER | PARTIAL | PASSWORD @@ -8234,6 +8265,7 @@ unreserved_keyword: | PROCEDURE | QUOTE | READ + | REASSIGN | RECHECK | REINDEX | RELATIVE_P diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c index 797ea610db5ac22f700eae06ec681e4e53e67f87..639c260b69ba894127b1bf6c24ac2fce4760c69a 100644 --- a/src/backend/parser/keywords.c +++ b/src/backend/parser/keywords.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.167 2005/11/19 17:39:44 adunstan Exp $ + * $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.168 2005/11/21 12:49:32 alvherre Exp $ * *------------------------------------------------------------------------- */ @@ -252,6 +252,7 @@ static const ScanKeyword ScanKeywords[] = { {"outer", OUTER_P}, {"overlaps", OVERLAPS}, {"overlay", OVERLAY}, + {"owned", OWNED}, {"owner", OWNER}, {"partial", PARTIAL}, {"password", PASSWORD}, @@ -269,6 +270,7 @@ static const ScanKeyword ScanKeywords[] = { {"quote", QUOTE}, {"read", READ}, {"real", REAL}, + {"reassign", REASSIGN}, {"recheck", RECHECK}, {"references", REFERENCES}, {"reindex", REINDEX}, diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 75458bfce352f331b0336ef3781d9508ba2644aa..a17021eb2916550000bf24d7112652eb31b909d5 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.246 2005/11/19 17:39:45 adunstan Exp $ + * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.247 2005/11/21 12:49:32 alvherre Exp $ * *------------------------------------------------------------------------- */ @@ -347,6 +347,8 @@ check_xact_readonly(Node *parsetree) case T_GrantStmt: case T_GrantRoleStmt: case T_TruncateStmt: + case T_DropOwnedStmt: + case T_ReassignOwnedStmt: ereport(ERROR, (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION), errmsg("transaction is read-only"))); @@ -725,8 +727,7 @@ ProcessUtility(Node *parsetree, break; /* - * ******************************** object creation / destruction ******************************** - * + * **************** object creation / destruction ****************** */ case T_DefineStmt: { @@ -1019,6 +1020,14 @@ ProcessUtility(Node *parsetree, DropRole((DropRoleStmt *) parsetree); break; + case T_DropOwnedStmt: + DropOwnedObjects((DropOwnedStmt *) parsetree); + break; + + case T_ReassignOwnedStmt: + ReassignOwnedObjects((ReassignOwnedStmt *) parsetree); + break; + case T_LockStmt: LockTableCommand((LockStmt *) parsetree); break; @@ -1677,6 +1686,14 @@ CreateCommandTag(Node *parsetree) tag = "DROP ROLE"; break; + case T_DropOwnedStmt: + tag = "DROP OWNED"; + break; + + case T_ReassignOwnedStmt: + tag = "REASSIGN OWNED"; + break; + case T_LockStmt: tag = "LOCK TABLE"; break; diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h index 5c5e87c746e952c511e7ceae2c1ba197d8c1b376..c91406390b015f96fecdc1a551856e967320d6ce 100644 --- a/src/include/catalog/dependency.h +++ b/src/include/catalog/dependency.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/catalog/dependency.h,v 1.17 2005/10/15 02:49:42 momjian Exp $ + * $PostgreSQL: pgsql/src/include/catalog/dependency.h,v 1.18 2005/11/21 12:49:32 alvherre Exp $ * *------------------------------------------------------------------------- */ @@ -177,6 +177,8 @@ extern long changeDependencyFor(Oid classId, Oid objectId, Oid refClassId, Oid oldRefObjectId, Oid newRefObjectId); +extern bool objectIsInternalDependency(Oid classId, Oid objectId); + /* in pg_shdepend.c */ extern void recordSharedDependencyOn(ObjectAddress *depender, @@ -201,4 +203,8 @@ extern void copyTemplateDependencies(Oid templateDbId, Oid newDbId); extern void dropDatabaseDependencies(Oid databaseId); +extern void shdepDropOwned(List *relids, DropBehavior behavior); + +extern void shdepReassignOwned(List *relids, Oid newrole); + #endif /* DEPENDENCY_H */ diff --git a/src/include/commands/conversioncmds.h b/src/include/commands/conversioncmds.h index f484ceaff2cf112dd1f212be88c973941486bf0b..05d30a31cba3ebe3bbccc31d0b3b8b8b7cdce911 100644 --- a/src/include/commands/conversioncmds.h +++ b/src/include/commands/conversioncmds.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/conversioncmds.h,v 1.11 2005/11/19 17:39:45 adunstan Exp $ + * $PostgreSQL: pgsql/src/include/commands/conversioncmds.h,v 1.12 2005/11/21 12:49:32 alvherre Exp $ * *------------------------------------------------------------------------- */ @@ -22,5 +22,6 @@ extern void DropConversionCommand(List *conversion_name, DropBehavior behavior, bool missing_ok); extern void RenameConversion(List *name, const char *newname); extern void AlterConversionOwner(List *name, Oid newOwnerId); +extern void AlterConversionOwner_oid(Oid conversionOid, Oid newOwnerId); #endif /* CONVERSIONCMDS_H */ diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h index ad7c6a178a02c8d8650a0b28d733b665f581567d..39603cf31f27bd7de9e9218da54b9f36bb3ae3d0 100644 --- a/src/include/commands/defrem.h +++ b/src/include/commands/defrem.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/defrem.h,v 1.68 2005/10/15 02:49:44 momjian Exp $ + * $PostgreSQL: pgsql/src/include/commands/defrem.h,v 1.69 2005/11/21 12:49:32 alvherre Exp $ * *------------------------------------------------------------------------- */ @@ -51,6 +51,7 @@ extern void SetFunctionReturnType(Oid funcOid, Oid newRetType); extern void SetFunctionArgType(Oid funcOid, int argIndex, Oid newArgType); extern void RenameFunction(List *name, List *argtypes, const char *newname); extern void AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId); +extern void AlterFunctionOwner_oid(Oid procOid, Oid newOwnerId); extern void AlterFunction(AlterFunctionStmt *stmt); extern void CreateCast(CreateCastStmt *stmt); extern void DropCast(DropCastStmt *stmt); @@ -64,6 +65,7 @@ extern void RemoveOperator(RemoveOperStmt *stmt); extern void RemoveOperatorById(Oid operOid); extern void AlterOperatorOwner(List *name, TypeName *typeName1, TypeName *typename2, Oid newOwnerId); +extern void AlterOperatorOwner_oid(Oid operOid, Oid newOwnerId); /* commands/aggregatecmds.c */ extern void DefineAggregate(List *names, List *parameters); @@ -77,6 +79,7 @@ extern void RemoveOpClass(RemoveOpClassStmt *stmt); extern void RemoveOpClassById(Oid opclassOid); extern void RenameOpClass(List *name, const char *access_method, const char *newname); extern void AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId); +extern void AlterOpClassOwner_oid(Oid opcOid, Oid newOwnerId); /* support routines in commands/define.c */ diff --git a/src/include/commands/schemacmds.h b/src/include/commands/schemacmds.h index f47baf6ef3b71a65450ad48d1e59092d14c2167c..6611364bf75076313a11d1be0977af5491f8f943 100644 --- a/src/include/commands/schemacmds.h +++ b/src/include/commands/schemacmds.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/schemacmds.h,v 1.11 2005/11/19 17:39:45 adunstan Exp $ + * $PostgreSQL: pgsql/src/include/commands/schemacmds.h,v 1.12 2005/11/21 12:49:32 alvherre Exp $ * *------------------------------------------------------------------------- */ @@ -24,5 +24,6 @@ extern void RemoveSchemaById(Oid schemaOid); extern void RenameSchema(const char *oldname, const char *newname); extern void AlterSchemaOwner(const char *name, Oid newOwnerId); +extern void AlterSchemaOwner_oid(const Oid schemaOid, Oid newOwnerId); #endif /* SCHEMACMDS_H */ diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h index 077416484486489358765bb59b91bd2f13a03c7e..b6e30f6f699b27f9268d3b235cbac9bf379d3bc4 100644 --- a/src/include/commands/tablecmds.h +++ b/src/include/commands/tablecmds.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/tablecmds.h,v 1.24 2005/10/15 02:49:44 momjian Exp $ + * $PostgreSQL: pgsql/src/include/commands/tablecmds.h,v 1.25 2005/11/21 12:49:32 alvherre Exp $ * *------------------------------------------------------------------------- */ @@ -24,6 +24,8 @@ extern void RemoveRelation(const RangeVar *relation, DropBehavior behavior); extern void AlterTable(AlterTableStmt *stmt); +extern void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing); + extern void AlterTableInternal(Oid relid, List *cmds, bool recurse); extern void AlterTableCreateToastTable(Oid relOid, bool silent); diff --git a/src/include/commands/user.h b/src/include/commands/user.h index ab2829a266bba88730e1ff4892103fd6784ec878..a8fba2f874d349468bf155484cdb1186b9354ab4 100644 --- a/src/include/commands/user.h +++ b/src/include/commands/user.h @@ -4,7 +4,7 @@ * Commands for manipulating roles (formerly called users). * * - * $PostgreSQL: pgsql/src/include/commands/user.h,v 1.27 2005/06/28 05:09:12 tgl Exp $ + * $PostgreSQL: pgsql/src/include/commands/user.h,v 1.28 2005/11/21 12:49:32 alvherre Exp $ * *------------------------------------------------------------------------- */ @@ -20,5 +20,7 @@ extern void AlterRoleSet(AlterRoleSetStmt *stmt); extern void DropRole(DropRoleStmt *stmt); extern void GrantRole(GrantRoleStmt *stmt); extern void RenameRole(const char *oldname, const char *newname); +extern void DropOwnedObjects(DropOwnedStmt *stmt); +extern void ReassignOwnedObjects(ReassignOwnedStmt *stmt); #endif /* USER_H */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 327e4301ff9549be2b5fb96f68b5e2b4e76282e2..919753ea92fc9b269dca3934c12fb6ebd16aa814 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.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/nodes/nodes.h,v 1.176 2005/10/15 02:49:45 momjian Exp $ + * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.177 2005/11/21 12:49:32 alvherre Exp $ * *------------------------------------------------------------------------- */ @@ -286,6 +286,8 @@ typedef enum NodeTag T_DropTableSpaceStmt, T_AlterObjectSchemaStmt, T_AlterOwnerStmt, + T_DropOwnedStmt, + T_ReassignOwnedStmt, T_A_Expr = 800, T_ColumnRef, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 8baed3e4491e5d895076f487e9192a429df4bf22..a482abfb351e9c5d64605a34a9944e568e63117c 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.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/nodes/parsenodes.h,v 1.293 2005/11/19 17:39:45 adunstan Exp $ + * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.294 2005/11/21 12:49:32 alvherre Exp $ * *------------------------------------------------------------------------- */ @@ -1875,4 +1875,24 @@ typedef struct DeallocateStmt char *name; /* The name of the plan to remove */ } DeallocateStmt; +/* + * DROP OWNED statement + */ +typedef struct DropOwnedStmt +{ + NodeTag type; + List *roles; + DropBehavior behavior; +} DropOwnedStmt; + +/* + * REASSIGN OWNED statement + */ +typedef struct ReassignOwnedStmt +{ + NodeTag type; + List *roles; + char *newrole; +} ReassignOwnedStmt; + #endif /* PARSENODES_H */ diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h index 8699913eb95cf1a1e7be656e3ccd42846c36d4df..7b2122b9a9f1f47923265f1ac1a02f72579e99ee 100644 --- a/src/include/utils/acl.h +++ b/src/include/utils/acl.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/utils/acl.h,v 1.88 2005/11/18 02:38:24 tgl Exp $ + * $PostgreSQL: pgsql/src/include/utils/acl.h,v 1.89 2005/11/21 12:49:33 alvherre Exp $ * * NOTES * An ACL array is simply an array of AclItems, representing the union @@ -221,6 +221,9 @@ extern Datum hash_aclitem(PG_FUNCTION_ARGS); * prototypes for functions in aclchk.c */ extern void ExecuteGrantStmt(GrantStmt *stmt); +extern void ExecGrantStmt_oids(bool is_grant, GrantObjectType objtype, + List *objects, bool all_privs, AclMode privileges, + List *grantees, bool grant_option, DropBehavior behavior); extern AclMode pg_class_aclmask(Oid table_oid, Oid roleid, AclMode mask, AclMaskHow how); diff --git a/src/test/regress/expected/dependency.out b/src/test/regress/expected/dependency.out index 2c31e581bfe84353c63dc1dbf2ec2d0269992f06..4781b5f9f29841abe3b82f175ffb75244dce8d61 100644 --- a/src/test/regress/expected/dependency.out +++ b/src/test/regress/expected/dependency.out @@ -38,6 +38,76 @@ DROP USER regression_user2; ALTER TABLE deptest OWNER TO regression_user3; DROP USER regression_user3; ERROR: role "regression_user3" cannot be dropped because some objects depend on it +\set VERBOSITY default -- if we drop the object, we can drop the user too DROP TABLE deptest; DROP USER regression_user3; +-- Test DROP OWNED +CREATE USER regression_user0; +CREATE USER regression_user1; +CREATE USER regression_user2; +SET SESSION AUTHORIZATION regression_user0; +-- permission denied +DROP OWNED BY regression_user1; +ERROR: permission denied to drop objects +DROP OWNED BY regression_user0, regression_user2; +ERROR: permission denied to drop objects +REASSIGN OWNED BY regression_user0 TO regression_user1; +ERROR: permission denied to reassign objects +REASSIGN OWNED BY regression_user1 TO regression_user0; +ERROR: permission denied to reassign objects +-- this one is allowed +DROP OWNED BY regression_user0; +CREATE TABLE deptest1 (); +GRANT ALL ON deptest1 TO regression_user1 WITH GRANT OPTION; +SET SESSION AUTHORIZATION regression_user1; +CREATE TABLE deptest (a serial primary key, b text); +NOTICE: CREATE TABLE will create implicit sequence "deptest_a_seq" for serial column "deptest.a" +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "deptest_pkey" for table "deptest" +GRANT ALL ON deptest1 TO regression_user2; +RESET SESSION AUTHORIZATION; +\z deptest1 + Access privileges for database "regression" + Schema | Name | Type | Access privileges +--------+----------+-------+---------------------------------------------------------------------------------------------------------------------------------------- + public | deptest1 | table | {regression_user0=arwdRxt/regression_user0,regression_user1=a*r*w*d*R*x*t*/regression_user0,regression_user2=arwdRxt/regression_user1} +(1 row) + +DROP OWNED BY regression_user1; +-- all grants revoked +\z deptest1 + Access privileges for database "regression" + Schema | Name | Type | Access privileges +--------+----------+-------+--------------------------------------------- + public | deptest1 | table | {regression_user0=arwdRxt/regression_user0} +(1 row) + +-- table was dropped +\d deptest +-- Test REASSIGN OWNED +GRANT ALL ON deptest1 TO regression_user1; +SET SESSION AUTHORIZATION regression_user1; +CREATE TABLE deptest (a serial primary key, b text); +NOTICE: CREATE TABLE will create implicit sequence "deptest_a_seq" for serial column "deptest.a" +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "deptest_pkey" for table "deptest" +RESET SESSION AUTHORIZATION; +REASSIGN OWNED BY regression_user1 TO regression_user2; +\dt deptest + List of relations + Schema | Name | Type | Owner +--------+---------+-------+------------------ + public | deptest | table | regression_user2 +(1 row) + +-- doesn't work: grant still exists +DROP USER regression_user1; +ERROR: role "regression_user1" cannot be dropped because some objects depend on it +DETAIL: access to table deptest1 +DROP OWNED BY regression_user1; +DROP USER regression_user1; +\set VERBOSITY terse +DROP USER regression_user2; +ERROR: role "regression_user2" cannot be dropped because some objects depend on it +DROP OWNED BY regression_user2, regression_user0; +DROP USER regression_user2; +DROP USER regression_user0; diff --git a/src/test/regress/sql/dependency.sql b/src/test/regress/sql/dependency.sql index 3e4a232ea716654908b9e0d9eb1f50c250c92644..c1b189f527894d77e32631710ec9b9d01dc90a75 100644 --- a/src/test/regress/sql/dependency.sql +++ b/src/test/regress/sql/dependency.sql @@ -39,6 +39,55 @@ DROP USER regression_user2; ALTER TABLE deptest OWNER TO regression_user3; DROP USER regression_user3; +\set VERBOSITY default -- if we drop the object, we can drop the user too DROP TABLE deptest; DROP USER regression_user3; + +-- Test DROP OWNED +CREATE USER regression_user0; +CREATE USER regression_user1; +CREATE USER regression_user2; +SET SESSION AUTHORIZATION regression_user0; +-- permission denied +DROP OWNED BY regression_user1; +DROP OWNED BY regression_user0, regression_user2; +REASSIGN OWNED BY regression_user0 TO regression_user1; +REASSIGN OWNED BY regression_user1 TO regression_user0; +-- this one is allowed +DROP OWNED BY regression_user0; + +CREATE TABLE deptest1 (); +GRANT ALL ON deptest1 TO regression_user1 WITH GRANT OPTION; + +SET SESSION AUTHORIZATION regression_user1; +CREATE TABLE deptest (a serial primary key, b text); +GRANT ALL ON deptest1 TO regression_user2; +RESET SESSION AUTHORIZATION; +\z deptest1 + +DROP OWNED BY regression_user1; +-- all grants revoked +\z deptest1 +-- table was dropped +\d deptest + +-- Test REASSIGN OWNED +GRANT ALL ON deptest1 TO regression_user1; + +SET SESSION AUTHORIZATION regression_user1; +CREATE TABLE deptest (a serial primary key, b text); +RESET SESSION AUTHORIZATION; + +REASSIGN OWNED BY regression_user1 TO regression_user2; +\dt deptest +-- doesn't work: grant still exists +DROP USER regression_user1; +DROP OWNED BY regression_user1; +DROP USER regression_user1; + +\set VERBOSITY terse +DROP USER regression_user2; +DROP OWNED BY regression_user2, regression_user0; +DROP USER regression_user2; +DROP USER regression_user0;