Index: doc/src/sgml/reference.sgml =================================================================== RCS file: /home/alvherre/cvs/pgsql/doc/src/sgml/reference.sgml,v retrieving revision 1.56 diff -c -r1.56 reference.sgml *** doc/src/sgml/reference.sgml 29 Jul 2005 15:13:11 -0000 1.56 --- doc/src/sgml/reference.sgml 19 Nov 2005 21:30:50 -0000 *************** *** 102,107 **** --- 102,108 ---- &dropLanguage; &dropOperator; &dropOperatorClass; + &dropOwned; &dropRole; &dropRule; &dropSchema; *************** *** 125,130 **** --- 126,132 ---- ¬ify; &prepare; &prepareTransaction; + &reassignOwned; &reindex; &releaseSavepoint; &reset; Index: doc/src/sgml/ref/allfiles.sgml =================================================================== RCS file: /home/alvherre/cvs/pgsql/doc/src/sgml/ref/allfiles.sgml,v retrieving revision 1.66 diff -c -r1.66 allfiles.sgml *** doc/src/sgml/ref/allfiles.sgml 29 Jul 2005 15:13:11 -0000 1.66 --- doc/src/sgml/ref/allfiles.sgml 19 Nov 2005 21:30:50 -0000 *************** *** 70,75 **** --- 70,76 ---- + *************** *** 93,98 **** --- 94,100 ---- + Index: src/backend/catalog/aclchk.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/catalog/aclchk.c,v retrieving revision 1.120 diff -c -r1.120 aclchk.c *** src/backend/catalog/aclchk.c 15 Oct 2005 02:49:12 -0000 1.120 --- src/backend/catalog/aclchk.c 19 Nov 2005 21:36:24 -0000 *************** *** 17,22 **** --- 17,23 ---- */ #include "postgres.h" + #include "access/genam.h" #include "access/heapam.h" #include "catalog/catalog.h" #include "catalog/dependency.h" *************** *** 41,52 **** #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 AclMode string_to_privilege(const char *privname); static const char *privilege_to_string(AclMode privilege); --- 42,66 ---- #include "utils/syscache.h" ! 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,110 **** foreach(j, grantees) { ! PrivGrantee *grantee = (PrivGrantee *) lfirst(j); ! AclItem aclitem; Acl *newer_acl; ! if (grantee->rolname) ! aclitem. ai_grantee = get_roleid_checked(grantee->rolname); ! ! else ! aclitem. ai_grantee = ACL_ID_PUBLIC; /* * Grant options can only be granted to individual roles, not PUBLIC. --- 110,119 ---- foreach(j, grantees) { ! AclItem aclitem; Acl *newer_acl; ! aclitem.ai_grantee = lfirst_oid(j); /* * Grant options can only be granted to individual roles, not PUBLIC. *************** *** 151,221 **** void ExecuteGrantStmt(GrantStmt *stmt) { switch (stmt->objtype) { case ACL_OBJECT_RELATION: ! ExecuteGrantStmt_Relation(stmt); break; case ACL_OBJECT_DATABASE: ! ExecuteGrantStmt_Database(stmt); break; case ACL_OBJECT_FUNCTION: ! ExecuteGrantStmt_Function(stmt); break; case ACL_OBJECT_LANGUAGE: ! ExecuteGrantStmt_Language(stmt); break; case ACL_OBJECT_NAMESPACE: ! ExecuteGrantStmt_Namespace(stmt); break; case ACL_OBJECT_TABLESPACE: ! ExecuteGrantStmt_Tablespace(stmt); 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; } 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_RELATION)) ereport(ERROR, (errcode(ERRCODE_INVALID_GRANT_OPERATION), ! errmsg("invalid privilege type %s for table", privilege_to_string(priv)))); privileges |= priv; } } ! foreach(i, stmt->objects) { ! RangeVar *relvar = (RangeVar *) lfirst(i); ! Oid relOid; ! Relation relation; ! HeapTuple tuple; ! Form_pg_class pg_class_tuple; Datum aclDatum; bool isNull; AclMode avail_goptions; AclMode this_privileges; --- 160,466 ---- 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: ! all_privileges = ACL_ALL_RIGHTS_RELATION; ! errormsg = _("invalid privilege type %s for table"); break; case ACL_OBJECT_DATABASE: ! all_privileges = ACL_ALL_RIGHTS_DATABASE; ! errormsg = _("invalid privilege type %s for database"); break; case ACL_OBJECT_FUNCTION: ! all_privileges = ACL_ALL_RIGHTS_FUNCTION; ! errormsg = _("invalid privilege type %s for function"); break; case ACL_OBJECT_LANGUAGE: ! all_privileges = ACL_ALL_RIGHTS_LANGUAGE; ! errormsg = _("invalid privilege type %s for language"); break; case ACL_OBJECT_NAMESPACE: ! all_privileges = ACL_ALL_RIGHTS_NAMESPACE; ! errormsg = _("invalid privilege type %s for namespace"); break; case ACL_OBJECT_TABLESPACE: ! all_privileges = ACL_ALL_RIGHTS_TABLESPACE; ! errormsg = _("invalid privilege type %s for tablespace"); break; default: elog(ERROR, "unrecognized GrantStmt.objtype: %d", (int) stmt->objtype); } if (stmt->privileges == NIL) { all_privs = true; ! privileges = all_privileges; } else { all_privs = false; privileges = ACL_NO_RIGHTS; ! foreach(cell, stmt->privileges) { ! char *privname = strVal(lfirst(cell)); AclMode priv = string_to_privilege(privname); ! if (priv & ~((AclMode) all_privileges)) ereport(ERROR, (errcode(ERRCODE_INVALID_GRANT_OPERATION), ! errmsg(errormsg, privilege_to_string(priv)))); + privileges |= priv; } } ! /* 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) ! { ! 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,228 **** --- 468,474 ---- Acl *new_acl; Oid grantorId; Oid ownerId; + HeapTuple tuple; HeapTuple newtuple; Datum values[Natts_pg_class]; char nulls[Natts_pg_class]; *************** *** 232,240 **** Oid *oldmembers; Oid *newmembers; - /* open pg_class */ - relation = heap_open(RelationRelationId, RowExclusiveLock); - relOid = RangeVarGetRelid(relvar, false); tuple = SearchSysCache(RELOID, ObjectIdGetDatum(relOid), 0, 0, 0); --- 478,483 ---- *************** *** 247,261 **** ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is an index", ! relvar->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))); ! /* * Get owner ID and working copy of existing ACL. If there's no ACL, * substitute the proper default. --- 490,503 ---- ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is an index", ! 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", ! NameStr(pg_class_tuple->relname)))); /* * Get owner ID and working copy of existing ACL. If there's no ACL, * substitute the proper default. *************** *** 285,291 **** 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); } /* --- 527,533 ---- 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, ! NameStr(pg_class_tuple->relname)); } /* *************** *** 297,303 **** * GRANT case.) */ this_privileges = privileges & ACL_OPTION_TO_PRIVS(avail_goptions); ! if (stmt->is_grant) { if (this_privileges == 0) ereport(WARNING, --- 539,545 ---- * GRANT case.) */ this_privileges = privileges & ACL_OPTION_TO_PRIVS(avail_goptions); ! if (is_grant) { if (this_privileges == 0) ereport(WARNING, *************** *** 328,336 **** */ 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, grantorId, ownerId); nnewmembers = aclmembers(new_acl, &newmembers); --- 570,578 ---- */ noldmembers = aclmembers(old_acl, &oldmembers); ! new_acl = merge_acl_with_grant(old_acl, is_grant, ! grant_option, behavior, ! grantees, this_privileges, grantorId, ownerId); nnewmembers = aclmembers(new_acl, &newmembers); *************** *** 352,358 **** /* Update the shared dependency ACL info */ updateAclDependencies(RelationRelationId, relOid, ! ownerId, stmt->is_grant, noldmembers, oldmembers, nnewmembers, newmembers); --- 594,600 ---- /* Update the shared dependency ACL info */ updateAclDependencies(RelationRelationId, relOid, ! ownerId, is_grant, noldmembers, oldmembers, nnewmembers, newmembers); *************** *** 360,409 **** pfree(new_acl); - heap_close(relation, RowExclusiveLock); - /* prevent error when processing duplicate objects */ CommandCounterIncrement(); } } static void ! ExecuteGrantStmt_Database(GrantStmt *stmt) { ! AclMode privileges; ! bool all_privs; ! ListCell *i; ! if (stmt->privileges == NIL) ! { ! all_privs = true; 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; ! } ! } ! foreach(i, stmt->objects) { ! char *dbname = strVal(lfirst(i)); ! Relation relation; ! ScanKeyData entry[1]; ! HeapScanDesc scan; ! HeapTuple tuple; Form_pg_database pg_database_tuple; Datum aclDatum; bool isNull; --- 602,630 ---- pfree(new_acl); /* prevent error when processing duplicate objects */ CommandCounterIncrement(); } + + heap_close(relation, RowExclusiveLock); } static void ! ExecGrant_Database(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_DATABASE; ! relation = heap_open(DatabaseRelationId, RowExclusiveLock); ! foreach (cell, objects) { ! Oid datId = lfirst_oid(cell); Form_pg_database pg_database_tuple; Datum aclDatum; bool isNull; *************** *** 421,438 **** int nnewmembers; Oid *oldmembers; Oid *newmembers; ! relation = heap_open(DatabaseRelationId, RowExclusiveLock); 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))); pg_database_tuple = (Form_pg_database) GETSTRUCT(tuple); /* --- 642,664 ---- int nnewmembers; Oid *oldmembers; Oid *newmembers; + ScanKeyData entry[1]; + SysScanDesc scan; + HeapTuple tuple; ! /* There's no syscache for pg_database, so must look the hard way */ ScanKeyInit(&entry[0], ! ObjectIdAttributeNumber, ! BTEqualStrategyNumber, F_OIDEQ, ! ObjectIdGetDatum(datId)); ! scan = systable_beginscan(relation, DatabaseOidIndexId, true, ! SnapshotNow, 1, entry); ! ! tuple = systable_getnext(scan); ! if (!HeapTupleIsValid(tuple)) ! elog(ERROR, "could not find tuple for database %u", datId); ! pg_database_tuple = (Form_pg_database) GETSTRUCT(tuple); /* *************** *** 476,482 **** * GRANT case.) */ this_privileges = privileges & ACL_OPTION_TO_PRIVS(avail_goptions); ! if (stmt->is_grant) { if (this_privileges == 0) ereport(WARNING, --- 702,708 ---- * GRANT case.) */ this_privileges = privileges & ACL_OPTION_TO_PRIVS(avail_goptions); ! if (is_grant) { if (this_privileges == 0) ereport(WARNING, *************** *** 507,515 **** */ 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, grantorId, ownerId); nnewmembers = aclmembers(new_acl, &newmembers); --- 733,741 ---- */ noldmembers = aclmembers(old_acl, &oldmembers); ! new_acl = merge_acl_with_grant(old_acl, is_grant, ! grant_option, behavior, ! grantees, this_privileges, grantorId, ownerId); nnewmembers = aclmembers(new_acl, &newmembers); *************** *** 522,528 **** 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); simple_heap_update(relation, &newtuple->t_self, newtuple); --- 748,755 ---- 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); simple_heap_update(relation, &newtuple->t_self, newtuple); *************** *** 531,587 **** /* Update the shared dependency ACL info */ updateAclDependencies(DatabaseRelationId, HeapTupleGetOid(tuple), ! ownerId, stmt->is_grant, noldmembers, oldmembers, nnewmembers, newmembers); ! pfree(new_acl); ! ! heap_endscan(scan); ! heap_close(relation, RowExclusiveLock); /* prevent error when processing duplicate objects */ CommandCounterIncrement(); } } static void ! ExecuteGrantStmt_Function(GrantStmt *stmt) { ! AclMode privileges; ! bool all_privs; ! ListCell *i; ! if (stmt->privileges == NIL) ! { ! all_privs = true; 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; ! } ! } ! foreach(i, stmt->objects) { ! FuncWithArgs *func = (FuncWithArgs *) lfirst(i); ! Oid oid; ! Relation relation; ! HeapTuple tuple; Form_pg_proc pg_proc_tuple; Datum aclDatum; bool isNull; --- 758,794 ---- /* Update the shared dependency ACL info */ updateAclDependencies(DatabaseRelationId, HeapTupleGetOid(tuple), ! ownerId, is_grant, noldmembers, oldmembers, nnewmembers, newmembers); ! systable_endscan(scan); ! pfree(new_acl); /* prevent error when processing duplicate objects */ CommandCounterIncrement(); } + + heap_close(relation, RowExclusiveLock); } static void ! ExecGrant_Function(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_FUNCTION; ! relation = heap_open(ProcedureRelationId, RowExclusiveLock); ! foreach (cell, objects) { ! Oid funcId = lfirst_oid(cell); Form_pg_proc pg_proc_tuple; Datum aclDatum; bool isNull; *************** *** 591,596 **** --- 798,804 ---- Acl *new_acl; Oid grantorId; Oid ownerId; + HeapTuple tuple; HeapTuple newtuple; Datum values[Natts_pg_proc]; char nulls[Natts_pg_proc]; *************** *** 600,613 **** Oid *oldmembers; Oid *newmembers; - oid = LookupFuncNameTypeNames(func->funcname, func->funcargs, false); - - relation = heap_open(ProcedureRelationId, RowExclusiveLock); tuple = SearchSysCache(PROCOID, ! ObjectIdGetDatum(oid), 0, 0, 0); if (!HeapTupleIsValid(tuple)) ! elog(ERROR, "cache lookup failed for function %u", oid); pg_proc_tuple = (Form_pg_proc) GETSTRUCT(tuple); /* --- 808,819 ---- Oid *oldmembers; Oid *newmembers; tuple = SearchSysCache(PROCOID, ! ObjectIdGetDatum(funcId), 0, 0, 0); if (!HeapTupleIsValid(tuple)) ! elog(ERROR, "cache lookup failed for function %u", funcId); ! pg_proc_tuple = (Form_pg_proc) GETSTRUCT(tuple); /* *************** *** 634,640 **** */ if (avail_goptions == ACL_NO_RIGHTS) { ! if (pg_proc_aclmask(oid, grantorId, ACL_ALL_RIGHTS_FUNCTION | ACL_GRANT_OPTION_FOR(ACL_ALL_RIGHTS_FUNCTION), ACLMASK_ANY) == ACL_NO_RIGHTS) --- 840,846 ---- */ if (avail_goptions == ACL_NO_RIGHTS) { ! if (pg_proc_aclmask(funcId, grantorId, ACL_ALL_RIGHTS_FUNCTION | ACL_GRANT_OPTION_FOR(ACL_ALL_RIGHTS_FUNCTION), ACLMASK_ANY) == ACL_NO_RIGHTS) *************** *** 651,657 **** * GRANT case.) */ this_privileges = privileges & ACL_OPTION_TO_PRIVS(avail_goptions); ! if (stmt->is_grant) { if (this_privileges == 0) ereport(WARNING, --- 857,863 ---- * GRANT case.) */ this_privileges = privileges & ACL_OPTION_TO_PRIVS(avail_goptions); ! if (is_grant) { if (this_privileges == 0) ereport(WARNING, *************** *** 682,690 **** */ 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, grantorId, ownerId); nnewmembers = aclmembers(new_acl, &newmembers); --- 888,896 ---- */ noldmembers = aclmembers(old_acl, &oldmembers); ! new_acl = merge_acl_with_grant(old_acl, is_grant, ! grant_option, behavior, ! grantees, this_privileges, grantorId, ownerId); nnewmembers = aclmembers(new_acl, &newmembers); *************** *** 697,703 **** 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); simple_heap_update(relation, &newtuple->t_self, newtuple); --- 903,910 ---- 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); simple_heap_update(relation, &newtuple->t_self, newtuple); *************** *** 705,712 **** CatalogUpdateIndexes(relation, newtuple); /* Update the shared dependency ACL info */ ! updateAclDependencies(ProcedureRelationId, oid, ! ownerId, stmt->is_grant, noldmembers, oldmembers, nnewmembers, newmembers); --- 912,919 ---- CatalogUpdateIndexes(relation, newtuple); /* Update the shared dependency ACL info */ ! updateAclDependencies(ProcedureRelationId, funcId, ! ownerId, is_grant, noldmembers, oldmembers, nnewmembers, newmembers); *************** *** 714,761 **** pfree(new_acl); - heap_close(relation, RowExclusiveLock); - /* prevent error when processing duplicate objects */ CommandCounterIncrement(); } } static void ! ExecuteGrantStmt_Language(GrantStmt *stmt) { ! AclMode privileges; ! bool all_privs; ! ListCell *i; ! if (stmt->privileges == NIL) ! { ! all_privs = true; 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; ! } ! } ! foreach(i, stmt->objects) { ! char *langname = strVal(lfirst(i)); ! Relation relation; ! HeapTuple tuple; Form_pg_language pg_language_tuple; Datum aclDatum; bool isNull; --- 921,949 ---- pfree(new_acl); /* prevent error when processing duplicate objects */ CommandCounterIncrement(); } + + heap_close(relation, RowExclusiveLock); } static void ! ExecGrant_Language(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_LANGUAGE; ! relation = heap_open(LanguageRelationId, RowExclusiveLock); ! foreach (cell, objects) { ! Oid langid = lfirst_oid(cell); Form_pg_language pg_language_tuple; Datum aclDatum; bool isNull; *************** *** 765,770 **** --- 953,959 ---- Acl *new_acl; Oid grantorId; Oid ownerId; + HeapTuple tuple; HeapTuple newtuple; Datum values[Natts_pg_language]; char nulls[Natts_pg_language]; *************** *** 774,794 **** Oid *oldmembers; Oid *newmembers; ! relation = heap_open(LanguageRelationId, RowExclusiveLock); ! tuple = SearchSysCache(LANGNAME, ! PointerGetDatum(langname), 0, 0, 0); if (!HeapTupleIsValid(tuple)) ! ereport(ERROR, ! (errcode(ERRCODE_UNDEFINED_OBJECT), ! errmsg("language \"%s\" does not exist", langname))); 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."))); /* * Get owner ID and working copy of existing ACL. If there's no ACL, --- 963,982 ---- Oid *oldmembers; Oid *newmembers; ! tuple = SearchSysCache(LANGOID, ! ObjectIdGetDatum(langid), 0, 0, 0); if (!HeapTupleIsValid(tuple)) ! 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", ! 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,840 **** * GRANT case.) */ this_privileges = privileges & ACL_OPTION_TO_PRIVS(avail_goptions); ! if (stmt->is_grant) { if (this_privileges == 0) ereport(WARNING, --- 1022,1028 ---- * GRANT case.) */ this_privileges = privileges & ACL_OPTION_TO_PRIVS(avail_goptions); ! if (is_grant) { if (this_privileges == 0) ereport(WARNING, *************** *** 865,873 **** */ 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, grantorId, ownerId); nnewmembers = aclmembers(new_acl, &newmembers); --- 1053,1061 ---- */ noldmembers = aclmembers(old_acl, &oldmembers); ! new_acl = merge_acl_with_grant(old_acl, is_grant, ! grant_option, behavior, ! grantees, this_privileges, grantorId, ownerId); nnewmembers = aclmembers(new_acl, &newmembers); *************** *** 880,886 **** 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); simple_heap_update(relation, &newtuple->t_self, newtuple); --- 1068,1075 ---- 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); simple_heap_update(relation, &newtuple->t_self, newtuple); *************** *** 889,895 **** /* Update the shared dependency ACL info */ updateAclDependencies(LanguageRelationId, HeapTupleGetOid(tuple), ! ownerId, stmt->is_grant, noldmembers, oldmembers, nnewmembers, newmembers); --- 1078,1084 ---- /* Update the shared dependency ACL info */ updateAclDependencies(LanguageRelationId, HeapTupleGetOid(tuple), ! ownerId, is_grant, noldmembers, oldmembers, nnewmembers, newmembers); *************** *** 897,944 **** pfree(new_acl); - heap_close(relation, RowExclusiveLock); - /* prevent error when processing duplicate objects */ CommandCounterIncrement(); } } static void ! ExecuteGrantStmt_Namespace(GrantStmt *stmt) { ! AclMode privileges; ! bool all_privs; ! ListCell *i; ! if (stmt->privileges == NIL) ! { ! all_privs = true; 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; ! } ! } ! foreach(i, stmt->objects) { ! char *nspname = strVal(lfirst(i)); ! Relation relation; ! HeapTuple tuple; Form_pg_namespace pg_namespace_tuple; Datum aclDatum; bool isNull; --- 1086,1114 ---- pfree(new_acl); /* prevent error when processing duplicate objects */ CommandCounterIncrement(); } + + heap_close(relation, RowExclusiveLock); } static void ! ExecGrant_Namespace(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_NAMESPACE; ! relation = heap_open(NamespaceRelationId, RowExclusiveLock); ! foreach(cell, objects) { ! Oid nspid = lfirst_oid(cell); Form_pg_namespace pg_namespace_tuple; Datum aclDatum; bool isNull; *************** *** 948,953 **** --- 1118,1124 ---- Acl *new_acl; Oid grantorId; Oid ownerId; + HeapTuple tuple; HeapTuple newtuple; Datum values[Natts_pg_namespace]; char nulls[Natts_pg_namespace]; *************** *** 957,970 **** Oid *oldmembers; Oid *newmembers; ! relation = heap_open(NamespaceRelationId, RowExclusiveLock); ! tuple = SearchSysCache(NAMESPACENAME, ! CStringGetDatum(nspname), 0, 0, 0); if (!HeapTupleIsValid(tuple)) ! ereport(ERROR, ! (errcode(ERRCODE_UNDEFINED_SCHEMA), ! errmsg("schema \"%s\" does not exist", nspname))); pg_namespace_tuple = (Form_pg_namespace) GETSTRUCT(tuple); /* --- 1128,1139 ---- Oid *oldmembers; Oid *newmembers; ! tuple = SearchSysCache(NAMESPACEOID, ! ObjectIdGetDatum(nspid), 0, 0, 0); if (!HeapTupleIsValid(tuple)) ! elog(ERROR, "cache lookup failed for namespace %u", nspid); ! pg_namespace_tuple = (Form_pg_namespace) GETSTRUCT(tuple); /* *************** *** 997,1003 **** 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); } /* --- 1166,1172 ---- 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, ! NameStr(pg_namespace_tuple->nspname)); } /* *************** *** 1009,1015 **** * GRANT case.) */ this_privileges = privileges & ACL_OPTION_TO_PRIVS(avail_goptions); ! if (stmt->is_grant) { if (this_privileges == 0) ereport(WARNING, --- 1178,1184 ---- * GRANT case.) */ this_privileges = privileges & ACL_OPTION_TO_PRIVS(avail_goptions); ! if (is_grant) { if (this_privileges == 0) ereport(WARNING, *************** *** 1040,1048 **** */ 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, grantorId, ownerId); nnewmembers = aclmembers(new_acl, &newmembers); --- 1209,1217 ---- */ noldmembers = aclmembers(old_acl, &oldmembers); ! new_acl = merge_acl_with_grant(old_acl, is_grant, ! grant_option, behavior, ! grantees, this_privileges, grantorId, ownerId); nnewmembers = aclmembers(new_acl, &newmembers); *************** *** 1055,1061 **** 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); simple_heap_update(relation, &newtuple->t_self, newtuple); --- 1224,1231 ---- 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); simple_heap_update(relation, &newtuple->t_self, newtuple); *************** *** 1064,1070 **** /* Update the shared dependency ACL info */ updateAclDependencies(NamespaceRelationId, HeapTupleGetOid(tuple), ! ownerId, stmt->is_grant, noldmembers, oldmembers, nnewmembers, newmembers); --- 1234,1240 ---- /* Update the shared dependency ACL info */ updateAclDependencies(NamespaceRelationId, HeapTupleGetOid(tuple), ! ownerId, is_grant, noldmembers, oldmembers, nnewmembers, newmembers); *************** *** 1072,1121 **** pfree(new_acl); - heap_close(relation, RowExclusiveLock); - /* prevent error when processing duplicate objects */ CommandCounterIncrement(); } } static void ! ExecuteGrantStmt_Tablespace(GrantStmt *stmt) { ! AclMode privileges; ! bool all_privs; ! ListCell *i; ! if (stmt->privileges == NIL) ! { ! all_privs = true; 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; ! } ! } ! foreach(i, stmt->objects) { ! char *spcname = strVal(lfirst(i)); ! Relation relation; ! ScanKeyData entry[1]; ! HeapScanDesc scan; ! HeapTuple tuple; Form_pg_tablespace pg_tablespace_tuple; Datum aclDatum; bool isNull; --- 1242,1270 ---- pfree(new_acl); /* prevent error when processing duplicate objects */ CommandCounterIncrement(); } + + heap_close(relation, RowExclusiveLock); } static void ! ExecGrant_Tablespace(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_TABLESPACE; ! relation = heap_open(TableSpaceRelationId, RowExclusiveLock); ! foreach(cell, objects) { ! Oid tblId = lfirst_oid(cell); Form_pg_tablespace pg_tablespace_tuple; Datum aclDatum; bool isNull; *************** *** 1133,1150 **** int nnewmembers; Oid *oldmembers; Oid *newmembers; ! relation = heap_open(TableSpaceRelationId, RowExclusiveLock); 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))); pg_tablespace_tuple = (Form_pg_tablespace) GETSTRUCT(tuple); /* --- 1282,1302 ---- int nnewmembers; Oid *oldmembers; Oid *newmembers; + ScanKeyData entry[1]; + SysScanDesc scan; + HeapTuple tuple; ! /* There's no syscache for pg_tablespace, so must look the hard way */ ScanKeyInit(&entry[0], ! ObjectIdAttributeNumber, ! BTEqualStrategyNumber, F_OIDEQ, ! ObjectIdGetDatum(tblId)); ! scan = systable_beginscan(relation, TablespaceOidIndexId, true, ! SnapshotNow, 1, entry); ! tuple = systable_getnext(scan); if (!HeapTupleIsValid(tuple)) ! elog(ERROR, "cache lookup failed for tablespace %u", tblId); ! pg_tablespace_tuple = (Form_pg_tablespace) GETSTRUCT(tuple); /* *************** *** 1176,1182 **** 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); } /* --- 1328,1334 ---- 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, ! NameStr(pg_tablespace_tuple->spcname)); } /* *************** *** 1188,1194 **** * GRANT case.) */ this_privileges = privileges & ACL_OPTION_TO_PRIVS(avail_goptions); ! if (stmt->is_grant) { if (this_privileges == 0) ereport(WARNING, --- 1340,1346 ---- * GRANT case.) */ this_privileges = privileges & ACL_OPTION_TO_PRIVS(avail_goptions); ! if (is_grant) { if (this_privileges == 0) ereport(WARNING, *************** *** 1219,1227 **** */ 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, grantorId, ownerId); nnewmembers = aclmembers(new_acl, &newmembers); --- 1371,1379 ---- */ noldmembers = aclmembers(old_acl, &oldmembers); ! new_acl = merge_acl_with_grant(old_acl, is_grant, ! grant_option, behavior, ! grantees, this_privileges, grantorId, ownerId); nnewmembers = aclmembers(new_acl, &newmembers); *************** *** 1234,1240 **** 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); simple_heap_update(relation, &newtuple->t_self, newtuple); --- 1386,1393 ---- 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); simple_heap_update(relation, &newtuple->t_self, newtuple); *************** *** 1242,1260 **** CatalogUpdateIndexes(relation, newtuple); /* Update the shared dependency ACL info */ ! updateAclDependencies(TableSpaceRelationId, HeapTupleGetOid(tuple), ! ownerId, stmt->is_grant, noldmembers, oldmembers, nnewmembers, newmembers); ! pfree(new_acl); ! heap_endscan(scan); ! heap_close(relation, RowExclusiveLock); /* prevent error when processing duplicate objects */ CommandCounterIncrement(); } } --- 1395,1414 ---- CatalogUpdateIndexes(relation, newtuple); /* Update the shared dependency ACL info */ ! updateAclDependencies(TableSpaceRelationId, tblId, ! ownerId, is_grant, noldmembers, oldmembers, nnewmembers, newmembers); ! systable_endscan(scan); ! pfree(new_acl); /* prevent error when processing duplicate objects */ CommandCounterIncrement(); } + + heap_close(relation, RowExclusiveLock); } *************** *** 1537,1543 **** AclMode result; Relation pg_database; ScanKeyData entry[1]; ! HeapScanDesc scan; HeapTuple tuple; Datum aclDatum; bool isNull; --- 1691,1697 ---- AclMode result; Relation pg_database; ScanKeyData entry[1]; ! SysScanDesc scan; HeapTuple tuple; Datum aclDatum; bool isNull; *************** *** 1558,1565 **** ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(db_oid)); ! scan = heap_beginscan(pg_database, SnapshotNow, 1, entry); ! tuple = heap_getnext(scan, ForwardScanDirection); if (!HeapTupleIsValid(tuple)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_DATABASE), --- 1712,1720 ---- ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(db_oid)); ! scan = systable_beginscan(pg_database, DatabaseOidIndexId, true, ! SnapshotNow, 1, entry); ! tuple = systable_getnext(scan); if (!HeapTupleIsValid(tuple)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_DATABASE), *************** *** 1588,1594 **** if (acl && (Pointer) acl != DatumGetPointer(aclDatum)) pfree(acl); ! heap_endscan(scan); heap_close(pg_database, AccessShareLock); return result; --- 1743,1749 ---- if (acl && (Pointer) acl != DatumGetPointer(aclDatum)) pfree(acl); ! systable_endscan(scan); heap_close(pg_database, AccessShareLock); return result; *************** *** 1801,1807 **** AclMode result; Relation pg_tablespace; ScanKeyData entry[1]; ! HeapScanDesc scan; HeapTuple tuple; Datum aclDatum; bool isNull; --- 1956,1962 ---- AclMode result; Relation pg_tablespace; ScanKeyData entry[1]; ! SysScanDesc scan; HeapTuple tuple; Datum aclDatum; bool isNull; *************** *** 1829,1836 **** ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(spc_oid)); ! scan = heap_beginscan(pg_tablespace, SnapshotNow, 1, entry); ! tuple = heap_getnext(scan, ForwardScanDirection); if (!HeapTupleIsValid(tuple)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), --- 1984,1992 ---- ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(spc_oid)); ! scan = systable_beginscan(pg_tablespace, TablespaceOidIndexId, true, ! SnapshotNow, 1, entry); ! tuple = systable_getnext(scan); if (!HeapTupleIsValid(tuple)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), *************** *** 1859,1865 **** if (acl && (Pointer) acl != DatumGetPointer(aclDatum)) pfree(acl); ! heap_endscan(scan); heap_close(pg_tablespace, AccessShareLock); return result; --- 2015,2021 ---- if (acl && (Pointer) acl != DatumGetPointer(aclDatum)) pfree(acl); ! systable_endscan(scan); heap_close(pg_tablespace, AccessShareLock); return result; *************** *** 2091,2097 **** { Relation pg_tablespace; ScanKeyData entry[1]; ! HeapScanDesc scan; HeapTuple spctuple; Oid spcowner; --- 2247,2253 ---- { Relation pg_tablespace; ScanKeyData entry[1]; ! SysScanDesc scan; HeapTuple spctuple; Oid spcowner; *************** *** 2105,2113 **** ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(spc_oid)); ! scan = heap_beginscan(pg_tablespace, SnapshotNow, 1, entry); ! spctuple = heap_getnext(scan, ForwardScanDirection); if (!HeapTupleIsValid(spctuple)) ereport(ERROR, --- 2261,2270 ---- ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(spc_oid)); ! scan = systable_beginscan(pg_tablespace, TablespaceOidIndexId, true, ! SnapshotNow, 1, entry); ! spctuple = systable_getnext(scan); if (!HeapTupleIsValid(spctuple)) ereport(ERROR, *************** *** 2116,2122 **** spcowner = ((Form_pg_tablespace) GETSTRUCT(spctuple))->spcowner; ! heap_endscan(scan); heap_close(pg_tablespace, AccessShareLock); return has_privs_of_role(roleid, spcowner); --- 2273,2279 ---- spcowner = ((Form_pg_tablespace) GETSTRUCT(spctuple))->spcowner; ! systable_endscan(scan); heap_close(pg_tablespace, AccessShareLock); return has_privs_of_role(roleid, spcowner); *************** *** 2159,2165 **** { Relation pg_database; ScanKeyData entry[1]; ! HeapScanDesc scan; HeapTuple dbtuple; Oid dba; --- 2316,2322 ---- { Relation pg_database; ScanKeyData entry[1]; ! SysScanDesc scan; HeapTuple dbtuple; Oid dba; *************** *** 2173,2181 **** ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(db_oid)); ! scan = heap_beginscan(pg_database, SnapshotNow, 1, entry); ! dbtuple = heap_getnext(scan, ForwardScanDirection); if (!HeapTupleIsValid(dbtuple)) ereport(ERROR, --- 2330,2339 ---- ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(db_oid)); ! scan = systable_beginscan(pg_database, DatabaseOidIndexId, true, ! SnapshotNow, 1, entry); ! dbtuple = systable_getnext(scan); if (!HeapTupleIsValid(dbtuple)) ereport(ERROR, *************** *** 2184,2190 **** dba = ((Form_pg_database) GETSTRUCT(dbtuple))->datdba; ! heap_endscan(scan); heap_close(pg_database, AccessShareLock); return has_privs_of_role(roleid, dba); --- 2342,2348 ---- dba = ((Form_pg_database) GETSTRUCT(dbtuple))->datdba; ! systable_endscan(scan); heap_close(pg_database, AccessShareLock); return has_privs_of_role(roleid, dba); Index: src/backend/catalog/pg_depend.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/catalog/pg_depend.c,v retrieving revision 1.15 diff -c -r1.15 pg_depend.c *** src/backend/catalog/pg_depend.c 15 Oct 2005 02:49:14 -0000 1.15 --- src/backend/catalog/pg_depend.c 19 Nov 2005 21:38:13 -0000 *************** *** 163,168 **** --- 163,220 ---- } /* + * 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 * * classId/objectId specify the referencing object. Index: src/backend/catalog/pg_shdepend.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/catalog/pg_shdepend.c,v retrieving revision 1.3 diff -c -r1.3 pg_shdepend.c *** src/backend/catalog/pg_shdepend.c 15 Oct 2005 02:49:14 -0000 1.3 --- src/backend/catalog/pg_shdepend.c 19 Nov 2005 21:30:50 -0000 *************** *** 16,26 **** --- 16,39 ---- #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,1044 **** --- 1055,1305 ---- 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); + } Index: src/backend/commands/conversioncmds.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/conversioncmds.c,v retrieving revision 1.23 diff -c -r1.23 conversioncmds.c *** src/backend/commands/conversioncmds.c 15 Oct 2005 02:49:15 -0000 1.23 --- src/backend/commands/conversioncmds.c 19 Nov 2005 21:53:49 -0000 *************** *** 30,35 **** --- 30,37 ---- #include "utils/lsyscache.h" #include "utils/syscache.h" + static void AlterConversionOwner_internal(Relation rel, Oid conversionOid, + Oid newOwnerId); /* * CREATE CONVERSION *************** *** 172,187 **** } /* ! * Change conversion owner */ void AlterConversionOwner(List *name, Oid newOwnerId) { Oid conversionOid; - HeapTuple tup; Relation rel; - Form_pg_conversion convForm; - AclResult aclresult; rel = heap_open(ConversionRelationId, RowExclusiveLock); --- 174,186 ---- } /* ! * Change conversion owner, by name */ void AlterConversionOwner(List *name, Oid newOwnerId) { Oid conversionOid; Relation rel; rel = heap_open(ConversionRelationId, RowExclusiveLock); *************** *** 192,197 **** --- 191,230 ---- 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); *************** *** 206,218 **** */ if (convForm->conowner != newOwnerId) { /* 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)); /* Must be able to become new owner */ check_is_member_of_role(GetUserId(), newOwnerId); --- 239,253 ---- */ 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, ! NameStr(convForm->conname)); /* Must be able to become new owner */ check_is_member_of_role(GetUserId(), newOwnerId); *************** *** 240,245 **** newOwnerId); } - heap_close(rel, NoLock); heap_freetuple(tup); } --- 275,279 ---- Index: src/backend/commands/functioncmds.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/functioncmds.c,v retrieving revision 1.69 diff -c -r1.69 functioncmds.c *** src/backend/commands/functioncmds.c 15 Oct 2005 02:49:15 -0000 1.69 --- src/backend/commands/functioncmds.c 19 Nov 2005 21:30:50 -0000 *************** *** 55,60 **** --- 55,61 ---- #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,868 **** } /* ! * Change function owner */ void AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId) { Oid procOid; HeapTuple tup; - Form_pg_proc procForm; - Relation rel; - AclResult aclresult; rel = heap_open(ProcedureRelationId, RowExclusiveLock); --- 854,867 ---- } /* ! * Change function owner by name and args */ void AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId) { + Relation rel; Oid procOid; HeapTuple tup; rel = heap_open(ProcedureRelationId, RowExclusiveLock); *************** *** 873,887 **** 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) 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."))); /* * If the new owner is the same as the existing owner, consider the * command to have succeeded. This is for dump restoration purposes. --- 872,924 ---- 0, 0, 0); if (!HeapTupleIsValid(tup)) /* should not happen */ elog(ERROR, "cache lookup failed for function %u", procOid); ! 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,908 **** /* Otherwise, must be owner of the existing object */ if (!pg_proc_ownercheck(procOid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, ! NameListToString(name)); /* Must be able to become new owner */ check_is_member_of_role(GetUserId(), newOwnerId); --- 939,945 ---- /* Otherwise, must be owner of the existing object */ if (!pg_proc_ownercheck(procOid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, ! NameStr(procForm->proname)); /* Must be able to become new owner */ check_is_member_of_role(GetUserId(), newOwnerId); *************** *** 937,943 **** repl_val[Anum_pg_proc_proacl - 1] = PointerGetDatum(newAcl); } ! newtuple = heap_modifytuple(tup, RelationGetDescr(rel), repl_val, repl_null, repl_repl); simple_heap_update(rel, &newtuple->t_self, newtuple); CatalogUpdateIndexes(rel, newtuple); --- 974,981 ---- repl_val[Anum_pg_proc_proacl - 1] = PointerGetDatum(newAcl); } ! newtuple = heap_modifytuple(tup, RelationGetDescr(rel), repl_val, ! repl_null, repl_repl); simple_heap_update(rel, &newtuple->t_self, newtuple); CatalogUpdateIndexes(rel, newtuple); *************** *** 949,955 **** } ReleaseSysCache(tup); - heap_close(rel, NoLock); } /* --- 987,992 ---- Index: src/backend/commands/opclasscmds.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/opclasscmds.c,v retrieving revision 1.38 diff -c -r1.38 opclasscmds.c *** src/backend/commands/opclasscmds.c 15 Oct 2005 02:49:15 -0000 1.38 --- src/backend/commands/opclasscmds.c 19 Nov 2005 21:47:10 -0000 *************** *** 58,63 **** --- 58,65 ---- 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,898 **** } /* ! * Change opclass owner */ 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; amOid = GetSysCacheOid(AMNAME, CStringGetDatum(access_method), --- 881,919 ---- } /* ! * 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 amOid; Relation rel; ! HeapTuple tup; ! char *opcname; ! char *schemaname; amOid = GetSysCacheOid(AMNAME, CStringGetDatum(access_method), *************** *** 912,917 **** --- 933,940 ---- if (schemaname) { + Oid namespaceOid; + namespaceOid = LookupExplicitNamespace(schemaname); tup = SearchSysCacheCopy(CLAAMNAMENSP, *************** *** 924,934 **** (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("operator class \"%s\" does not exist for access method \"%s\"", opcname, access_method))); - - opcOid = HeapTupleGetOid(tup); } else { opcOid = OpclassnameGetOpcid(amOid, opcname); if (!OidIsValid(opcOid)) ereport(ERROR, --- 947,957 ---- (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("operator class \"%s\" does not exist for access method \"%s\"", opcname, access_method))); } else { + Oid opcOid; + opcOid = OpclassnameGetOpcid(amOid, opcname); if (!OidIsValid(opcOid)) ereport(ERROR, *************** *** 941,950 **** 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; } opcForm = (Form_pg_opclass) GETSTRUCT(tup); /* * If the new owner is the same as the existing owner, consider the * command to have succeeded. This is for dump restoration purposes. --- 964,995 ---- 0, 0, 0); if (!HeapTupleIsValid(tup)) /* should not happen */ elog(ERROR, "cache lookup failed for opclass %u", opcOid); } + + 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,963 **** /* 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)); /* Must be able to become new owner */ check_is_member_of_role(GetUserId(), newOwnerId); --- 1002,1008 ---- /* Otherwise, must be owner of the existing object */ if (!pg_opclass_ownercheck(HeapTupleGetOid(tup), GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPCLASS, ! NameStr(opcForm->opcname)); /* Must be able to become new owner */ check_is_member_of_role(GetUserId(), newOwnerId); *************** *** 980,988 **** CatalogUpdateIndexes(rel, tup); /* Update owner dependency reference */ ! changeDependencyOnOwner(OperatorClassRelationId, opcOid, newOwnerId); } - - heap_close(rel, NoLock); - heap_freetuple(tup); } --- 1025,1031 ---- CatalogUpdateIndexes(rel, tup); /* Update owner dependency reference */ ! changeDependencyOnOwner(OperatorClassRelationId, HeapTupleGetOid(tup), ! newOwnerId); } } Index: src/backend/commands/operatorcmds.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/operatorcmds.c,v retrieving revision 1.26 diff -c -r1.26 operatorcmds.c *** src/backend/commands/operatorcmds.c 15 Oct 2005 02:49:15 -0000 1.26 --- src/backend/commands/operatorcmds.c 19 Nov 2005 21:52:12 -0000 *************** *** 48,53 **** --- 48,55 ---- #include "utils/syscache.h" + static void AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId); + /* * DefineOperator * this function extracts all the information from the *************** *** 260,265 **** --- 262,279 ---- 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,283 **** 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); tup = SearchSysCacheCopy(OPEROID, ObjectIdGetDatum(operOid), 0, 0, 0); --- 282,308 ---- Oid newOwnerId) { Oid operOid; Relation rel; 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,304 **** /* Otherwise, must be owner of the existing object */ if (!pg_oper_ownercheck(operOid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER, ! NameListToString(name)); /* Must be able to become new owner */ check_is_member_of_role(GetUserId(), newOwnerId); --- 323,329 ---- /* Otherwise, must be owner of the existing object */ if (!pg_oper_ownercheck(operOid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER, ! NameStr(oprForm->oprname)); /* Must be able to become new owner */ check_is_member_of_role(GetUserId(), newOwnerId); *************** *** 325,331 **** changeDependencyOnOwner(OperatorRelationId, operOid, newOwnerId); } - heap_close(rel, NoLock); heap_freetuple(tup); - } --- 350,354 ---- Index: src/backend/commands/schemacmds.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/schemacmds.c,v retrieving revision 1.35 diff -c -r1.35 schemacmds.c *** src/backend/commands/schemacmds.c 15 Oct 2005 02:49:15 -0000 1.35 --- src/backend/commands/schemacmds.c 19 Nov 2005 21:49:42 -0000 *************** *** 31,36 **** --- 31,38 ---- #include "utils/syscache.h" + static void AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId); + /* * CREATE SCHEMA */ *************** *** 264,269 **** --- 266,293 ---- 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 */ *************** *** 272,278 **** { HeapTuple tup; Relation rel; - Form_pg_namespace nspForm; rel = heap_open(NamespaceRelationId, RowExclusiveLock); --- 296,301 ---- *************** *** 283,288 **** --- 306,327 ---- 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); /* *************** *** 303,309 **** /* Otherwise, must be owner of the existing object */ if (!pg_namespace_ownercheck(HeapTupleGetOid(tup), GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE, ! name); /* Must be able to become new owner */ check_is_member_of_role(GetUserId(), newOwnerId); --- 342,348 ---- /* Otherwise, must be owner of the existing object */ if (!pg_namespace_ownercheck(HeapTupleGetOid(tup), GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE, ! NameStr(nspForm->nspname)); /* Must be able to become new owner */ check_is_member_of_role(GetUserId(), newOwnerId); *************** *** 356,361 **** newOwnerId); } - ReleaseSysCache(tup); - heap_close(rel, NoLock); } --- 395,398 ---- Index: src/backend/commands/tablecmds.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/tablecmds.c,v retrieving revision 1.174 diff -c -r1.174 tablecmds.c *** src/backend/commands/tablecmds.c 15 Oct 2005 02:49:15 -0000 1.174 --- src/backend/commands/tablecmds.c 19 Nov 2005 21:30:50 -0000 *************** *** 236,242 **** 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); --- 236,241 ---- *************** *** 5264,5270 **** * checks (this is necessary not just an optimization, else we'd fail to * handle toast tables properly). */ ! static void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing) { Relation target_rel; --- 5263,5269 ---- * checks (this is necessary not just an optimization, else we'd fail to * handle toast tables properly). */ ! void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing) { Relation target_rel; Index: src/backend/commands/typecmds.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/typecmds.c,v retrieving revision 1.82 diff -c -r1.82 typecmds.c *** src/backend/commands/typecmds.c 18 Oct 2005 01:06:24 -0000 1.82 --- src/backend/commands/typecmds.c 19 Nov 2005 21:30:50 -0000 *************** *** 2096,2102 **** * AlterTypeOwnerInternal - change type owner unconditionally * * This is currently only used to propagate ALTER TABLE OWNER to the ! * table's rowtype. It assumes the caller has done all needed checks. */ void AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId) --- 2096,2103 ---- * AlterTypeOwnerInternal - change type owner unconditionally * * This is currently only used to propagate ALTER TABLE OWNER to the ! * table's rowtype, and to implement REASSIGN OWNED BY. It assumes the ! * caller has done all needed checks. */ void AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId) Index: src/backend/commands/user.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/user.c,v retrieving revision 1.164 diff -c -r1.164 user.c *** src/backend/commands/user.c 4 Nov 2005 17:25:15 -0000 1.164 --- src/backend/commands/user.c 19 Nov 2005 21:30:50 -0000 *************** *** 1119,1124 **** --- 1119,1185 ---- } /* + * 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 * * Given a list of role names (as String nodes), generate a list of role OIDs Index: src/backend/nodes/copyfuncs.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/nodes/copyfuncs.c,v retrieving revision 1.317 diff -c -r1.317 copyfuncs.c *** src/backend/nodes/copyfuncs.c 14 Nov 2005 23:54:12 -0000 1.317 --- src/backend/nodes/copyfuncs.c 19 Nov 2005 21:30:50 -0000 *************** *** 2594,2599 **** --- 2594,2620 ---- 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 *************** *** 3145,3150 **** --- 3166,3177 ---- 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); Index: src/backend/nodes/equalfuncs.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/nodes/equalfuncs.c,v retrieving revision 1.254 diff -c -r1.254 equalfuncs.c *** src/backend/nodes/equalfuncs.c 14 Nov 2005 23:54:15 -0000 1.254 --- src/backend/nodes/equalfuncs.c 19 Nov 2005 21:30:50 -0000 *************** *** 1468,1477 **** return true; } ! /* ! * stuff from parsenodes.h ! */ static bool _equalAExpr(A_Expr *a, A_Expr *b) --- 1468,1490 ---- return true; } + static bool + _equalDropOwnedStmt(DropOwnedStmt *a, DropOwnedStmt *b) + { + COMPARE_NODE_FIELD(roles); + COMPARE_SCALAR_FIELD(behavior); ! 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) *************** *** 2187,2192 **** --- 2200,2212 ---- 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); Index: src/backend/parser/gram.y =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/parser/gram.y,v retrieving revision 2.512 diff -c -r2.512 gram.y *** src/backend/parser/gram.y 13 Nov 2005 19:11:28 -0000 2.512 --- src/backend/parser/gram.y 19 Nov 2005 21:30:50 -0000 *************** *** 153,158 **** --- 153,159 ---- VariableResetStmt VariableSetStmt VariableShowStmt ViewStmt CheckPointStmt CreateConversionStmt DeallocateStmt PrepareStmt ExecuteStmt + DropOwnedStmt ReassignOwnedStmt %type select_no_parens select_with_parens select_clause simple_select *************** *** 382,388 **** 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 PARTIAL PASSWORD PLACING POSITION PRECISION PRESERVE PREPARE PREPARED PRIMARY --- 383,389 ---- 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 OWNED OWNER PARTIAL PASSWORD PLACING POSITION PRECISION PRESERVE PREPARE PREPARED PRIMARY *************** *** 390,396 **** QUOTE ! READ REAL RECHECK REFERENCES REINDEX RELATIVE_P RELEASE RENAME REPEATABLE REPLACE RESET RESTART RESTRICT RETURNS REVOKE RIGHT ROLE ROLLBACK ROW ROWS RULE --- 391,397 ---- QUOTE ! READ REAL REASSIGN RECHECK REFERENCES REINDEX RELATIVE_P RELEASE RENAME REPEATABLE REPLACE RESET RESTART RESTRICT RETURNS REVOKE RIGHT ROLE ROLLBACK ROW ROWS RULE *************** *** 533,538 **** --- 534,540 ---- | DropCastStmt | DropGroupStmt | DropOpClassStmt + | DropOwnedStmt | DropPLangStmt | DropRuleStmt | DropStmt *************** *** 553,558 **** --- 555,561 ---- | LockStmt | NotifyStmt | PrepareStmt + | ReassignOwnedStmt | ReindexStmt | RemoveAggrStmt | RemoveFuncStmt *************** *** 2813,2818 **** --- 2816,2848 ---- } ; + /***************************************************************************** + * + * 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; + } + ; /***************************************************************************** * *************** *** 8209,8214 **** --- 8239,8245 ---- | OIDS | OPERATOR | OPTION + | OWNED | OWNER | PARTIAL | PASSWORD *************** *** 8221,8226 **** --- 8252,8258 ---- | PROCEDURE | QUOTE | READ + | REASSIGN | RECHECK | REINDEX | RELATIVE_P Index: src/backend/parser/keywords.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/parser/keywords.c,v retrieving revision 1.166 diff -c -r1.166 keywords.c *** src/backend/parser/keywords.c 15 Oct 2005 02:49:22 -0000 1.166 --- src/backend/parser/keywords.c 19 Nov 2005 21:30:50 -0000 *************** *** 251,256 **** --- 251,257 ---- {"outer", OUTER_P}, {"overlaps", OVERLAPS}, {"overlay", OVERLAY}, + {"owned", OWNED}, {"owner", OWNER}, {"partial", PARTIAL}, {"password", PASSWORD}, *************** *** 268,273 **** --- 269,275 ---- {"quote", QUOTE}, {"read", READ}, {"real", REAL}, + {"reassign", REASSIGN}, {"recheck", RECHECK}, {"references", REFERENCES}, {"reindex", REINDEX}, Index: src/backend/tcop/utility.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/tcop/utility.c,v retrieving revision 1.245 diff -c -r1.245 utility.c *** src/backend/tcop/utility.c 15 Oct 2005 02:49:27 -0000 1.245 --- src/backend/tcop/utility.c 19 Nov 2005 21:30:50 -0000 *************** *** 319,324 **** --- 319,326 ---- 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"))); *************** *** 689,696 **** break; /* ! * ******************************** object creation / destruction ******************************** ! * */ case T_DefineStmt: { --- 691,697 ---- break; /* ! * **************** object creation / destruction ****************** */ case T_DefineStmt: { *************** *** 983,988 **** --- 984,997 ---- 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; *************** *** 1641,1646 **** --- 1650,1663 ---- 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; Index: src/include/catalog/dependency.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/catalog/dependency.h,v retrieving revision 1.17 diff -c -r1.17 dependency.h *** src/include/catalog/dependency.h 15 Oct 2005 02:49:42 -0000 1.17 --- src/include/catalog/dependency.h 19 Nov 2005 21:30:50 -0000 *************** *** 177,182 **** --- 177,184 ---- Oid refClassId, Oid oldRefObjectId, Oid newRefObjectId); + extern bool objectIsInternalDependency(Oid classId, Oid objectId); + /* in pg_shdepend.c */ extern void recordSharedDependencyOn(ObjectAddress *depender, *************** *** 201,204 **** --- 203,210 ---- extern void dropDatabaseDependencies(Oid databaseId); + extern void shdepDropOwned(List *relids, DropBehavior behavior); + + extern void shdepReassignOwned(List *relids, Oid newrole); + #endif /* DEPENDENCY_H */ Index: src/include/commands/conversioncmds.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/commands/conversioncmds.h,v retrieving revision 1.10 diff -c -r1.10 conversioncmds.h *** src/include/commands/conversioncmds.h 28 Jun 2005 05:09:12 -0000 1.10 --- src/include/commands/conversioncmds.h 19 Nov 2005 21:30:50 -0000 *************** *** 21,25 **** --- 21,26 ---- extern void DropConversionCommand(List *conversion_name, DropBehavior behavior); 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 */ Index: src/include/commands/defrem.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/commands/defrem.h,v retrieving revision 1.68 diff -c -r1.68 defrem.h *** src/include/commands/defrem.h 15 Oct 2005 02:49:44 -0000 1.68 --- src/include/commands/defrem.h 19 Nov 2005 21:30:50 -0000 *************** *** 51,56 **** --- 51,57 ---- 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,69 **** --- 65,71 ---- 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,82 **** --- 79,85 ---- 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 */ Index: src/include/commands/schemacmds.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/commands/schemacmds.h,v retrieving revision 1.10 diff -c -r1.10 schemacmds.h *** src/include/commands/schemacmds.h 28 Jun 2005 05:09:12 -0000 1.10 --- src/include/commands/schemacmds.h 19 Nov 2005 21:30:50 -0000 *************** *** 24,28 **** --- 24,29 ---- 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 */ Index: src/include/commands/tablecmds.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/commands/tablecmds.h,v retrieving revision 1.24 diff -c -r1.24 tablecmds.h *** src/include/commands/tablecmds.h 15 Oct 2005 02:49:44 -0000 1.24 --- src/include/commands/tablecmds.h 19 Nov 2005 21:30:50 -0000 *************** *** 24,29 **** --- 24,31 ---- 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); Index: src/include/commands/user.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/commands/user.h,v retrieving revision 1.27 diff -c -r1.27 user.h *** src/include/commands/user.h 28 Jun 2005 05:09:12 -0000 1.27 --- src/include/commands/user.h 19 Nov 2005 21:30:50 -0000 *************** *** 20,24 **** --- 20,26 ---- 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 */ Index: src/include/nodes/nodes.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/nodes/nodes.h,v retrieving revision 1.176 diff -c -r1.176 nodes.h *** src/include/nodes/nodes.h 15 Oct 2005 02:49:45 -0000 1.176 --- src/include/nodes/nodes.h 19 Nov 2005 21:30:50 -0000 *************** *** 286,291 **** --- 286,293 ---- T_DropTableSpaceStmt, T_AlterObjectSchemaStmt, T_AlterOwnerStmt, + T_DropOwnedStmt, + T_ReassignOwnedStmt, T_A_Expr = 800, T_ColumnRef, Index: src/include/nodes/parsenodes.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/nodes/parsenodes.h,v retrieving revision 1.292 diff -c -r1.292 parsenodes.h *** src/include/nodes/parsenodes.h 26 Oct 2005 19:21:55 -0000 1.292 --- src/include/nodes/parsenodes.h 19 Nov 2005 21:30:50 -0000 *************** *** 1874,1877 **** --- 1874,1897 ---- 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 */ Index: src/include/utils/acl.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/utils/acl.h,v retrieving revision 1.88 diff -c -r1.88 acl.h *** src/include/utils/acl.h 18 Nov 2005 02:38:24 -0000 1.88 --- src/include/utils/acl.h 19 Nov 2005 21:30:50 -0000 *************** *** 221,226 **** --- 221,229 ---- * 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); Index: src/test/regress/expected/dependency.out =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/test/regress/expected/dependency.out,v retrieving revision 1.2 diff -c -r1.2 dependency.out *** src/test/regress/expected/dependency.out 4 Aug 2005 01:09:29 -0000 1.2 --- src/test/regress/expected/dependency.out 19 Nov 2005 22:40:31 -0000 *************** *** 38,43 **** --- 38,113 ---- 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; Index: src/test/regress/sql/dependency.sql =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/test/regress/sql/dependency.sql,v retrieving revision 1.2 diff -c -r1.2 dependency.sql *** src/test/regress/sql/dependency.sql 4 Aug 2005 01:09:29 -0000 1.2 --- src/test/regress/sql/dependency.sql 19 Nov 2005 22:39:36 -0000 *************** *** 39,44 **** --- 39,93 ---- 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;