From 59d1b3d99e690734fa2a2bd0fae12b0cb1084294 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Thu, 7 Jul 2005 20:40:02 +0000 Subject: [PATCH] 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. --- doc/src/sgml/catalogs.sgml | 160 ++- src/backend/catalog/Makefile | 7 +- src/backend/catalog/aclchk.c | 127 ++- src/backend/catalog/dependency.c | 59 +- src/backend/catalog/heap.c | 6 +- src/backend/catalog/pg_conversion.c | 6 +- src/backend/catalog/pg_namespace.c | 6 +- src/backend/catalog/pg_operator.c | 7 +- src/backend/catalog/pg_proc.c | 8 +- src/backend/catalog/pg_shdepend.c | 1042 ++++++++++++++++++++ src/backend/catalog/pg_type.c | 13 +- src/backend/commands/conversioncmds.c | 7 +- src/backend/commands/dbcommands.c | 18 +- src/backend/commands/functioncmds.c | 5 +- src/backend/commands/opclasscmds.c | 8 +- src/backend/commands/operatorcmds.c | 5 +- src/backend/commands/schemacmds.c | 6 +- src/backend/commands/tablecmds.c | 5 +- src/backend/commands/tablespace.c | 10 +- src/backend/commands/typecmds.c | 6 +- src/backend/commands/user.c | 54 +- src/backend/utils/adt/acl.c | 83 +- src/bin/initdb/initdb.c | 11 +- src/include/catalog/catversion.h | 4 +- src/include/catalog/dependency.h | 66 +- src/include/catalog/indexing.h | 9 +- src/include/catalog/pg_shdepend.h | 91 ++ src/include/catalog/pg_type.h | 3 +- src/include/utils/acl.h | 3 +- src/test/regress/expected/cluster.out | 1 + src/test/regress/expected/dependency.out | 39 + src/test/regress/expected/privileges.out | 1 + src/test/regress/expected/sanity_check.out | 3 +- src/test/regress/parallel_schedule | 2 +- src/test/regress/serial_schedule | 3 +- src/test/regress/sql/cluster.sql | 1 + src/test/regress/sql/dependency.sql | 41 + src/test/regress/sql/privileges.sql | 1 + 38 files changed, 1842 insertions(+), 85 deletions(-) create mode 100644 src/backend/catalog/pg_shdepend.c create mode 100644 src/include/catalog/pg_shdepend.h create mode 100644 src/test/regress/expected/dependency.out create mode 100644 src/test/regress/sql/dependency.sql diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 7358dfb512..560a6b3f35 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -1,6 +1,6 @@ @@ -173,6 +173,11 @@ query rewrite rules + + pg_shdepend + dependencies on shared objects + + pg_statistic planner statistics @@ -1890,6 +1895,12 @@ RESTRICT case. + + See also pg_shdepend, + which performs a similar function for dependencies involving objects + that are shared across a database cluster. + + <structname>pg_depend</> Columns @@ -3024,7 +3035,7 @@ proargmodes - "char"[] + char[] An array with the modes of the function arguments, encoded as @@ -3198,6 +3209,149 @@ + + <structname>pg_shdepend</structname> + + + pg_shdepend + + + + The catalog pg_shdepend records the + dependency relationships between database objects and shared objects, + such as roles. This information allows + PostgreSQL to ensure that those objects are + unreferenced before attempting to delete them. + + + + See also pg_depend, + which performs a similar function for dependencies involving objects + within a single database. + + + + Unlike most system catalogs, pg_shdepend + is shared across all databases of a cluster: there is only one + copy of pg_shdepend per cluster, not + one per database. + + +
+ <structname>pg_shdepend</> Columns + + + + + Name + Type + References + Description + + + + + + dbid + oid + pg_database.oid + The OID of the database the dependent object is in, + or zero for a shared object + + + + classid + oid + pg_class.oid + The OID of the system catalog the dependent object is in + + + + objid + oid + any OID column + The OID of the specific dependent object + + + + refclassid + oid + pg_class.oid + The OID of the system catalog the referenced object is in + (must be a shared catalog) + + + + refobjid + oid + any OID column + The OID of the specific referenced object + + + + deptype + char + + + A code defining the specific semantics of this dependency relationship; see text. + + + + + +
+ + + In all cases, a pg_shdepend entry indicates that + the referenced object may not be dropped without also dropping the dependent + object. However, there are several subflavors identified by + deptype: + + + + SHARED_DEPENDENCY_OWNER (o) + + + The referenced object (which must be a role) is the owner of the + dependent object. + + + + + + SHARED_DEPENDENCY_ACL (a) + + + The referenced object (which must be a role) is mentioned in the + ACL (access control list, i.e., privileges list) of the + dependent object. (A SHARED_DEPENDENCY_ACL entry is + not made for the owner of the object, since the owner will have + a SHARED_DEPENDENCY_OWNER entry anyway.) + + + + + + SHARED_DEPENDENCY_PIN (p) + + + There is no dependent object; this type of entry is a signal + that the system itself depends on the referenced object, and so + that object must never be deleted. Entries of this type are + created only by initdb. The columns for the + dependent object contain zeroes. + + + + + + Other dependency flavors may be needed in future. Note in particular + that the current definition only supports roles as referenced objects. + + + + + <structname>pg_statistic</structname> @@ -4196,7 +4350,7 @@ pg_database.oid OID of the database in which the object exists, or - zero if the object is a globally-shared object, or + zero if the object is a shared object, or NULL if the object is a transaction ID
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); } @@ -584,6 +590,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); @@ -679,6 +686,11 @@ dropdb(const char *dbname) /* Close pg_database, but keep exclusive lock till commit */ heap_close(pgdbrel, NoLock); + /* + * Remove shared dependency references for the database. + */ + dropDatabaseDependencies(db_id); + /* * Set flag to update flat database file at commit. */ @@ -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); + * 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); - 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; - - 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); @@ -1052,6 +1053,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) */ diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index b61c56d721..f5ea903ca3 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -42,7 +42,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * Portions taken from FreeBSD. * - * $PostgreSQL: pgsql/src/bin/initdb/initdb.c,v 1.90 2005/07/02 17:01:50 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/initdb/initdb.c,v 1.91 2005/07/07 20:39:59 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1512,6 +1512,10 @@ setup_depend(void) * must be the only entries for their objects. */ "DELETE FROM pg_depend;\n", + "VACUUM pg_depend;\n", + "DELETE FROM pg_shdepend;\n", + "VACUUM pg_shdepend;\n", + "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' " " FROM pg_class;\n", "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' " @@ -1541,10 +1545,13 @@ setup_depend(void) "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' " " FROM pg_namespace " " WHERE nspname LIKE 'pg%';\n", + + "INSERT INTO pg_shdepend SELECT 0, 0, 0, tableoid, oid, 'p' " + " FROM pg_authid;\n", NULL }; - fputs(_("initializing pg_depend ... "), stdout); + fputs(_("initializing dependencies ... "), stdout); fflush(stdout); snprintf(cmd, sizeof(cmd), diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index a465037e4b..9a9266ff41 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.284 2005/07/01 19:19:02 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.285 2005/07/07 20:39:59 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200507011 +#define CATALOG_VERSION_NO 200507071 #endif diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h index 98f6f08832..06fdf02a10 100644 --- a/src/include/catalog/dependency.h +++ b/src/include/catalog/dependency.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/dependency.h,v 1.14 2004/12/31 22:03:24 pgsql Exp $ + * $PostgreSQL: pgsql/src/include/catalog/dependency.h,v 1.15 2005/07/07 20:39:59 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -67,6 +67,40 @@ typedef enum DependencyType DEPENDENCY_PIN = 'p' } DependencyType; +/* + * There is also a SharedDependencyType enum type that determines the exact + * semantics of an entry in pg_shdepend. Just like regular dependency entries, + * any pg_shdepend entry means that the referenced object cannot be dropped + * unless the dependent object is dropped at the same time. There are some + * additional rules however: + * + * (a) For a SHARED_DEPENDENCY_PIN entry, there is no dependent object -- + * rather, the referenced object is an essential part of the system. This + * applies to the initdb-created superuser. Entries of this type are only + * created by initdb; objects in this category don't need further pg_shdepend + * entries if more objects come to depend on them. + * + * (b) a SHARED_DEPENDENCY_OWNER entry means that the referenced object is + * the role owning the dependent object. The referenced object must be + * a pg_authid entry. + * + * (c) a SHARED_DEPENDENCY_ACL entry means that the referenced object is + * a role mentioned in the ACL field of the dependent object. The referenced + * object must be a pg_authid entry. (SHARED_DEPENDENCY_ACL entries are not + * created for the owner of an object; hence two objects may be linked by + * one or the other, but not both, of these dependency types.) + * + * SHARED_DEPENDENCY_INVALID is a value used as a parameter in internal + * routines, and is not valid in the catalog itself. + */ +typedef enum SharedDependencyType +{ + SHARED_DEPENDENCY_PIN = 'p', + SHARED_DEPENDENCY_OWNER = 'o', + SHARED_DEPENDENCY_ACL = 'a', + SHARED_DEPENDENCY_INVALID = 0 +} SharedDependencyType; + /* * The two objects related by a dependency are identified by ObjectAddresses. @@ -81,7 +115,8 @@ typedef struct ObjectAddress /* - * This enum covers all system catalogs whose OIDs can appear in classId. + * This enum covers all system catalogs whose OIDs can appear in + * pg_depend.classId or pg_shdepend.classId. */ typedef enum ObjectClass { @@ -98,6 +133,9 @@ typedef enum ObjectClass OCLASS_REWRITE, /* pg_rewrite */ OCLASS_TRIGGER, /* pg_trigger */ OCLASS_SCHEMA, /* pg_namespace */ + OCLASS_ROLE, /* pg_authid */ + OCLASS_DATABASE, /* pg_database */ + OCLASS_TBLSPACE, /* pg_tablespace */ MAX_OCLASS /* MUST BE LAST */ } ObjectClass; @@ -136,4 +174,28 @@ extern void recordMultipleDependencies(const ObjectAddress *depender, extern long deleteDependencyRecordsFor(Oid classId, Oid objectId); +/* in pg_shdepend.c */ + +extern void recordSharedDependencyOn(ObjectAddress *depender, + ObjectAddress *referenced, + SharedDependencyType deptype); + +extern void deleteSharedDependencyRecordsFor(Oid classId, Oid objectId); + +extern void recordDependencyOnOwner(Oid classId, Oid objectId, Oid owner); + +extern void changeDependencyOnOwner(Oid classId, Oid objectId, + Oid newOwnerId); + +extern void updateAclDependencies(Oid classId, Oid objectId, + Oid ownerId, bool isGrant, + int noldmembers, Oid *oldmembers, + int nnewmembers, Oid *newmembers); + +extern char *checkSharedDependencies(Oid classId, Oid objectId); + +extern void copyTemplateDependencies(Oid templateDbId, Oid newDbId); + +extern void dropDatabaseDependencies(Oid databaseId); + #endif /* DEPENDENCY_H */ diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h index 757a209579..1ab33708f0 100644 --- a/src/include/catalog/indexing.h +++ b/src/include/catalog/indexing.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/indexing.h,v 1.88 2005/06/28 05:09:04 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/indexing.h,v 1.89 2005/07/07 20:39:59 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -179,6 +179,13 @@ DECLARE_UNIQUE_INDEX(pg_rewrite_oid_index,2692, on pg_rewrite using btree(oid oi DECLARE_UNIQUE_INDEX(pg_rewrite_rel_rulename_index,2693, on pg_rewrite using btree(ev_class oid_ops, rulename name_ops)); #define RewriteRelRulenameIndexId 2693 +/* This following index is not used for a cache and is not unique */ +DECLARE_INDEX(pg_shdepend_depender_index,1232, on pg_shdepend using btree(dbid oid_ops, classid oid_ops, objid oid_ops)); +#define SharedDependDependerIndexId 1232 +/* This following index is not used for a cache and is not unique */ +DECLARE_INDEX(pg_shdepend_reference_index,1233, on pg_shdepend using btree(refclassid oid_ops, refobjid oid_ops)); +#define SharedDependReferenceIndexId 1233 + DECLARE_UNIQUE_INDEX(pg_statistic_relid_att_index,2696, on pg_statistic using btree(starelid oid_ops, staattnum int2_ops)); #define StatisticRelidAttnumIndexId 2696 diff --git a/src/include/catalog/pg_shdepend.h b/src/include/catalog/pg_shdepend.h new file mode 100644 index 0000000000..f3c905bd2f --- /dev/null +++ b/src/include/catalog/pg_shdepend.h @@ -0,0 +1,91 @@ +/*------------------------------------------------------------------------- + * + * pg_shdepend.h + * definition of the system "shared dependency" relation (pg_shdepend) + * along with the relation's initial contents. + * + * + * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * $PostgreSQL: pgsql/src/include/catalog/pg_shdepend.h,v 1.1 2005/07/07 20:39:59 tgl Exp $ + * + * NOTES + * the genbki.sh script reads this file and generates .bki + * information from the DATA() statements. + * + *------------------------------------------------------------------------- + */ +#ifndef PG_SHDEPEND_H +#define PG_SHDEPEND_H + +/* ---------------- + * postgres.h contains the system type definitions and the + * CATALOG(), BKI_BOOTSTRAP and DATA() sugar words so this file + * can be read by both genbki.sh and the C compiler. + * ---------------- + */ + +/* ---------------- + * pg_shdepend definition. cpp turns this into + * typedef struct FormData_pg_shdepend + * ---------------- + */ +#define SharedDependRelationId 1214 +CATALOG(pg_shdepend,1214) BKI_SHARED_RELATION BKI_WITHOUT_OIDS +{ + /* + * Identification of the dependent (referencing) object. + * + * These fields are all zeroes for a DEPENDENCY_PIN entry. Also, + * dbid can be zero to denote a shared object. + */ + Oid dbid; /* OID of database containing object */ + Oid classid; /* OID of table containing object */ + Oid objid; /* OID of object itself */ + + /* + * Identification of the independent (referenced) object. This is + * always a shared object, so we need no database ID field. + */ + Oid refclassid; /* OID of table containing object */ + Oid refobjid; /* OID of object itself */ + + /* + * Precise semantics of the relationship are specified by the deptype + * field. See SharedDependencyType in catalog/dependency.h. + */ + char deptype; /* see codes in dependency.h */ +} FormData_pg_shdepend; + +/* ---------------- + * Form_pg_shdepend corresponds to a pointer to a row with + * the format of pg_shdepend relation. + * ---------------- + */ +typedef FormData_pg_shdepend *Form_pg_shdepend; + +/* ---------------- + * compiler constants for pg_shdepend + * ---------------- + */ +#define Natts_pg_shdepend 6 +#define Anum_pg_shdepend_dbid 1 +#define Anum_pg_shdepend_classid 2 +#define Anum_pg_shdepend_objid 3 +#define Anum_pg_shdepend_refclassid 4 +#define Anum_pg_shdepend_refobjid 5 +#define Anum_pg_shdepend_deptype 6 + + +/* + * pg_shdepend has no preloaded contents; system-defined dependencies are + * loaded into it during a late stage of the initdb process. + * + * NOTE: we do not represent all possible dependency pairs in pg_shdepend; + * for example, there's not much value in creating an explicit dependency + * from a relation to its database. Currently, only dependencies on roles + * are explicitly stored in pg_shdepend. + */ + +#endif /* PG_SHDEPEND_H */ diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h index 44b8cf0ced..9feeccedfd 100644 --- a/src/include/catalog/pg_type.h +++ b/src/include/catalog/pg_type.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_type.h,v 1.162 2005/06/28 05:09:12 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_type.h,v 1.163 2005/07/07 20:39:59 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -582,6 +582,7 @@ extern void GenerateTypeDependencies(Oid typeNamespace, Oid typeObjectId, Oid relationOid, char relationKind, + Oid owner, Oid inputProcedure, Oid outputProcedure, Oid receiveProcedure, diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h index 0b560e062c..7defb4e1e2 100644 --- a/src/include/utils/acl.h +++ b/src/include/utils/acl.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/acl.h,v 1.80 2005/06/29 20:34:15 tgl Exp $ + * $PostgreSQL: pgsql/src/include/utils/acl.h,v 1.81 2005/07/07 20:40:00 tgl Exp $ * * NOTES * An ACL array is simply an array of AclItems, representing the union @@ -208,6 +208,7 @@ extern Acl *aclnewowner(const Acl *old_acl, Oid oldOwnerId, Oid newOwnerId); extern AclMode aclmask(const Acl *acl, Oid roleid, Oid ownerId, AclMode mask, AclMaskHow how); +extern int aclmembers(const Acl *acl, Oid **roleids); extern bool is_member_of_role(Oid member, Oid role); extern bool is_admin_of_role(Oid member, Oid role); diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 7fa83a5723..aa8e967269 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -385,5 +385,6 @@ SELECT * FROM clstr_1; -- clean up \c - DROP TABLE clstr_1; +DROP TABLE clstr_2; DROP TABLE clstr_3; DROP USER clstr_user; diff --git a/src/test/regress/expected/dependency.out b/src/test/regress/expected/dependency.out new file mode 100644 index 0000000000..4ee3e8b6a8 --- /dev/null +++ b/src/test/regress/expected/dependency.out @@ -0,0 +1,39 @@ +-- +-- DEPENDENCIES +-- +CREATE USER regression_user; +CREATE USER regression_user2; +CREATE USER regression_user3; +CREATE GROUP regression_group; +CREATE TABLE deptest (); +GRANT SELECT ON TABLE deptest TO GROUP regression_group; +GRANT ALL ON TABLE deptest TO regression_user, regression_user2; +-- can't drop neither because they have privileges somewhere +DROP USER regression_user; +ERROR: role "regression_user" cannot be dropped because some objects depend on it +DETAIL: access to table deptest +DROP GROUP regression_group; +ERROR: role "regression_group" cannot be dropped because some objects depend on it +DETAIL: access to table deptest +-- if we revoke the privileges we can drop the group +REVOKE SELECT ON deptest FROM GROUP regression_group; +DROP GROUP regression_group; +-- can't drop the user if we revoke the privileges partially +REVOKE SELECT, INSERT, UPDATE, DELETE, RULE, REFERENCES ON deptest FROM regression_user; +DROP USER regression_user; +ERROR: role "regression_user" cannot be dropped because some objects depend on it +DETAIL: access to table deptest +-- now we are OK to drop him +REVOKE TRIGGER ON deptest FROM regression_user; +DROP USER regression_user; +-- we are OK too if we drop the privileges all at once +REVOKE ALL ON deptest FROM regression_user2; +DROP USER regression_user2; +-- can't drop the owner of an object +ALTER TABLE deptest OWNER TO regression_user3; +DROP USER regression_user3; +ERROR: role "regression_user3" cannot be dropped because some objects depend on it +DETAIL: owner of table deptest +-- if we drop the object, we can drop the user too +DROP TABLE deptest; +DROP USER regression_user3; diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out index 8fa8bb18ce..293d84e8f4 100644 --- a/src/test/regress/expected/privileges.out +++ b/src/test/regress/expected/privileges.out @@ -601,6 +601,7 @@ DROP TABLE atest3; DROP TABLE atest4; DROP GROUP regressgroup1; DROP GROUP regressgroup2; +REVOKE USAGE ON LANGUAGE sql FROM regressuser1; DROP USER regressuser1; DROP USER regressuser2; DROP USER regressuser3; diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out index 3593bc356b..5c0e5ca895 100644 --- a/src/test/regress/expected/sanity_check.out +++ b/src/test/regress/expected/sanity_check.out @@ -56,6 +56,7 @@ SELECT relname, relhasindex pg_operator | t pg_proc | t pg_rewrite | t + pg_shdepend | t pg_statistic | t pg_tablespace | t pg_trigger | t @@ -65,7 +66,7 @@ SELECT relname, relhasindex shighway | t tenk1 | t tenk2 | t -(55 rows) +(56 rows) -- -- another sanity check: every system catalog that has OIDs should have diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 9a3f792732..3e52f6f558 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -68,7 +68,7 @@ test: misc # ---------- # The fifth group of parallel test # ---------- -test: select_views portals_p2 rules foreign_key cluster +test: select_views portals_p2 rules foreign_key cluster dependency # ---------- # The sixth group of parallel test diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index bb60dc0a10..bf9517fee2 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -1,4 +1,4 @@ -# $PostgreSQL: pgsql/src/test/regress/serial_schedule,v 1.27 2005/06/17 22:32:50 tgl Exp $ +# $PostgreSQL: pgsql/src/test/regress/serial_schedule,v 1.28 2005/07/07 20:40:01 tgl Exp $ # This should probably be in an order similar to parallel_schedule. test: boolean test: char @@ -98,3 +98,4 @@ test: polymorphism test: rowtypes test: stats test: tablespace +test: dependency diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index f669922b03..db300b1998 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -156,5 +156,6 @@ SELECT * FROM clstr_1; -- clean up \c - DROP TABLE clstr_1; +DROP TABLE clstr_2; DROP TABLE clstr_3; DROP USER clstr_user; diff --git a/src/test/regress/sql/dependency.sql b/src/test/regress/sql/dependency.sql new file mode 100644 index 0000000000..6d52b62dee --- /dev/null +++ b/src/test/regress/sql/dependency.sql @@ -0,0 +1,41 @@ +-- +-- DEPENDENCIES +-- + +CREATE USER regression_user; +CREATE USER regression_user2; +CREATE USER regression_user3; +CREATE GROUP regression_group; + +CREATE TABLE deptest (); + +GRANT SELECT ON TABLE deptest TO GROUP regression_group; +GRANT ALL ON TABLE deptest TO regression_user, regression_user2; + +-- can't drop neither because they have privileges somewhere +DROP USER regression_user; +DROP GROUP regression_group; + +-- if we revoke the privileges we can drop the group +REVOKE SELECT ON deptest FROM GROUP regression_group; +DROP GROUP regression_group; + +-- can't drop the user if we revoke the privileges partially +REVOKE SELECT, INSERT, UPDATE, DELETE, RULE, REFERENCES ON deptest FROM regression_user; +DROP USER regression_user; + +-- now we are OK to drop him +REVOKE TRIGGER ON deptest FROM regression_user; +DROP USER regression_user; + +-- we are OK too if we drop the privileges all at once +REVOKE ALL ON deptest FROM regression_user2; +DROP USER regression_user2; + +-- can't drop the owner of an object +ALTER TABLE deptest OWNER TO regression_user3; +DROP USER regression_user3; + +-- if we drop the object, we can drop the user too +DROP TABLE deptest; +DROP USER regression_user3; diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql index aa65bf599d..ce65fefe61 100644 --- a/src/test/regress/sql/privileges.sql +++ b/src/test/regress/sql/privileges.sql @@ -339,6 +339,7 @@ DROP TABLE atest4; DROP GROUP regressgroup1; DROP GROUP regressgroup2; +REVOKE USAGE ON LANGUAGE sql FROM regressuser1; DROP USER regressuser1; DROP USER regressuser2; DROP USER regressuser3;