diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2005-07-07 20:40:02 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2005-07-07 20:40:02 +0000 |
commit | 59d1b3d99e690734fa2a2bd0fae12b0cb1084294 (patch) | |
tree | ff074126ccc1a6342de38737d79e218f22fb9be4 /src/backend | |
parent | 442b59dd8bc4b0efa1d733690f6ba9dae3f61b1f (diff) | |
download | postgresql-59d1b3d99e690734fa2a2bd0fae12b0cb1084294.tar.gz |
Track dependencies on shared objects (which is to say, roles; we already
have adequate mechanisms for tracking the contents of databases and
tablespaces). This solves the longstanding problem that you can drop a
user who still owns objects and/or has access permissions.
Alvaro Herrera, with some kibitzing from Tom Lane.
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/catalog/Makefile | 7 | ||||
-rw-r--r-- | src/backend/catalog/aclchk.c | 127 | ||||
-rw-r--r-- | src/backend/catalog/dependency.c | 59 | ||||
-rw-r--r-- | src/backend/catalog/heap.c | 6 | ||||
-rw-r--r-- | src/backend/catalog/pg_conversion.c | 6 | ||||
-rw-r--r-- | src/backend/catalog/pg_namespace.c | 6 | ||||
-rw-r--r-- | src/backend/catalog/pg_operator.c | 7 | ||||
-rw-r--r-- | src/backend/catalog/pg_proc.c | 8 | ||||
-rw-r--r-- | src/backend/catalog/pg_shdepend.c | 1042 | ||||
-rw-r--r-- | src/backend/catalog/pg_type.c | 13 | ||||
-rw-r--r-- | src/backend/commands/conversioncmds.c | 7 | ||||
-rw-r--r-- | src/backend/commands/dbcommands.c | 18 | ||||
-rw-r--r-- | src/backend/commands/functioncmds.c | 5 | ||||
-rw-r--r-- | src/backend/commands/opclasscmds.c | 8 | ||||
-rw-r--r-- | src/backend/commands/operatorcmds.c | 5 | ||||
-rw-r--r-- | src/backend/commands/schemacmds.c | 6 | ||||
-rw-r--r-- | src/backend/commands/tablecmds.c | 5 | ||||
-rw-r--r-- | src/backend/commands/tablespace.c | 10 | ||||
-rw-r--r-- | src/backend/commands/typecmds.c | 6 | ||||
-rw-r--r-- | src/backend/commands/user.c | 54 | ||||
-rw-r--r-- | src/backend/utils/adt/acl.c | 83 |
21 files changed, 1418 insertions, 70 deletions
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile index 09addc4af1..fdcb2ff5a5 100644 --- a/src/backend/catalog/Makefile +++ b/src/backend/catalog/Makefile @@ -2,7 +2,7 @@ # # Makefile for backend/catalog # -# $PostgreSQL: pgsql/src/backend/catalog/Makefile,v 1.54 2005/06/28 05:08:52 tgl Exp $ +# $PostgreSQL: pgsql/src/backend/catalog/Makefile,v 1.55 2005/07/07 20:39:57 tgl Exp $ # #------------------------------------------------------------------------- @@ -12,7 +12,8 @@ include $(top_builddir)/src/Makefile.global OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \ pg_aggregate.o pg_constraint.o pg_conversion.o pg_depend.o \ - pg_largeobject.o pg_namespace.o pg_operator.o pg_proc.o pg_type.o + pg_largeobject.o pg_namespace.o pg_operator.o pg_proc.o pg_shdepend.o \ + pg_type.o BKIFILES = postgres.bki postgres.description @@ -31,7 +32,7 @@ POSTGRES_BKI_SRCS := $(addprefix $(top_srcdir)/src/include/catalog/,\ pg_operator.h pg_opclass.h pg_am.h pg_amop.h pg_amproc.h \ pg_language.h pg_largeobject.h pg_aggregate.h pg_statistic.h \ pg_rewrite.h pg_trigger.h pg_listener.h pg_description.h pg_cast.h \ - pg_namespace.h pg_conversion.h pg_database.h \ + pg_namespace.h pg_conversion.h pg_database.h pg_shdepend.h \ pg_authid.h pg_auth_members.h pg_tablespace.h pg_depend.h \ indexing.h \ ) diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c index f095ef24d6..8053ca73bb 100644 --- a/src/backend/catalog/aclchk.c +++ b/src/backend/catalog/aclchk.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.114 2005/06/28 19:51:21 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.115 2005/07/07 20:39:57 tgl Exp $ * * NOTES * See acl.h. @@ -19,6 +19,7 @@ #include "access/heapam.h" #include "catalog/catalog.h" +#include "catalog/dependency.h" #include "catalog/indexing.h" #include "catalog/namespace.h" #include "catalog/pg_auth_members.h" @@ -252,6 +253,10 @@ ExecuteGrantStmt_Relation(GrantStmt *stmt) Datum values[Natts_pg_class]; char nulls[Natts_pg_class]; char replaces[Natts_pg_class]; + int noldmembers; + int nnewmembers; + Oid *oldmembers; + Oid *newmembers; /* open pg_class */ relation = heap_open(RelationRelationId, RowExclusiveLock); @@ -344,11 +349,19 @@ ExecuteGrantStmt_Relation(GrantStmt *stmt) /* get a detoasted copy of the ACL */ old_acl = DatumGetAclPCopy(aclDatum); + /* + * We need the members of both old and new ACLs so we can correct + * the shared dependency information. + */ + 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); + /* finished building new ACL value, now insert it */ MemSet(values, 0, sizeof(values)); MemSet(nulls, ' ', sizeof(nulls)); @@ -359,13 +372,19 @@ ExecuteGrantStmt_Relation(GrantStmt *stmt) newtuple = heap_modifytuple(tuple, RelationGetDescr(relation), values, nulls, replaces); - ReleaseSysCache(tuple); - simple_heap_update(relation, &newtuple->t_self, newtuple); /* keep the catalog indexes up to date */ CatalogUpdateIndexes(relation, newtuple); + /* Update the shared dependency ACL info */ + updateAclDependencies(RelationRelationId, relOid, + ownerId, stmt->is_grant, + noldmembers, oldmembers, + nnewmembers, newmembers); + + ReleaseSysCache(tuple); + pfree(new_acl); heap_close(relation, RowExclusiveLock); @@ -422,6 +441,10 @@ ExecuteGrantStmt_Database(GrantStmt *stmt) Datum values[Natts_pg_database]; char nulls[Natts_pg_database]; char replaces[Natts_pg_database]; + int noldmembers; + int nnewmembers; + Oid *oldmembers; + Oid *newmembers; relation = heap_open(DatabaseRelationId, RowExclusiveLock); ScanKeyInit(&entry[0], @@ -503,11 +526,19 @@ ExecuteGrantStmt_Database(GrantStmt *stmt) /* get a detoasted copy of the ACL */ old_acl = DatumGetAclPCopy(aclDatum); + /* + * We need the members of both old and new ACLs so we can correct + * the shared dependency information. + */ + 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); + /* finished building new ACL value, now insert it */ MemSet(values, 0, sizeof(values)); MemSet(nulls, ' ', sizeof(nulls)); @@ -523,6 +554,12 @@ ExecuteGrantStmt_Database(GrantStmt *stmt) /* keep the catalog indexes up to date */ CatalogUpdateIndexes(relation, newtuple); + /* Update the shared dependency ACL info */ + updateAclDependencies(DatabaseRelationId, HeapTupleGetOid(tuple), + ownerId, stmt->is_grant, + noldmembers, oldmembers, + nnewmembers, newmembers); + pfree(new_acl); heap_endscan(scan); @@ -580,6 +617,10 @@ ExecuteGrantStmt_Function(GrantStmt *stmt) Datum values[Natts_pg_proc]; char nulls[Natts_pg_proc]; char replaces[Natts_pg_proc]; + int noldmembers; + int nnewmembers; + Oid *oldmembers; + Oid *newmembers; oid = LookupFuncNameTypeNames(func->funcname, func->funcargs, false); @@ -658,11 +699,19 @@ ExecuteGrantStmt_Function(GrantStmt *stmt) /* get a detoasted copy of the ACL */ old_acl = DatumGetAclPCopy(aclDatum); + /* + * We need the members of both old and new ACLs so we can correct + * the shared dependency information. + */ + 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); + /* finished building new ACL value, now insert it */ MemSet(values, 0, sizeof(values)); MemSet(nulls, ' ', sizeof(nulls)); @@ -673,13 +722,19 @@ ExecuteGrantStmt_Function(GrantStmt *stmt) newtuple = heap_modifytuple(tuple, RelationGetDescr(relation), values, nulls, replaces); - ReleaseSysCache(tuple); - simple_heap_update(relation, &newtuple->t_self, newtuple); /* keep the catalog indexes up to date */ CatalogUpdateIndexes(relation, newtuple); + /* Update the shared dependency ACL info */ + updateAclDependencies(ProcedureRelationId, oid, + ownerId, stmt->is_grant, + noldmembers, oldmembers, + nnewmembers, newmembers); + + ReleaseSysCache(tuple); + pfree(new_acl); heap_close(relation, RowExclusiveLock); @@ -734,6 +789,10 @@ ExecuteGrantStmt_Language(GrantStmt *stmt) Datum values[Natts_pg_language]; char nulls[Natts_pg_language]; char replaces[Natts_pg_language]; + int noldmembers; + int nnewmembers; + Oid *oldmembers; + Oid *newmembers; relation = heap_open(LanguageRelationId, RowExclusiveLock); tuple = SearchSysCache(LANGNAME, @@ -822,11 +881,19 @@ ExecuteGrantStmt_Language(GrantStmt *stmt) /* get a detoasted copy of the ACL */ old_acl = DatumGetAclPCopy(aclDatum); + /* + * We need the members of both old and new ACLs so we can correct + * the shared dependency information. + */ + 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); + /* finished building new ACL value, now insert it */ MemSet(values, 0, sizeof(values)); MemSet(nulls, ' ', sizeof(nulls)); @@ -837,13 +904,19 @@ ExecuteGrantStmt_Language(GrantStmt *stmt) newtuple = heap_modifytuple(tuple, RelationGetDescr(relation), values, nulls, replaces); - ReleaseSysCache(tuple); - simple_heap_update(relation, &newtuple->t_self, newtuple); /* keep the catalog indexes up to date */ CatalogUpdateIndexes(relation, newtuple); + /* Update the shared dependency ACL info */ + updateAclDependencies(LanguageRelationId, HeapTupleGetOid(tuple), + ownerId, stmt->is_grant, + noldmembers, oldmembers, + nnewmembers, newmembers); + + ReleaseSysCache(tuple); + pfree(new_acl); heap_close(relation, RowExclusiveLock); @@ -898,6 +971,10 @@ ExecuteGrantStmt_Namespace(GrantStmt *stmt) Datum values[Natts_pg_namespace]; char nulls[Natts_pg_namespace]; char replaces[Natts_pg_namespace]; + int noldmembers; + int nnewmembers; + Oid *oldmembers; + Oid *newmembers; relation = heap_open(NamespaceRelationId, RowExclusiveLock); tuple = SearchSysCache(NAMESPACENAME, @@ -977,11 +1054,19 @@ ExecuteGrantStmt_Namespace(GrantStmt *stmt) /* get a detoasted copy of the ACL */ old_acl = DatumGetAclPCopy(aclDatum); + /* + * We need the members of both old and new ACLs so we can correct + * the shared dependency information. + */ + 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); + /* finished building new ACL value, now insert it */ MemSet(values, 0, sizeof(values)); MemSet(nulls, ' ', sizeof(nulls)); @@ -992,13 +1077,19 @@ ExecuteGrantStmt_Namespace(GrantStmt *stmt) newtuple = heap_modifytuple(tuple, RelationGetDescr(relation), values, nulls, replaces); - ReleaseSysCache(tuple); - simple_heap_update(relation, &newtuple->t_self, newtuple); /* keep the catalog indexes up to date */ CatalogUpdateIndexes(relation, newtuple); + /* Update the shared dependency ACL info */ + updateAclDependencies(NamespaceRelationId, HeapTupleGetOid(tuple), + ownerId, stmt->is_grant, + noldmembers, oldmembers, + nnewmembers, newmembers); + + ReleaseSysCache(tuple); + pfree(new_acl); heap_close(relation, RowExclusiveLock); @@ -1055,6 +1146,10 @@ ExecuteGrantStmt_Tablespace(GrantStmt *stmt) Datum values[Natts_pg_tablespace]; char nulls[Natts_pg_tablespace]; char replaces[Natts_pg_tablespace]; + int noldmembers; + int nnewmembers; + Oid *oldmembers; + Oid *newmembers; relation = heap_open(TableSpaceRelationId, RowExclusiveLock); ScanKeyInit(&entry[0], @@ -1136,11 +1231,19 @@ ExecuteGrantStmt_Tablespace(GrantStmt *stmt) /* get a detoasted copy of the ACL */ old_acl = DatumGetAclPCopy(aclDatum); + /* + * We need the members of both old and new ACLs so we can correct + * the shared dependency information. + */ + 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); + /* finished building new ACL value, now insert it */ MemSet(values, 0, sizeof(values)); MemSet(nulls, ' ', sizeof(nulls)); @@ -1156,6 +1259,12 @@ ExecuteGrantStmt_Tablespace(GrantStmt *stmt) /* keep the catalog indexes up to date */ 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); diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c index febd0b42dc..227b21c656 100644 --- a/src/backend/catalog/dependency.c +++ b/src/backend/catalog/dependency.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.44 2005/04/14 20:03:23 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.45 2005/07/07 20:39:57 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -22,9 +22,11 @@ #include "catalog/indexing.h" #include "catalog/namespace.h" #include "catalog/pg_attrdef.h" +#include "catalog/pg_authid.h" #include "catalog/pg_cast.h" #include "catalog/pg_constraint.h" #include "catalog/pg_conversion.h" +#include "catalog/pg_database.h" #include "catalog/pg_depend.h" #include "catalog/pg_language.h" #include "catalog/pg_namespace.h" @@ -32,12 +34,15 @@ #include "catalog/pg_operator.h" #include "catalog/pg_proc.h" #include "catalog/pg_rewrite.h" +#include "catalog/pg_tablespace.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" #include "commands/comment.h" +#include "commands/dbcommands.h" #include "commands/defrem.h" #include "commands/proclang.h" #include "commands/schemacmds.h" +#include "commands/tablespace.h" #include "commands/trigger.h" #include "commands/typecmds.h" #include "lib/stringinfo.h" @@ -509,6 +514,7 @@ recursiveDeletion(const ObjectAddress *object, break; } + /* delete the pg_depend tuple */ simple_heap_delete(depRel, &tup->t_self); } @@ -585,6 +591,14 @@ recursiveDeletion(const ObjectAddress *object, DeleteComments(object->objectId, object->classId, object->objectSubId); /* + * Delete shared dependency references related to this object. + * Sub-objects (columns) don't have dependencies on global objects, + * so skip them. + */ + if (object->objectSubId == 0) + deleteSharedDependencyRecordsFor(object->classId, object->objectId); + + /* * CommandCounterIncrement here to ensure that preceding changes are * all visible. */ @@ -1365,6 +1379,18 @@ getObjectClass(const ObjectAddress *object) case NamespaceRelationId: Assert(object->objectSubId == 0); return OCLASS_SCHEMA; + + case AuthIdRelationId: + Assert(object->objectSubId == 0); + return OCLASS_ROLE; + + case DatabaseRelationId: + Assert(object->objectSubId == 0); + return OCLASS_DATABASE; + + case TableSpaceRelationId: + Assert(object->objectSubId == 0); + return OCLASS_TBLSPACE; } /* shouldn't get here */ @@ -1680,6 +1706,37 @@ getObjectDescription(const ObjectAddress *object) break; } + case OCLASS_ROLE: + { + appendStringInfo(&buffer, _("role %s"), + GetUserNameFromId(object->objectId)); + break; + } + + case OCLASS_DATABASE: + { + char *datname; + + datname = get_database_name(object->objectId); + if (!datname) + elog(ERROR, "cache lookup failed for database %u", + object->objectId); + appendStringInfo(&buffer, _("database %s"), datname); + break; + } + + case OCLASS_TBLSPACE: + { + char *tblspace; + + tblspace = get_tablespace_name(object->objectId); + if (!tblspace) + elog(ERROR, "cache lookup failed for tablespace %u", + object->objectId); + appendStringInfo(&buffer, _("tablespace %s"), tblspace); + break; + } + default: appendStringInfo(&buffer, "unrecognized object %u %u %d", object->classId, diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 8c6f6bec25..29fd3b0b6d 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.285 2005/06/05 00:38:07 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.286 2005/07/07 20:39:57 tgl Exp $ * * * INTERFACE ROUTINES @@ -755,6 +755,8 @@ heap_create_with_catalog(const char *relname, * make a dependency link to force the relation to be deleted if its * namespace is. Skip this in bootstrap mode, since we don't make * dependencies while bootstrapping. + * + * Also make a dependency link to its owner. */ if (!IsBootstrapProcessingMode()) { @@ -768,6 +770,8 @@ heap_create_with_catalog(const char *relname, referenced.objectId = relnamespace; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + + recordDependencyOnOwner(RelationRelationId, new_rel_oid, GetUserId()); } /* diff --git a/src/backend/catalog/pg_conversion.c b/src/backend/catalog/pg_conversion.c index 61a1a53902..568b77dfed 100644 --- a/src/backend/catalog/pg_conversion.c +++ b/src/backend/catalog/pg_conversion.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/pg_conversion.c,v 1.24 2005/06/28 05:08:52 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/pg_conversion.c,v 1.25 2005/07/07 20:39:57 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -120,6 +120,10 @@ ConversionCreate(const char *conname, Oid connamespace, referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + /* create dependency on owner */ + recordDependencyOnOwner(ConversionRelationId, HeapTupleGetOid(tup), + conowner); + heap_freetuple(tup); heap_close(rel, RowExclusiveLock); diff --git a/src/backend/catalog/pg_namespace.c b/src/backend/catalog/pg_namespace.c index 8144d64136..b0145f62a5 100644 --- a/src/backend/catalog/pg_namespace.c +++ b/src/backend/catalog/pg_namespace.c @@ -8,13 +8,14 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/pg_namespace.c,v 1.14 2005/06/28 05:08:52 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/pg_namespace.c,v 1.15 2005/07/07 20:39:57 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" #include "access/heapam.h" +#include "catalog/dependency.h" #include "catalog/indexing.h" #include "catalog/pg_namespace.h" #include "utils/builtins.h" @@ -72,5 +73,8 @@ NamespaceCreate(const char *nspName, Oid ownerId) heap_close(nspdesc, RowExclusiveLock); + /* Record dependency on owner */ + recordDependencyOnOwner(NamespaceRelationId, nspoid, ownerId); + return nspoid; } diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c index 55fa9e127c..903a46ac0f 100644 --- a/src/backend/catalog/pg_operator.c +++ b/src/backend/catalog/pg_operator.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/pg_operator.c,v 1.92 2005/06/28 05:08:52 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/pg_operator.c,v 1.93 2005/07/07 20:39:57 tgl Exp $ * * NOTES * these routines moved here from commands/define.c and somewhat cleaned up. @@ -889,6 +889,7 @@ makeOperatorDependencies(HeapTuple tuple) /* In case we are updating a shell, delete any existing entries */ deleteDependencyRecordsFor(myself.classId, myself.objectId); + deleteSharedDependencyRecordsFor(myself.classId, myself.objectId); /* Dependency on namespace */ if (OidIsValid(oper->oprnamespace)) @@ -962,4 +963,8 @@ makeOperatorDependencies(HeapTuple tuple) referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } + + /* Dependency on owner */ + recordDependencyOnOwner(OperatorRelationId, HeapTupleGetOid(tuple), + oper->oprowner); } diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c index f8ed892ebf..e610d447bd 100644 --- a/src/backend/catalog/pg_proc.c +++ b/src/backend/catalog/pg_proc.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.131 2005/06/28 19:51:21 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.132 2005/07/07 20:39:57 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -349,7 +349,10 @@ ProcedureCreate(const char *procedureName, * existing function, first delete any existing pg_depend entries. */ if (is_update) + { deleteDependencyRecordsFor(ProcedureRelationId, retval); + deleteSharedDependencyRecordsFor(ProcedureRelationId, retval); + } myself.classId = ProcedureRelationId; myself.objectId = retval; @@ -382,6 +385,9 @@ ProcedureCreate(const char *procedureName, recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } + /* dependency on owner */ + recordDependencyOnOwner(ProcedureRelationId, retval, GetUserId()); + heap_freetuple(tup); heap_close(rel, RowExclusiveLock); diff --git a/src/backend/catalog/pg_shdepend.c b/src/backend/catalog/pg_shdepend.c new file mode 100644 index 0000000000..b5013e8844 --- /dev/null +++ b/src/backend/catalog/pg_shdepend.c @@ -0,0 +1,1042 @@ +/*------------------------------------------------------------------------- + * + * pg_shdepend.c + * routines to support manipulation of the pg_shdepend relation + * + * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $PostgreSQL: pgsql/src/backend/catalog/pg_shdepend.c,v 1.1 2005/07/07 20:39:57 tgl Exp $ + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/genam.h" +#include "access/heapam.h" +#include "catalog/dependency.h" +#include "catalog/indexing.h" +#include "catalog/pg_authid.h" +#include "catalog/pg_database.h" +#include "catalog/pg_shdepend.h" +#include "lib/stringinfo.h" +#include "miscadmin.h" +#include "utils/fmgroids.h" +#include "utils/inval.h" +#include "utils/syscache.h" + + +typedef enum +{ + LOCAL_OBJECT, + SHARED_OBJECT, + REMOTE_OBJECT +} objectType; + +static int getOidListDiff(Oid *list1, int nlist1, Oid *list2, int nlist2, + Oid **diff); +static Oid classIdGetDbId(Oid classId); +static void shdepLockAndCheckObject(Oid classId, Oid objectId); +static void shdepChangeDep(Relation sdepRel, Oid classid, Oid objid, + Oid refclassid, Oid refobjid, + SharedDependencyType deptype); +static void shdepAddDependency(Relation sdepRel, Oid classId, Oid objectId, + Oid refclassId, Oid refobjId, + SharedDependencyType deptype); +static void shdepDropDependency(Relation sdepRel, Oid classId, Oid objectId, + Oid refclassId, Oid refobjId, + SharedDependencyType deptype); +static void storeObjectDescription(StringInfo descs, objectType type, + ObjectAddress *object, + SharedDependencyType deptype, + int count); +static bool isSharedObjectPinned(Oid classId, Oid objectId, Relation sdepRel); + + +/* + * recordSharedDependencyOn + * + * Record a dependency between 2 objects via their respective ObjectAddresses. + * The first argument is the dependent object, the second the one it + * references (which must be a shared object). + * + * This locks the referenced object and makes sure it still exists. + * Then it creates an entry in pg_shdepend. The lock is kept until + * the end of the transaction. + * + * Dependencies on pinned objects are not recorded. + */ +void +recordSharedDependencyOn(ObjectAddress *depender, + ObjectAddress *referenced, + SharedDependencyType deptype) +{ + Relation sdepRel; + + /* + * Objects in pg_shdepend can't have SubIds. + */ + Assert(depender->objectSubId == 0); + Assert(referenced->objectSubId == 0); + + /* + * During bootstrap, do nothing since pg_shdepend may not exist yet. + * initdb will fill in appropriate pg_shdepend entries after bootstrap. + */ + if (IsBootstrapProcessingMode()) + return; + + sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock); + + /* If the referenced object is pinned, do nothing. */ + if (!isSharedObjectPinned(referenced->classId, referenced->objectId, + sdepRel)) + { + shdepAddDependency(sdepRel, depender->classId, depender->objectId, + referenced->classId, referenced->objectId, + deptype); + } + + heap_close(sdepRel, RowExclusiveLock); +} + +/* + * recordDependencyOnOwner + * + * A convenient wrapper of recordSharedDependencyOn -- register the specified + * user as owner of the given object. + * + * Note: it's the caller's responsibility to ensure that there isn't an owner + * entry for the object already. + */ +void +recordDependencyOnOwner(Oid classId, Oid objectId, Oid owner) +{ + ObjectAddress myself, + referenced; + + myself.classId = classId; + myself.objectId = objectId; + myself.objectSubId = 0; + + referenced.classId = AuthIdRelationId; + referenced.objectId = owner; + referenced.objectSubId = 0; + + recordSharedDependencyOn(&myself, &referenced, SHARED_DEPENDENCY_OWNER); +} + +/* + * shdepChangeDep + * + * Update shared dependency records to account for an updated referenced + * object. This is an internal workhorse for operations such as changing + * an object's owner. + * + * There must be no more than one existing entry for the given dependent + * object and dependency type! So in practice this can only be used for + * updating SHARED_DEPENDENCY_OWNER entries, which should have that property. + * + * If there is no previous entry, we assume it was referencing a PINned + * object, so we create a new entry. If the new referenced object is + * PINned, we don't create an entry (and drop the old one, if any). + * + * sdepRel must be the pg_shdepend relation, already opened and suitably + * locked. + */ +static void +shdepChangeDep(Relation sdepRel, Oid classid, Oid objid, + Oid refclassid, Oid refobjid, + SharedDependencyType deptype) +{ + Oid dbid = classIdGetDbId(classid); + HeapTuple oldtup = NULL; + HeapTuple scantup; + ScanKeyData key[3]; + SysScanDesc scan; + + /* + * Make sure the new referenced object doesn't go away while we record + * the dependency. + */ + shdepLockAndCheckObject(refclassid, refobjid); + + /* + * Look for a previous entry + */ + ScanKeyInit(&key[0], + Anum_pg_shdepend_dbid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(dbid)); + ScanKeyInit(&key[1], + Anum_pg_shdepend_classid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(classid)); + ScanKeyInit(&key[2], + Anum_pg_shdepend_objid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(objid)); + + scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true, + SnapshotNow, 3, key); + + while ((scantup = systable_getnext(scan)) != NULL) + { + /* Ignore if not of the target dependency type */ + if (((Form_pg_shdepend) GETSTRUCT(scantup))->deptype != deptype) + continue; + /* Caller screwed up if multiple matches */ + if (oldtup) + elog(ERROR, + "multiple pg_shdepend entries for object %u/%u deptype %c", + classid, objid, deptype); + oldtup = heap_copytuple(scantup); + } + + systable_endscan(scan); + + if (isSharedObjectPinned(refclassid, refobjid, sdepRel)) + { + /* No new entry needed, so just delete existing entry if any */ + if (oldtup) + simple_heap_delete(sdepRel, &oldtup->t_self); + } + else if (oldtup) + { + /* Need to update existing entry */ + Form_pg_shdepend shForm = (Form_pg_shdepend) GETSTRUCT(oldtup); + + /* Since oldtup is a copy, we can just modify it in-memory */ + shForm->refclassid = refclassid; + shForm->refobjid = refobjid; + + simple_heap_update(sdepRel, &oldtup->t_self, oldtup); + + /* keep indexes current */ + CatalogUpdateIndexes(sdepRel, oldtup); + } + else + { + /* Need to insert new entry */ + Datum values[Natts_pg_shdepend]; + bool nulls[Natts_pg_shdepend]; + + memset(nulls, 0, sizeof(nulls)); + + values[Anum_pg_shdepend_dbid - 1] = ObjectIdGetDatum(dbid); + values[Anum_pg_shdepend_classid - 1] = ObjectIdGetDatum(classid); + values[Anum_pg_shdepend_objid - 1] = ObjectIdGetDatum(objid); + + values[Anum_pg_shdepend_refclassid - 1] = ObjectIdGetDatum(refclassid); + values[Anum_pg_shdepend_refobjid - 1] = ObjectIdGetDatum(refobjid); + values[Anum_pg_shdepend_deptype - 1] = CharGetDatum(deptype); + + /* + * we are reusing oldtup just to avoid declaring a new variable, + * but it's certainly a new tuple + */ + oldtup = heap_form_tuple(RelationGetDescr(sdepRel), values, nulls); + simple_heap_insert(sdepRel, oldtup); + + /* keep indexes current */ + CatalogUpdateIndexes(sdepRel, oldtup); + } + + if (oldtup) + heap_freetuple(oldtup); +} + +/* + * changeDependencyOnOwner + * + * Update the shared dependencies to account for the new owner. + */ +void +changeDependencyOnOwner(Oid classId, Oid objectId, Oid newOwnerId) +{ + Relation sdepRel; + + sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock); + + /* Adjust the SHARED_DEPENDENCY_OWNER entry */ + shdepChangeDep(sdepRel, classId, objectId, + AuthIdRelationId, newOwnerId, + SHARED_DEPENDENCY_OWNER); + + /*---------- + * There should never be a SHARED_DEPENDENCY_ACL entry for the owner, + * so get rid of it if there is one. This can happen if the new owner + * was previously granted some rights to the object. + * + * This step is analogous to aclnewowner's removal of duplicate entries + * in the ACL. We have to do it to handle this scenario: + * A grants some rights on an object to B + * ALTER OWNER changes the object's owner to B + * ALTER OWNER changes the object's owner to C + * The third step would remove all mention of B from the object's ACL, + * but we'd still have a SHARED_DEPENDENCY_ACL for B if we did not do + * things this way. + * + * The rule against having a SHARED_DEPENDENCY_ACL entry for the owner + * allows us to fix things up in just this one place, without having + * to make the various ALTER OWNER routines each know about it. + *---------- + */ + shdepDropDependency(sdepRel, classId, objectId, + AuthIdRelationId, newOwnerId, + SHARED_DEPENDENCY_ACL); + + heap_close(sdepRel, RowExclusiveLock); +} + +/* + * getOidListDiff + * Helper for updateAclDependencies. + * + * Takes two Oid arrays and returns elements from the first not found in the + * second. We assume both arrays are sorted and de-duped, and that the + * second array does not contain any values not found in the first. + * + * NOTE: Both input arrays are pfreed. + */ +static int +getOidListDiff(Oid *list1, int nlist1, Oid *list2, int nlist2, Oid **diff) +{ + Oid *result; + int i, + j, + k = 0; + + AssertArg(nlist1 >= nlist2 && nlist2 >= 0); + + result = palloc(sizeof(Oid) * (nlist1 - nlist2)); + *diff = result; + + for (i = 0, j = 0; i < nlist1 && j < nlist2; ) + { + if (list1[i] == list2[j]) + { + i++; + j++; + } + else if (list1[i] < list2[j]) + { + result[k++] = list1[i]; + i++; + } + else + { + /* can't happen */ + elog(WARNING, "invalid element %u in shorter list", list2[j]); + j++; + } + } + + for (; i < nlist1; i++) + result[k++] = list1[i]; + + /* We should have copied the exact number of elements */ + AssertState(k == (nlist1 - nlist2)); + + if (list1) + pfree(list1); + if (list2) + pfree(list2); + + return k; +} + +/* + * updateAclDependencies + * Update the pg_shdepend info for an object's ACL during GRANT/REVOKE. + * + * classId, objectId: identify the object whose ACL this is + * ownerId: role owning the object + * isGrant: are we adding or removing ACL entries? + * noldmembers, oldmembers: array of roleids appearing in old ACL + * nnewmembers, newmembers: array of roleids appearing in new ACL + * + * We calculate the difference between the new and old lists of roles, + * and then insert (if it's a grant) or delete (if it's a revoke) from + * pg_shdepend as appropiate. + * + * Note that we can't insert blindly at grant, because we would end up with + * duplicate registered dependencies. We could check for existence of the + * tuple before inserting, but that seems to be more expensive than what we are + * doing now. On the other hand, we can't just delete the tuples blindly at + * revoke, because the user may still have other privileges. + * + * NOTE: Both input arrays must be sorted and de-duped. They are pfreed + * before return. + */ +void +updateAclDependencies(Oid classId, Oid objectId, Oid ownerId, bool isGrant, + int noldmembers, Oid *oldmembers, + int nnewmembers, Oid *newmembers) +{ + Relation sdepRel; + Oid *diff; + int ndiff, + i; + + /* + * Calculate the differences between the old and new lists. + */ + if (isGrant) + ndiff = getOidListDiff(newmembers, nnewmembers, + oldmembers, noldmembers, &diff); + else + ndiff = getOidListDiff(oldmembers, noldmembers, + newmembers, nnewmembers, &diff); + + if (ndiff > 0) + { + sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock); + + /* Add or drop the respective dependency */ + for (i = 0; i < ndiff; i++) + { + Oid roleid = diff[i]; + + /* + * Skip the owner: he has an OWNER shdep entry instead. + * (This is not just a space optimization; it makes ALTER OWNER + * easier. See notes in changeDependencyOnOwner.) + */ + if (roleid == ownerId) + continue; + + /* Skip pinned roles */ + if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel)) + continue; + + if (isGrant) + shdepAddDependency(sdepRel, classId, objectId, + AuthIdRelationId, roleid, + SHARED_DEPENDENCY_ACL); + else + shdepDropDependency(sdepRel, classId, objectId, + AuthIdRelationId, roleid, + SHARED_DEPENDENCY_ACL); + } + + heap_close(sdepRel, RowExclusiveLock); + } + + pfree(diff); +} + +/* + * A struct to keep track of dependencies found in other databases. + */ +typedef struct +{ + Oid dbOid; + int count; +} remoteDep; + +/* + * checkSharedDependencies + * + * Check whether there are shared dependency entries for a given shared + * object. Returns a string containing a newline-separated list of object + * descriptions that depend on the shared object, or NULL if none is found. + * + * We can find three different kinds of dependencies: dependencies on objects + * of the current database; dependencies on shared objects; and dependencies + * on objects local to other databases. We can (and do) provide descriptions + * of the two former kinds of objects, but we can't do that for "remote" + * objects, so we just provide a count of them. + * + * If we find a SHARED_DEPENDENCY_PIN entry, we can error out early. + */ +char * +checkSharedDependencies(Oid classId, Oid objectId) +{ + Relation sdepRel; + ScanKeyData key[2]; + SysScanDesc scan; + HeapTuple tup; + int totalDeps = 0; + int numLocalDeps = 0; + int numSharedDeps = 0; + List *remDeps = NIL; + ListCell *cell; + ObjectAddress object; + StringInfoData descs; + + /* + * We try to limit the number of reported dependencies to something + * sane, both for the user's sake and to avoid blowing out memory. + */ +#define MAX_REPORTED_DEPS 100 + + initStringInfo(&descs); + + sdepRel = heap_open(SharedDependRelationId, AccessShareLock); + + ScanKeyInit(&key[0], + Anum_pg_shdepend_refclassid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(classId)); + ScanKeyInit(&key[1], + Anum_pg_shdepend_refobjid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(objectId)); + + scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true, + SnapshotNow, 2, key); + + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tup); + + /* This case can be dispatched quickly */ + if (sdepForm->deptype == SHARED_DEPENDENCY_PIN) + { + object.classId = classId; + object.objectId = objectId; + object.objectSubId = 0; + ereport(ERROR, + (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST), + errmsg("cannot drop %s because it is required by the database system", + getObjectDescription(&object)))); + } + + object.classId = sdepForm->classid; + object.objectId = sdepForm->objid; + object.objectSubId = 0; + + /* + * If it's a dependency local to this database or it's a shared + * object, describe it. + * + * If it's a remote dependency, keep track of it so we can report + * the number of them later. + */ + if (sdepForm->dbid == MyDatabaseId) + { + numLocalDeps++; + if (++totalDeps <= MAX_REPORTED_DEPS) + storeObjectDescription(&descs, LOCAL_OBJECT, &object, + sdepForm->deptype, 0); + } + else if (sdepForm->dbid == InvalidOid) + { + numSharedDeps++; + if (++totalDeps <= MAX_REPORTED_DEPS) + storeObjectDescription(&descs, SHARED_OBJECT, &object, + sdepForm->deptype, 0); + } + else + { + /* It's not local nor shared, so it must be remote. */ + remoteDep *dep; + bool stored = false; + + /* + * XXX this info is kept on a simple List. Maybe it's not good + * for performance, but using a hash table seems needlessly + * complex. The expected number of databases is not high + * anyway, I suppose. + */ + foreach(cell, remDeps) + { + dep = lfirst(cell); + if (dep->dbOid == sdepForm->dbid) + { + dep->count++; + stored = true; + break; + } + } + if (!stored) + { + dep = (remoteDep *) palloc(sizeof(remoteDep)); + dep->dbOid = sdepForm->dbid; + dep->count = 1; + remDeps = lappend(remDeps, dep); + totalDeps++; + } + } + } + + systable_endscan(scan); + + heap_close(sdepRel, AccessShareLock); + + if (totalDeps > MAX_REPORTED_DEPS) + { + /* + * Report seems unreasonably long, so reduce it to per-database info + * + * Note: we don't ever suppress per-database totals, which should + * be OK as long as there aren't too many databases ... + */ + descs.len = 0; /* reset to empty */ + descs.data[0] = '\0'; + + if (numLocalDeps > 0) + { + appendStringInfo(&descs, _("%d objects in this database"), + numLocalDeps); + if (numSharedDeps > 0) + appendStringInfoChar(&descs, '\n'); + } + if (numSharedDeps > 0) + appendStringInfo(&descs, _("%d shared objects"), + numSharedDeps); + } + + foreach(cell, remDeps) + { + remoteDep *dep = lfirst(cell); + + object.classId = DatabaseRelationId; + object.objectId = dep->dbOid; + object.objectSubId = 0; + + storeObjectDescription(&descs, REMOTE_OBJECT, &object, + SHARED_DEPENDENCY_INVALID, dep->count); + } + + list_free_deep(remDeps); + + if (descs.len == 0) + { + pfree(descs.data); + return NULL; + } + + return descs.data; +} + +/* + * copyTemplateDependencies + * + * Routine to create the initial shared dependencies of a new database. + * We simply copy the dependencies from the template database. + */ +void +copyTemplateDependencies(Oid templateDbId, Oid newDbId) +{ + Relation sdepRel; + TupleDesc sdepDesc; + ScanKeyData key[1]; + SysScanDesc scan; + HeapTuple tup; + CatalogIndexState indstate; + Datum values[Natts_pg_shdepend]; + bool nulls[Natts_pg_shdepend]; + bool replace[Natts_pg_shdepend]; + + sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock); + sdepDesc = RelationGetDescr(sdepRel); + + indstate = CatalogOpenIndexes(sdepRel); + + /* Scan all entries with dbid = templateDbId */ + ScanKeyInit(&key[0], + Anum_pg_shdepend_dbid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(templateDbId)); + + scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true, + SnapshotNow, 1, key); + + /* Set up to copy the tuples except for inserting newDbId */ + memset(values, 0, sizeof(values)); + memset(nulls, false, sizeof(nulls)); + memset(replace, false, sizeof(replace)); + + replace[Anum_pg_shdepend_dbid - 1] = true; + values[Anum_pg_shdepend_dbid - 1] = ObjectIdGetDatum(newDbId); + + /* + * Copy the entries of the original database, changing the database Id + * to that of the new database. Note that because we are not copying + * rows with dbId == 0 (ie, rows describing dependent shared objects) + * we won't copy the ownership dependency of the template database + * itself; this is what we want. + */ + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + HeapTuple newtup; + + newtup = heap_modify_tuple(tup, sdepDesc, values, nulls, replace); + simple_heap_insert(sdepRel, newtup); + + /* Keep indexes current */ + CatalogIndexInsert(indstate, newtup); + + heap_freetuple(newtup); + } + + systable_endscan(scan); + + CatalogCloseIndexes(indstate); + heap_close(sdepRel, RowExclusiveLock); +} + +/* + * dropDatabaseDependencies + * + * Delete pg_shdepend entries corresponding to a database that's being + * dropped. + */ +void +dropDatabaseDependencies(Oid databaseId) +{ + Relation sdepRel; + ScanKeyData key[1]; + SysScanDesc scan; + HeapTuple tup; + + sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock); + + /* + * First, delete all the entries that have the database Oid in the + * dbid field. + */ + ScanKeyInit(&key[0], + Anum_pg_shdepend_dbid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(databaseId)); + /* We leave the other index fields unspecified */ + + scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true, + SnapshotNow, 1, key); + + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + simple_heap_delete(sdepRel, &tup->t_self); + } + + systable_endscan(scan); + + /* Now delete all entries corresponding to the database itself */ + shdepDropDependency(sdepRel, DatabaseRelationId, databaseId, + InvalidOid, InvalidOid, + SHARED_DEPENDENCY_INVALID); + + heap_close(sdepRel, RowExclusiveLock); +} + +/* + * deleteSharedDependencyRecordsFor + * + * Delete all pg_shdepend entries corresponding to a database-local object + * that's being dropped or modified. + */ +void +deleteSharedDependencyRecordsFor(Oid classId, Oid objectId) +{ + Relation sdepRel; + + sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock); + + shdepDropDependency(sdepRel, classId, objectId, + InvalidOid, InvalidOid, + SHARED_DEPENDENCY_INVALID); + + heap_close(sdepRel, RowExclusiveLock); +} + +/* + * shdepAddDependency + * Internal workhorse for inserting into pg_shdepend + * + * sdepRel must be the pg_shdepend relation, already opened and suitably + * locked. + */ +static void +shdepAddDependency(Relation sdepRel, Oid classId, Oid objectId, + Oid refclassId, Oid refobjId, + SharedDependencyType deptype) +{ + HeapTuple tup; + Datum values[Natts_pg_shdepend]; + bool nulls[Natts_pg_shdepend]; + + /* + * Make sure the object doesn't go away while we record the dependency + * on it. DROP routines should lock the object exclusively before they + * check shared dependencies. + */ + shdepLockAndCheckObject(refclassId, refobjId); + + memset(nulls, false, sizeof(nulls)); + + /* + * Form the new tuple and record the dependency. + */ + values[Anum_pg_shdepend_dbid - 1] = ObjectIdGetDatum(classIdGetDbId(classId)); + values[Anum_pg_shdepend_classid - 1] = ObjectIdGetDatum(classId); + values[Anum_pg_shdepend_objid - 1] = ObjectIdGetDatum(objectId); + + values[Anum_pg_shdepend_refclassid - 1] = ObjectIdGetDatum(refclassId); + values[Anum_pg_shdepend_refobjid - 1] = ObjectIdGetDatum(refobjId); + values[Anum_pg_shdepend_deptype - 1] = CharGetDatum(deptype); + + tup = heap_form_tuple(sdepRel->rd_att, values, nulls); + + simple_heap_insert(sdepRel, tup); + + /* keep indexes current */ + CatalogUpdateIndexes(sdepRel, tup); + + /* clean up */ + heap_freetuple(tup); +} + +/* + * shdepDropDependency + * Internal workhorse for deleting entries from pg_shdepend. + * + * We drop entries having the following properties: + * dependent object is the one identified by classId/objectId + * if refclassId isn't InvalidOid, it must match the entry's refclassid + * if refobjId isn't InvalidOid, it must match the entry's refobjid + * if deptype isn't SHARED_DEPENDENCY_INVALID, it must match entry's deptype + * + * sdepRel must be the pg_shdepend relation, already opened and suitably + * locked. + */ +static void +shdepDropDependency(Relation sdepRel, Oid classId, Oid objectId, + Oid refclassId, Oid refobjId, + SharedDependencyType deptype) +{ + ScanKeyData key[3]; + SysScanDesc scan; + HeapTuple tup; + + /* Scan for entries matching the dependent object */ + ScanKeyInit(&key[0], + Anum_pg_shdepend_dbid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(classIdGetDbId(classId))); + ScanKeyInit(&key[1], + Anum_pg_shdepend_classid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(classId)); + ScanKeyInit(&key[2], + Anum_pg_shdepend_objid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(objectId)); + + scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true, + SnapshotNow, 3, key); + + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_shdepend shdepForm = (Form_pg_shdepend) GETSTRUCT(tup); + + /* Filter entries according to additional parameters */ + if (OidIsValid(refclassId) && shdepForm->refclassid != refclassId) + continue; + if (OidIsValid(refobjId) && shdepForm->refobjid != refobjId) + continue; + if (deptype != SHARED_DEPENDENCY_INVALID && + shdepForm->deptype != deptype) + continue; + + /* OK, delete it */ + simple_heap_delete(sdepRel, &tup->t_self); + } + + systable_endscan(scan); +} + +/* + * classIdGetDbId + * + * Get the database Id that should be used in pg_shdepend, given the OID + * of the catalog containing the object. For shared objects, it's 0 + * (InvalidOid); for all other objects, it's the current database Id. + * + * XXX it's awfully tempting to hard-wire this instead of doing a syscache + * lookup ... but resist the temptation, unless you can prove it's a + * bottleneck. + */ +static Oid +classIdGetDbId(Oid classId) +{ + Oid dbId; + HeapTuple tup; + + tup = SearchSysCache(RELOID, + ObjectIdGetDatum(classId), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "cache lookup failed for relation %u", classId); + + if (((Form_pg_class) GETSTRUCT(tup))->relisshared) + dbId = InvalidOid; + else + dbId = MyDatabaseId; + + ReleaseSysCache(tup); + + return dbId; +} + +/* + * shdepLockAndCheckObject + * + * Lock the object that we are about to record a dependency on. + * After it's locked, verify that it hasn't been dropped while we + * weren't looking. If the object has been dropped, this function + * does not return! + */ +static void +shdepLockAndCheckObject(Oid classId, Oid objectId) +{ + /* AccessShareLock should be OK, since we are not modifying the object */ + LockSharedObject(classId, objectId, 0, AccessShareLock); + + /* + * We have to recognize sinval updates here, else our local syscache + * may still contain the object even if it was just dropped. + */ + AcceptInvalidationMessages(); + + switch (classId) + { + case AuthIdRelationId: + if (!SearchSysCacheExists(AUTHOID, + ObjectIdGetDatum(objectId), + 0, 0, 0)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("role %u was concurrently dropped", + objectId))); + break; + + /* + * Currently, this routine need not support any other shared object + * types besides roles. If we wanted to record explicit dependencies + * on databases or tablespaces, we'd need code along these lines: + */ +#ifdef NOT_USED + case TableSpaceRelationId: + { + /* For lack of a syscache on pg_tablespace, do this: */ + char *tablespace = get_tablespace_name(objectId); + + if (tablespace == NULL) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("tablespace %u was concurrently dropped", + objectId))); + pfree(tablespace); + break; + } +#endif + + default: + elog(ERROR, "unrecognized shared classId: %u", classId); + } +} + + +/* + * storeObjectDescription + * Append the description of a dependent object to "descs" + * + * While searching for dependencies of a shared object, we stash the + * descriptions of dependent objects we find in a single string, which we + * later pass to ereport() in the DETAIL field when somebody attempts to + * drop a referenced shared object. + * + * When type is LOCAL_OBJECT or SHARED_OBJECT, we expect object to be the + * dependent object, deptype is the dependency type, and count is not used. + * When type is REMOTE_OBJECT, we expect object to be the database object, + * and count to be nonzero; deptype is not used in this case. + */ +static void +storeObjectDescription(StringInfo descs, objectType type, + ObjectAddress *object, + SharedDependencyType deptype, + int count) +{ + char *objdesc = getObjectDescription(object); + + /* separate entries with a newline */ + if (descs->len != 0) + appendStringInfoChar(descs, '\n'); + + switch (type) + { + case LOCAL_OBJECT: + case SHARED_OBJECT: + if (deptype == SHARED_DEPENDENCY_OWNER) + appendStringInfo(descs, _("owner of %s"), objdesc); + else if (deptype == SHARED_DEPENDENCY_ACL) + appendStringInfo(descs, _("access to %s"), objdesc); + else + elog(ERROR, "unrecognized dependency type: %d", + (int) deptype); + break; + + case REMOTE_OBJECT: + /* translator: %s will always be "database %s" */ + appendStringInfo(descs, _("%d objects in %s"), count, objdesc); + break; + + default: + elog(ERROR, "unrecognized object type: %d", type); + } + + pfree(objdesc); +} + + +/* + * isSharedObjectPinned + * Return whether a given shared object has a SHARED_DEPENDENCY_PIN entry. + * + * sdepRel must be the pg_shdepend relation, already opened and suitably + * locked. + */ +static bool +isSharedObjectPinned(Oid classId, Oid objectId, Relation sdepRel) +{ + bool result = false; + ScanKeyData key[2]; + SysScanDesc scan; + HeapTuple tup; + + ScanKeyInit(&key[0], + Anum_pg_shdepend_refclassid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(classId)); + ScanKeyInit(&key[1], + Anum_pg_shdepend_refobjid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(objectId)); + + scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true, + SnapshotNow, 2, key); + + /* + * Since we won't generate additional pg_shdepend entries for pinned + * objects, there can be at most one entry referencing a pinned + * object. Hence, it's sufficient to look at the first returned + * tuple; we don't need to loop. + */ + tup = systable_getnext(scan); + if (HeapTupleIsValid(tup)) + { + Form_pg_shdepend shdepForm = (Form_pg_shdepend) GETSTRUCT(tup); + + if (shdepForm->deptype == SHARED_DEPENDENCY_PIN) + result = true; + } + + systable_endscan(scan); + + return result; +} diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c index fa4068bf9f..0144e10fdb 100644 --- a/src/backend/catalog/pg_type.c +++ b/src/backend/catalog/pg_type.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/pg_type.c,v 1.101 2005/06/28 05:08:52 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/pg_type.c,v 1.102 2005/07/07 20:39:57 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -75,7 +75,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace) namestrcpy(&name, typeName); values[i++] = NameGetDatum(&name); /* typname */ values[i++] = ObjectIdGetDatum(typeNamespace); /* typnamespace */ - values[i++] = ObjectIdGetDatum(InvalidOid); /* typowner */ + values[i++] = ObjectIdGetDatum(GetUserId()); /* typowner */ values[i++] = Int16GetDatum(0); /* typlen */ values[i++] = BoolGetDatum(false); /* typbyval */ values[i++] = CharGetDatum(0); /* typtype */ @@ -117,6 +117,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace) typoid, InvalidOid, 0, + GetUserId(), InvalidOid, InvalidOid, InvalidOid, @@ -330,6 +331,7 @@ TypeCreate(const char *typeName, typeObjectId, relationOid, relationKind, + GetUserId(), inputProcedure, outputProcedure, receiveProcedure, @@ -365,6 +367,7 @@ GenerateTypeDependencies(Oid typeNamespace, Oid relationOid, /* only for 'c'atalog * types */ char relationKind, /* ditto */ + Oid owner, Oid inputProcedure, Oid outputProcedure, Oid receiveProcedure, @@ -379,7 +382,10 @@ GenerateTypeDependencies(Oid typeNamespace, referenced; if (rebuild) + { deleteDependencyRecordsFor(TypeRelationId, typeObjectId); + deleteSharedDependencyRecordsFor(TypeRelationId, typeObjectId); + } myself.classId = TypeRelationId; myself.objectId = typeObjectId; @@ -485,6 +491,9 @@ GenerateTypeDependencies(Oid typeNamespace, /* Normal dependency on the default expression. */ if (defaultExpr) recordDependencyOnExpr(&myself, defaultExpr, NIL, DEPENDENCY_NORMAL); + + /* Shared dependency on owner. */ + recordDependencyOnOwner(TypeRelationId, typeObjectId, owner); } /* diff --git a/src/backend/commands/conversioncmds.c b/src/backend/commands/conversioncmds.c index c2aa48614e..9cc72ec732 100644 --- a/src/backend/commands/conversioncmds.c +++ b/src/backend/commands/conversioncmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/conversioncmds.c,v 1.19 2005/06/28 05:08:53 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/conversioncmds.c,v 1.20 2005/07/07 20:39:58 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -17,6 +17,7 @@ #include "catalog/pg_conversion.h" #include "access/heapam.h" #include "catalog/catalog.h" +#include "catalog/dependency.h" #include "catalog/indexing.h" #include "catalog/namespace.h" #include "catalog/pg_type.h" @@ -220,6 +221,10 @@ AlterConversionOwner(List *name, Oid newOwnerId) simple_heap_update(rel, &tup->t_self, tup); CatalogUpdateIndexes(rel, tup); + + /* Update owner dependency reference */ + changeDependencyOnOwner(ConversionRelationId, conversionOid, + newOwnerId); } heap_close(rel, NoLock); diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c index ba5ce9200b..486afb2e21 100644 --- a/src/backend/commands/dbcommands.c +++ b/src/backend/commands/dbcommands.c @@ -15,7 +15,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.164 2005/06/30 00:00:50 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.165 2005/07/07 20:39:58 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -28,6 +28,7 @@ #include "access/genam.h" #include "access/heapam.h" #include "catalog/catalog.h" +#include "catalog/dependency.h" #include "catalog/indexing.h" #include "catalog/pg_authid.h" #include "catalog/pg_database.h" @@ -511,6 +512,12 @@ createdb(const CreatedbStmt *stmt) /* Update indexes */ CatalogUpdateIndexes(pg_database_rel, tuple); + /* Register owner dependency */ + recordDependencyOnOwner(DatabaseRelationId, dboid, datdba); + + /* Create pg_shdepend entries for objects within database */ + copyTemplateDependencies(src_dboid, dboid); + /* Close pg_database, but keep exclusive lock till commit */ heap_close(pg_database_rel, NoLock); @@ -680,6 +687,11 @@ dropdb(const char *dbname) heap_close(pgdbrel, NoLock); /* + * Remove shared dependency references for the database. + */ + dropDatabaseDependencies(db_id); + + /* * Set flag to update flat database file at commit. */ database_file_update_needed(); @@ -951,6 +963,10 @@ AlterDatabaseOwner(const char *dbname, Oid newOwnerId) CatalogUpdateIndexes(rel, newtuple); heap_freetuple(newtuple); + + /* Update owner dependency reference */ + changeDependencyOnOwner(DatabaseRelationId, HeapTupleGetOid(tuple), + newOwnerId); } systable_endscan(scan); diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c index 3329822fe6..8867538f8e 100644 --- a/src/backend/commands/functioncmds.c +++ b/src/backend/commands/functioncmds.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.62 2005/06/28 05:08:53 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.63 2005/07/07 20:39:58 tgl Exp $ * * DESCRIPTION * These routines take the parse tree and pick out the @@ -925,6 +925,9 @@ AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId) CatalogUpdateIndexes(rel, newtuple); heap_freetuple(newtuple); + + /* Update owner dependency reference */ + changeDependencyOnOwner(ProcedureRelationId, procOid, newOwnerId); } ReleaseSysCache(tup); diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c index 3610269644..a0d0abd553 100644 --- a/src/backend/commands/opclasscmds.c +++ b/src/backend/commands/opclasscmds.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/opclasscmds.c,v 1.33 2005/06/28 05:08:53 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/opclasscmds.c,v 1.34 2005/07/07 20:39:58 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -392,6 +392,9 @@ DefineOpClass(CreateOpClassStmt *stmt) recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } + /* dependency on owner */ + recordDependencyOnOwner(OperatorClassRelationId, opclassoid, GetUserId()); + heap_close(rel, RowExclusiveLock); } @@ -962,6 +965,9 @@ AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId) simple_heap_update(rel, &tup->t_self, tup); CatalogUpdateIndexes(rel, tup); + + /* Update owner dependency reference */ + changeDependencyOnOwner(OperatorClassRelationId, amOid, newOwnerId); } heap_close(rel, NoLock); diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c index adea1b2b5d..ae93e9230e 100644 --- a/src/backend/commands/operatorcmds.c +++ b/src/backend/commands/operatorcmds.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/operatorcmds.c,v 1.22 2005/06/28 05:08:54 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/operatorcmds.c,v 1.23 2005/07/07 20:39:58 tgl Exp $ * * DESCRIPTION * The "DefineFoo" routines take the parse tree and pick out the @@ -310,6 +310,9 @@ AlterOperatorOwner(List *name, TypeName *typeName1, TypeName *typeName2, simple_heap_update(rel, &tup->t_self, tup); CatalogUpdateIndexes(rel, tup); + + /* Update owner dependency reference */ + changeDependencyOnOwner(OperatorRelationId, operOid, newOwnerId); } heap_close(rel, NoLock); diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c index d92812f1fc..c4c20ddeb3 100644 --- a/src/backend/commands/schemacmds.c +++ b/src/backend/commands/schemacmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/schemacmds.c,v 1.31 2005/06/28 05:08:54 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/schemacmds.c,v 1.32 2005/07/07 20:39:58 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -342,6 +342,10 @@ AlterSchemaOwner(const char *name, Oid newOwnerId) CatalogUpdateIndexes(rel, newtuple); heap_freetuple(newtuple); + + /* Update owner dependency reference */ + changeDependencyOnOwner(NamespaceRelationId, HeapTupleGetOid(tup), + newOwnerId); } ReleaseSysCache(tup); diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index dda9532baf..2b50a402fc 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.162 2005/06/28 05:08:54 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.163 2005/07/07 20:39:58 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -5321,6 +5321,9 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId) heap_freetuple(newtuple); + /* Update owner dependency reference */ + changeDependencyOnOwner(RelationRelationId, relationOid, newOwnerId); + /* * If we are operating on a table, also change the ownership of * any indexes and sequences that belong to the table, as well as diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c index b925825062..a2cce14335 100644 --- a/src/backend/commands/tablespace.c +++ b/src/backend/commands/tablespace.c @@ -37,7 +37,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/tablespace.c,v 1.24 2005/07/04 04:51:46 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/tablespace.c,v 1.25 2005/07/07 20:39:58 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -50,6 +50,7 @@ #include "access/heapam.h" #include "catalog/catalog.h" +#include "catalog/dependency.h" #include "catalog/indexing.h" #include "catalog/pg_namespace.h" #include "catalog/pg_tablespace.h" @@ -307,6 +308,9 @@ CreateTableSpace(CreateTableSpaceStmt *stmt) heap_freetuple(tuple); + /* Record dependency on owner */ + recordDependencyOnOwner(TableSpaceRelationId, tablespaceoid, ownerId); + /* * Attempt to coerce target directory to safe permissions. If this * fails, it doesn't exist or has the wrong owner. @@ -818,6 +822,10 @@ AlterTableSpaceOwner(const char *name, Oid newOwnerId) CatalogUpdateIndexes(rel, newtuple); heap_freetuple(newtuple); + + /* Update owner dependency reference */ + changeDependencyOnOwner(TableSpaceRelationId, HeapTupleGetOid(tup), + newOwnerId); } heap_endscan(scandesc); diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 53570c7740..e5f2a2f762 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.73 2005/06/28 05:08:54 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.74 2005/07/07 20:39:58 tgl Exp $ * * DESCRIPTION * The "DefineFoo" routines take the parse tree and pick out the @@ -1208,6 +1208,7 @@ AlterDomainDefault(List *names, Node *defaultRaw) domainoid, typTup->typrelid, 0, /* relation kind is n/a */ + typTup->typowner, typTup->typinput, typTup->typoutput, typTup->typreceive, @@ -2080,6 +2081,9 @@ AlterTypeOwner(List *names, Oid newOwnerId) simple_heap_update(rel, &tup->t_self, tup); CatalogUpdateIndexes(rel, tup); + + /* Update owner dependency reference */ + changeDependencyOnOwner(TypeRelationId, typeOid, newOwnerId); } /* Clean up */ diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c index 982c286cde..4a46343d5d 100644 --- a/src/backend/commands/user.c +++ b/src/backend/commands/user.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/backend/commands/user.c,v 1.155 2005/06/29 20:34:13 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/user.c,v 1.156 2005/07/07 20:39:58 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -14,10 +14,10 @@ #include "access/genam.h" #include "access/heapam.h" +#include "catalog/dependency.h" #include "catalog/indexing.h" #include "catalog/pg_auth_members.h" #include "catalog/pg_authid.h" -#include "catalog/pg_database.h" #include "commands/user.h" #include "libpq/crypt.h" #include "miscadmin.h" @@ -742,10 +742,8 @@ DropRole(DropRoleStmt *stmt) const char *role = strVal(lfirst(item)); HeapTuple tuple, tmp_tuple; - Relation pg_rel; - TupleDesc pg_dsc; - ScanKeyData scankey; - HeapScanDesc scan; + ScanKeyData scankey; + char *detail; SysScanDesc sscan; Oid roleid; @@ -780,42 +778,18 @@ DropRole(DropRoleStmt *stmt) errmsg("must be superuser to drop superusers"))); /* - * Check if role still owns a database. If so, error out. - * - * (It used to be that this function would drop the database - * automatically. This is not only very dangerous for people that - * don't read the manual, it doesn't seem to be the behaviour one - * would expect either.) -- petere 2000/01/14) - */ - pg_rel = heap_open(DatabaseRelationId, AccessShareLock); - pg_dsc = RelationGetDescr(pg_rel); - - ScanKeyInit(&scankey, - Anum_pg_database_datdba, - BTEqualStrategyNumber, F_OIDEQ, - roleid); - - scan = heap_beginscan(pg_rel, SnapshotNow, 1, &scankey); - - if ((tmp_tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) - { - char *dbname; + * Lock the role, so nobody can add dependencies to her while we drop + * her. We keep the lock until the end of transaction. + */ + LockSharedObject(AuthIdRelationId, roleid, 0, AccessExclusiveLock); - dbname = NameStr(((Form_pg_database) GETSTRUCT(tmp_tuple))->datname); + /* Check for pg_shdepend entries depending on this role */ + if ((detail = checkSharedDependencies(AuthIdRelationId, roleid)) != NULL) ereport(ERROR, - (errcode(ERRCODE_OBJECT_IN_USE), - errmsg("role \"%s\" cannot be dropped", role), - errdetail("The role owns database \"%s\".", dbname))); - } - - heap_endscan(scan); - heap_close(pg_rel, AccessShareLock); - - /* - * Somehow we'd have to check for tables, views, etc. owned by the - * role as well, but those could be spread out over all sorts of - * databases which we don't have access to (easily). - */ + (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST), + errmsg("role \"%s\" cannot be dropped because some objects depend on it", + role), + errdetail("%s", detail))); /* * Remove the role from the pg_authid table diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c index 5b65099696..a416def815 100644 --- a/src/backend/utils/adt/acl.c +++ b/src/backend/utils/adt/acl.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.117 2005/06/29 20:34:14 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.118 2005/07/07 20:39:58 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -59,6 +59,7 @@ static void check_circularity(const Acl *old_acl, const AclItem *mod_aip, Oid ownerId); static Acl *recursive_revoke(Acl *acl, Oid grantee, AclMode revoke_privs, Oid ownerId, DropBehavior behavior); +static int oidComparator(const void *arg1, const void *arg2); static AclMode convert_priv_string(text *priv_type_text); @@ -1053,6 +1054,86 @@ aclmask(const Acl *acl, Oid roleid, Oid ownerId, /* + * aclmembers + * Find out all the roleids mentioned in an Acl. + * Note that we do not distinguish grantors from grantees. + * + * *roleids is set to point to a palloc'd array containing distinct OIDs + * in sorted order. The length of the array is the function result. + */ +int +aclmembers(const Acl *acl, Oid **roleids) +{ + Oid *list; + const AclItem *acldat; + int i, + j, + k; + + if (acl == NULL || ACL_NUM(acl) == 0) + { + *roleids = NULL; + return 0; + } + + /* Allocate the worst-case space requirement */ + list = palloc(ACL_NUM(acl) * 2 * sizeof(Oid)); + acldat = ACL_DAT(acl); + + /* + * Walk the ACL collecting mentioned RoleIds. + */ + j = 0; + for (i = 0; i < ACL_NUM(acl); i++) + { + const AclItem *ai = &acldat[i]; + + if (ai->ai_grantee != ACL_ID_PUBLIC) + list[j++] = ai->ai_grantee; + /* grantor is currently never PUBLIC, but let's check anyway */ + if (ai->ai_grantor != ACL_ID_PUBLIC) + list[j++] = ai->ai_grantor; + } + + /* Sort the array */ + qsort(list, j, sizeof(Oid), oidComparator); + + /* Remove duplicates from the array */ + k = 0; + for (i = 1; i < j; i++) + { + if (list[k] != list[i]) + list[++k] = list[i]; + } + + /* + * We could repalloc the array down to minimum size, but it's hardly + * worth it since it's only transient memory. + */ + *roleids = list; + + return k + 1; +} + +/* + * oidComparator + * qsort comparison function for Oids + */ +static int +oidComparator(const void *arg1, const void *arg2) +{ + Oid oid1 = * (const Oid *) arg1; + Oid oid2 = * (const Oid *) arg2; + + if (oid1 > oid2) + return 1; + if (oid1 < oid2) + return -1; + return 0; +} + + +/* * aclinsert (exported function) */ Datum |