Index: doc/src/sgml/catalogs.sgml =================================================================== RCS file: /home/alvherre/cvs/pgsql/doc/src/sgml/catalogs.sgml,v retrieving revision 2.106 diff -c -r2.106 catalogs.sgml *** doc/src/sgml/catalogs.sgml 28 Jun 2005 05:08:50 -0000 2.106 --- doc/src/sgml/catalogs.sgml 3 Jul 2005 03:27:44 -0000 *************** *** 174,179 **** --- 174,184 ---- + pg_shdepend + cross-database dependencies between objects + + + pg_statistic planner statistics *************** *** 3198,3203 **** --- 3203,3339 ---- + + + + <structname>pg_shdepend</structname> + + + pg_shdepend + + + + The shared catalog pg_shdepend records the + dependency relationships between database objects and global objects, + such as roles and tablespaces. This information allows + DROP ROLE and DROP TABLESPACE to + ensure that those objects are unreferenced before attempting to delete them. + + + + <structname>pg_shdepend</> Columns + + + + + Name + Type + References + Description + + + + + + dbid + oid + pg_database.oid + The OID of the database the dependent object is in + + + + 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 + + + + 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_NORMAL (n) + + + A normal relationship between separately-created objects. The + dependent object may be dropped without affecting the + referenced object. Example: a table has a normal + dependency on any role mentioned in its ACL, either as grantor + or grantee. + + + + + + SHARED_DEPENDENCY_OWNER (o) + + + The referenced object (which must be a role) is the owner of the + dependent object. OWNER entries behaves the same as NORMAL entries, + as they are purely an implementation detail. + + + + + + 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. + + + +
+ + <structname>pg_statistic</structname> Index: doc/src/sgml/ref/create_group.sgml =================================================================== RCS file: /home/alvherre/cvs/pgsql/doc/src/sgml/ref/create_group.sgml,v retrieving revision 1.15 diff -c -r1.15 create_group.sgml *** doc/src/sgml/ref/create_group.sgml 4 Jan 2005 00:39:53 -0000 1.15 --- doc/src/sgml/ref/create_group.sgml 30 Jan 2005 09:51:04 -0000 *************** *** 69,77 **** The SYSID clause can be used to choose the PostgreSQL group ID of the new group. ! This is normally not necessary, but may ! be useful if you need to recreate a group referenced in the ! permissions of some object.
If this is not specified, the highest assigned group ID plus one --- 69,75 ---- The SYSID clause can be used to choose the PostgreSQL group ID of the new group. ! This is normally not necessary. If this is not specified, the highest assigned group ID plus one Index: doc/src/sgml/ref/create_user.sgml =================================================================== RCS file: /home/alvherre/cvs/pgsql/doc/src/sgml/ref/create_user.sgml,v retrieving revision 1.36 diff -c -r1.36 create_user.sgml *** doc/src/sgml/ref/create_user.sgml 6 Jan 2005 00:11:14 -0000 1.36 --- doc/src/sgml/ref/create_user.sgml 30 Jan 2005 09:51:05 -0000 *************** *** 65,73 **** The SYSID clause can be used to choose the PostgreSQL user ID of the new user. ! This is normally not necessary, but may ! be useful if you need to recreate the owner of an orphaned ! object. If this is not specified, the highest assigned user ID plus one --- 65,71 ---- The SYSID clause can be used to choose the PostgreSQL user ID of the new user. ! This is normally not necessary. If this is not specified, the highest assigned user ID plus one Index: src/backend/catalog/Makefile =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/catalog/Makefile,v retrieving revision 1.54 diff -c -r1.54 Makefile *** src/backend/catalog/Makefile 28 Jun 2005 05:08:52 -0000 1.54 --- src/backend/catalog/Makefile 2 Jul 2005 02:44:17 -0000 *************** *** 12,18 **** 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 BKIFILES = postgres.bki postgres.description --- 12,19 ---- 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_shdepend.o \ ! pg_type.o BKIFILES = postgres.bki postgres.description *************** *** 31,37 **** 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_authid.h pg_auth_members.h pg_tablespace.h pg_depend.h \ indexing.h \ ) --- 32,38 ---- 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_shdepend.h \ pg_authid.h pg_auth_members.h pg_tablespace.h pg_depend.h \ indexing.h \ ) Index: src/backend/catalog/aclchk.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/catalog/aclchk.c,v retrieving revision 1.114 diff -c -r1.114 aclchk.c *** src/backend/catalog/aclchk.c 28 Jun 2005 19:51:21 -0000 1.114 --- src/backend/catalog/aclchk.c 2 Jul 2005 02:44:17 -0000 *************** *** 19,24 **** --- 19,25 ---- #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,257 **** --- 253,262 ---- 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,354 **** --- 349,373 ---- /* 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); + + /* Update the shared dependency ACL info */ + shdependUpdateAclInfo(RelationRelationId, relOid, + ownerId, stmt->is_grant, + noldmembers, oldmembers, + nnewmembers, newmembers); + /* finished building new ACL value, now insert it */ MemSet(values, 0, sizeof(values)); MemSet(nulls, ' ', sizeof(nulls)); *************** *** 422,427 **** --- 441,450 ---- 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,513 **** --- 526,550 ---- /* 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); + + /* Update the shared dependency ACL info */ + shdependUpdateAclInfo(DatabaseRelationId, HeapTupleGetOid(tuple), + ownerId, stmt->is_grant, + noldmembers, oldmembers, + nnewmembers, newmembers); + /* finished building new ACL value, now insert it */ MemSet(values, 0, sizeof(values)); MemSet(nulls, ' ', sizeof(nulls)); *************** *** 580,585 **** --- 617,626 ---- 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,668 **** --- 699,723 ---- /* 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); + + /* Update the shared dependency ACL info */ + shdependUpdateAclInfo(ProcedureRelationId, oid, + ownerId, stmt->is_grant, + noldmembers, oldmembers, + nnewmembers, newmembers); + /* finished building new ACL value, now insert it */ MemSet(values, 0, sizeof(values)); MemSet(nulls, ' ', sizeof(nulls)); *************** *** 734,739 **** --- 789,798 ---- 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,832 **** --- 881,905 ---- /* 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); + + /* Update the shared dependency ACL info */ + shdependUpdateAclInfo(LanguageRelationId, HeapTupleGetOid(tuple), + ownerId, stmt->is_grant, + noldmembers, oldmembers, + nnewmembers, newmembers); + /* finished building new ACL value, now insert it */ MemSet(values, 0, sizeof(values)); MemSet(nulls, ' ', sizeof(nulls)); *************** *** 898,903 **** --- 971,980 ---- 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,987 **** --- 1054,1078 ---- /* 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); + + /* Update the shared dependency ACL info */ + shdependUpdateAclInfo(NamespaceRelationId, HeapTupleGetOid(tuple), + ownerId, stmt->is_grant, + noldmembers, oldmembers, + nnewmembers, newmembers); + /* finished building new ACL value, now insert it */ MemSet(values, 0, sizeof(values)); MemSet(nulls, ' ', sizeof(nulls)); *************** *** 1055,1060 **** --- 1146,1155 ---- 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,1146 **** --- 1231,1255 ---- /* 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); + + /* Update the shared dependency ACL info */ + shdependUpdateAclInfo(TableSpaceRelationId, HeapTupleGetOid(tuple), + ownerId, stmt->is_grant, + noldmembers, oldmembers, + nnewmembers, newmembers); + /* finished building new ACL value, now insert it */ MemSet(values, 0, sizeof(values)); MemSet(nulls, ' ', sizeof(nulls)); Index: src/backend/catalog/dependency.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/catalog/dependency.c,v retrieving revision 1.44 diff -c -r1.44 dependency.c *** src/backend/catalog/dependency.c 14 Apr 2005 20:03:23 -0000 1.44 --- src/backend/catalog/dependency.c 2 Jul 2005 02:44:17 -0000 *************** *** 22,30 **** --- 22,32 ---- #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,43 **** --- 34,48 ---- #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,514 **** --- 514,520 ---- break; } + /* delete the pg_depend tuple */ simple_heap_delete(depRel, &tup->t_self); } *************** *** 585,590 **** --- 591,604 ---- 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. */ *************** *** 1326,1331 **** --- 1340,1357 ---- Assert(object->objectSubId == 0); return OCLASS_TYPE; + case DatabaseRelationId: + Assert(object->objectSubId == 0); + return OCLASS_DATABASE; + + case AuthIdRelationId: + Assert(object->objectSubId == 0); + return OCLASS_AUTHID; + + case TableSpaceRelationId: + Assert(object->objectSubId == 0); + return OCLASS_TBLSPACE; + case CastRelationId: Assert(object->objectSubId == 0); return OCLASS_CAST; *************** *** 1680,1685 **** --- 1706,1742 ---- 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, gettext("tablespace %s"), tblspace); + 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, gettext("database %s"), datname); + break; + } + + case OCLASS_AUTHID: + { + appendStringInfo(&buffer, gettext("role %s"), + GetUserNameFromId(object->objectId)); + break; + } + default: appendStringInfo(&buffer, "unrecognized object %u %u %d", object->classId, Index: src/backend/catalog/heap.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/catalog/heap.c,v retrieving revision 1.285 diff -c -r1.285 heap.c *** src/backend/catalog/heap.c 5 Jun 2005 00:38:07 -0000 1.285 --- src/backend/catalog/heap.c 2 Jul 2005 02:44:17 -0000 *************** *** 37,46 **** --- 37,48 ---- #include "catalog/index.h" #include "catalog/indexing.h" #include "catalog/pg_attrdef.h" + #include "catalog/pg_authid.h" #include "catalog/pg_constraint.h" #include "catalog/pg_inherits.h" #include "catalog/pg_namespace.h" #include "catalog/pg_statistic.h" + #include "catalog/pg_tablespace.h" #include "catalog/pg_type.h" #include "commands/tablecmds.h" #include "commands/trigger.h" *************** *** 755,760 **** --- 757,764 ---- * 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 dependency links to its owner and tablespace, as needed. */ if (!IsBootstrapProcessingMode()) { *************** *** 768,773 **** --- 772,794 ---- referenced.objectId = relnamespace; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + + /* the relation depends on its tablespace, if specified */ + if (reltablespace != 0) + { + referenced.classId = TableSpaceRelationId; + referenced.objectId = reltablespace; + referenced.objectSubId = 0; + recordSharedDependencyOn(&myself, &referenced, + SHARED_DEPENDENCY_NORMAL); + } + + /* the relation depends on its owner */ + referenced.classId = AuthIdRelationId; + referenced.objectId = GetUserId(); + referenced.objectSubId = 0; + recordSharedDependencyOn(&myself, &referenced, + SHARED_DEPENDENCY_OWNER); } /* Index: src/backend/catalog/pg_conversion.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/catalog/pg_conversion.c,v retrieving revision 1.24 diff -c -r1.24 pg_conversion.c *** src/backend/catalog/pg_conversion.c 28 Jun 2005 05:08:52 -0000 1.24 --- src/backend/catalog/pg_conversion.c 2 Jul 2005 02:44:17 -0000 *************** *** 17,22 **** --- 17,23 ---- #include "access/heapam.h" #include "catalog/dependency.h" #include "catalog/indexing.h" + #include "catalog/pg_authid.h" #include "catalog/pg_conversion.h" #include "catalog/pg_proc.h" #include "catalog/namespace.h" *************** *** 120,125 **** --- 121,132 ---- referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + /* record shared dependency on owner */ + referenced.classId = AuthIdRelationId; + referenced.objectId = conowner; + referenced.objectSubId = 0; + recordSharedDependencyOn(&myself, &referenced, SHARED_DEPENDENCY_OWNER); + heap_freetuple(tup); heap_close(rel, RowExclusiveLock); Index: src/backend/catalog/pg_operator.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/catalog/pg_operator.c,v retrieving revision 1.92 diff -c -r1.92 pg_operator.c *** src/backend/catalog/pg_operator.c 28 Jun 2005 05:08:52 -0000 1.92 --- src/backend/catalog/pg_operator.c 2 Jul 2005 02:44:17 -0000 *************** *** 21,26 **** --- 21,27 ---- #include "catalog/dependency.h" #include "catalog/indexing.h" #include "catalog/namespace.h" + #include "catalog/pg_authid.h" #include "catalog/pg_namespace.h" #include "catalog/pg_operator.h" #include "catalog/pg_proc.h" *************** *** 889,894 **** --- 890,896 ---- /* 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,965 **** --- 964,973 ---- referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } + + /* Dependency on owner */ + referenced.classId = AuthIdRelationId; + referenced.objectId = oper->oprowner; + referenced.objectSubId = 0; + recordSharedDependencyOn(&myself, &referenced, SHARED_DEPENDENCY_OWNER); } Index: src/backend/catalog/pg_proc.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/catalog/pg_proc.c,v retrieving revision 1.131 diff -c -r1.131 pg_proc.c *** src/backend/catalog/pg_proc.c 28 Jun 2005 19:51:21 -0000 1.131 --- src/backend/catalog/pg_proc.c 2 Jul 2005 02:44:17 -0000 *************** *** 382,387 **** --- 382,390 ---- recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } + /* dependency on owner */ + recordDependencyOnCurrentUser(&myself); + heap_freetuple(tup); heap_close(rel, RowExclusiveLock); Index: src/backend/catalog/pg_shdepend.c =================================================================== RCS file: src/backend/catalog/pg_shdepend.c diff -N src/backend/catalog/pg_shdepend.c *** /dev/null 1 Jan 1970 00:00:00 -0000 --- src/backend/catalog/pg_shdepend.c 3 Jul 2005 03:36:12 -0000 *************** *** 0 **** --- 1,951 ---- + /*------------------------------------------------------------------------- + * + * pg_shdepend.c + * routines to support manipulation of the pg_shdepend relation + * + * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $PostgreSQL$ + * + *------------------------------------------------------------------------- + */ + #include "postgres.h" + + #include "miscadmin.h" + #include "access/genam.h" + #include "access/heapam.h" + #include "access/htup.h" + #include "access/skey.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 "catalog/pg_tablespace.h" + #include "commands/tablespace.h" + #include "lib/stringinfo.h" + #include "storage/proc.h" + #include "utils/acl.h" + #include "utils/lsyscache.h" + #include "utils/rel.h" + #include "utils/fmgroids.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(Oid classid, Oid objid, Oid refclassid, Oid refobjid, + SharedDependencyType deptype); + static void shdepAddDependency(Relation shdepRel, Oid classId, Oid objectId, + Oid refclassId, Oid refobjId, + SharedDependencyType deptype); + static void shdepDropDependency(Relation shdepRel, Oid classId, Oid objectId, + Oid refclassId, Oid refobjId, + SharedDependencyType deptype); + static void storeObjectDescription(StringInfo descs, objectType type, + ObjectAddress *object, int count); + static bool objectIsSharePinned(Oid classId, Oid objectId, Relation shdepRel); + + /* + * recordSharedDependencyOn + * + * Record a dependency between 2 objects via their respective ObjectAddresses. + * The first argument is the dependent object, the second the one it + * references. + * + * 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. + */ + void + recordSharedDependencyOn(ObjectAddress *depender, + ObjectAddress *referenced, + SharedDependencyType deptype) + { + Relation shdepRel; + + /* + * Objects in pg_shdepend can't have SubIds. + * + * XXX Is this restriction actually useful, besides saving space + * in pg_shdepend? + */ + 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; + + shdepRel = heap_open(SharedDependRelationId, RowExclusiveLock); + + /* If the object is pinned, do nothing. */ + if (!objectIsSharePinned(referenced->classId, referenced->objectId, + shdepRel)) + { + shdepAddDependency(shdepRel, depender->classId, depender->objectId, + referenced->classId, referenced->objectId, + deptype); + } + + heap_close(shdepRel, RowExclusiveLock); + } + + /* + * recordDependencyOnCurrentUser + * + * A convenient wrapper of recordSharedDependencyOn -- register the current + * user as owner of the given object. + * + * Note we don't need to check if the object is pinned, because + * recordSharedDependencyOn will do it for us. + * + * Note: it's the caller's responsability to ensure that there isn't an owner + * entry for the object already. + */ + void + recordDependencyOnCurrentUser(ObjectAddress *object) + { + ObjectAddress referenced; + + referenced.classId = AuthIdRelationId; + referenced.objectId = GetUserId(); + referenced.objectSubId = 0; + + recordSharedDependencyOn(object, &referenced, SHARED_DEPENDENCY_OWNER); + } + + /* + * shdepChangeDep + * + * Update shared dependency records to account for an updated referenced + * object. This is an internal workhorse for changing the owning role or + * tablespace. + * + * 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.) + * + * The protocol is: + * - get the previous entry, if any + * - check whether the new entry is pinned + * - if the new entry is pinned + * - if there is a previous entry + * - delete it + * - clean up and return + * - if there is a previous entry + * - update it + * - else + * - create and insert the new entry + */ + static void + shdepChangeDep(Oid classid, Oid objid, Oid refclassid, Oid refobjid, + SharedDependencyType deptype) + { + HeapTuple oldtup; + Relation sdepRel; + ScanKeyData key[3]; + SysScanDesc scan; + bool found_old = false, + new_pinned; + + /* + * Make sure the new referenced object doesn't go away while we record + * the dependency. + */ + sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock); + + /* + * Look up the previous entry + */ + 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(objid)); + + scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true, + SnapshotNow, 3, key); + + while ((oldtup = systable_getnext(scan)) != NULL) + { + /* + * We only consider entries of the given dependency type. This is + * so changing an owner does not alter other entries the object may + * have from pg_authid (roles mentioned in the ACL). In the case + * of changing tablespace, this has no effect. + */ + if (((Form_pg_shdepend) GETSTRUCT(oldtup))->deptype != deptype) + continue; + + found_old = true; + break; + } + systable_endscan(scan); + + new_pinned = objectIsSharePinned(refclassid, refobjid, sdepRel); + + if (new_pinned) + { + if (found_old) + simple_heap_delete(sdepRel, &oldtup->t_self); + + goto cleanup; + } + + if (found_old) + { + HeapTuple newtup; + Form_pg_shdepend newForm; + + newtup = heap_copytuple(oldtup); + + newForm = (Form_pg_shdepend) GETSTRUCT(newtup); + newForm->refobjid = refobjid; + + simple_heap_update(sdepRel, &oldtup->t_self, newtup); + + /* keep indexes current */ + CatalogUpdateIndexes(sdepRel, newtup); + + heap_freetuple(newtup); + } + else + { + Datum values[Natts_pg_shdepend]; + bool nulls[Natts_pg_shdepend]; + int i = 0; + + MemSet(nulls, 0, Natts_pg_shdepend); + + values[i++] = ObjectIdGetDatum(classIdGetDbId(classid));/* dbid */ + values[i++] = ObjectIdGetDatum(classid); /* classid */ + values[i++] = ObjectIdGetDatum(objid); /* objid */ + + values[i++] = ObjectIdGetDatum(refclassid); /* refclassid */ + values[i++] = ObjectIdGetDatum(refobjid); /* refobjid */ + values[i++] = CharGetDatum(deptype); /* 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); + } + + cleanup: + heap_close(sdepRel, RowExclusiveLock); + } + + /* + * shdependChangeOwner + * + * Update the shared dependencies to account for the new owner. + * + */ + void + shdependChangeOwner(Oid classId, Oid objectId, Oid newOwnerId) + { + shdepChangeDep(classId, objectId, AuthIdRelationId, newOwnerId, + SHARED_DEPENDENCY_OWNER); + } + + /* + * shdependChangeTablespace + * + * Update the shared dependencies to account for the new tablespace. + */ + void + shdependChangeTablespace(Oid classId, Oid objectId, Oid newTblspc) + { + shdepChangeDep(classId, objectId, TableSpaceRelationId, newTblspc, + SHARED_DEPENDENCY_NORMAL); + } + + /* + * getOidListDiff + * Helper for shdependUpdateAclInfo. + * + * Takes two Oid arrays and returns elements from the first not found in the + * second. We assume both arrays are de-duped. + * + * NOTE: Both input list are freed. + */ + 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 if (list1[i] > list2[j]) + { + /* can't happen */ + elog(WARNING, "invalid element %u in short 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 (nlist1 != 0) + pfree(list1); + if (nlist2 != 0) + pfree(list2); + + return k; + } + + /* + * shdependUpdateAclInfo + * Update the pg_shdepend info related to an object's 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. + */ + void + shdependUpdateAclInfo(Oid classId, Oid objectId, Oid ownerId, bool isGrant, + int noldmembers, Oid *oldmembers, + int nnewmembers, Oid *newmembers) + { + Relation shdepRel; + 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 there are no differences, return early */ + if (ndiff == 0) + return; + + Assert(ndiff > 0); + + shdepRel = 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 a special shdep entry already */ + if (roleid == ownerId) + continue; + + /* Skip grants to PUBLIC, as it cannot be dropped. */ + if (roleid == ACL_ID_PUBLIC) + continue; + + /* Skip pinned roles */ + if (objectIsSharePinned(AuthIdRelationId, roleid, shdepRel)) + continue; + + if (isGrant) + shdepAddDependency(shdepRel, classId, objectId, AuthIdRelationId, + roleid, SHARED_DEPENDENCY_NORMAL); + else + shdepDropDependency(shdepRel, classId, objectId, AuthIdRelationId, + roleid, SHARED_DEPENDENCY_NORMAL); + } + + heap_close(shdepRel, RowExclusiveLock); + } + + /* + * 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 global + * 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 + * on the current database, dependencies on global objects; and dependencies + * on objects local to other databases. We can (and do) provide descriptions + * of the two former kind 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; + List *remDeps = NIL; + ListCell *cell; + ObjectAddress object; + StringInfo descs; + + descs = makeStringInfo(); + + 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; + + 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 == MyProc->databaseId) + storeObjectDescription(descs, LOCAL_OBJECT, &object, 0); + else if (classIdGetDbId(sdepForm->classid) == InvalidOid) + storeObjectDescription(descs, SHARED_OBJECT, &object, 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); + } + } + } + + systable_endscan(scan); + heap_close(sdepRel, AccessShareLock); + + foreach(cell, remDeps) + { + remoteDep *dep = lfirst(cell); + + object.classId = DatabaseRelationId; + object.objectId = dep->dbOid; + object.objectSubId = 0; + + storeObjectDescription(descs, REMOTE_OBJECT, &object, dep->count); + } + + list_free_deep(remDeps); + + if (descs->len == 0) + 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; + bool nulls[Natts_pg_shdepend]; + bool replace[Natts_pg_shdepend]; + + /* I think this lock is OK? */ + sdepRel = heap_open(SharedDependRelationId, RowShareLock); + sdepDesc = RelationGetDescr(sdepRel); + + ScanKeyInit(&key[0], + Anum_pg_shdepend_dbid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(templateDbId)); + + scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true, + SnapshotNow, 1, key); + + indstate = CatalogOpenIndexes(sdepRel); + + values = (Datum *) palloc0(sizeof(Datum) * Natts_pg_shdepend); + MemSet(nulls, false, Natts_pg_shdepend); + MemSet(replace, false, Natts_pg_shdepend); + 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. + */ + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + HeapTuple newtup; + Form_pg_shdepend shdepForm; + + /* + * Skip PIN entries, because we don't need multiple copies of those. + */ + shdepForm = (Form_pg_shdepend) GETSTRUCT(tup); + if (shdepForm->deptype == SHARED_DEPENDENCY_PIN) + continue; + + newtup = heap_modify_tuple(tup, sdepDesc, values, nulls, replace); + simple_heap_insert(sdepRel, newtup); + + /* Keep indexes current */ + CatalogIndexInsert(indstate, newtup); + + heap_freetuple(newtup); + } + + pfree(values); + systable_endscan(scan); + CatalogCloseIndexes(indstate); + heap_close(sdepRel, RowShareLock); + } + + /* + * dropDatabaseDependencies + * + * Delete pg_shdepend entries corresponding to a database that's being + * dropped. + */ + void + dropDatabaseDependencies(Oid databaseId) + { + Relation sdepRel; + ScanKeyData key[3]; + 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 */ + deleteSharedDependencyRecordsFor(DatabaseRelationId, databaseId); + + 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 + */ + static void + shdepAddDependency(Relation shdepRel, Oid classId, Oid objectId, + Oid refclassId, Oid refobjId, SharedDependencyType deptype) + { + HeapTuple tup; + bool nulls[Natts_pg_shdepend]; + Datum values[Natts_pg_shdepend]; + int i; + + /* + * 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, Natts_pg_shdepend); + + /* + * Form the new tuple and record the dependency. + */ + i = 0; + values[i++] = ObjectIdGetDatum(classIdGetDbId(classId)); /* dbid */ + values[i++] = ObjectIdGetDatum(classId); /* classid */ + values[i++] = ObjectIdGetDatum(objectId); /* objid */ + + values[i++] = ObjectIdGetDatum(refclassId); /* refclassid */ + values[i++] = ObjectIdGetDatum(refobjId); /* refobjid */ + values[i++] = CharGetDatum(deptype); /* deptype */ + + tup = heap_form_tuple(shdepRel->rd_att, values, nulls); + simple_heap_insert(shdepRel, tup); + + /* keep indexes current */ + CatalogUpdateIndexes(shdepRel, tup); + + /* clean up */ + heap_freetuple(tup); + } + + /* + * shdepDropDependency + * Internal workhorse for deleting entries from pg_shdepend. + */ + static void + shdepDropDependency(Relation shdepRel, Oid classId, Oid objectId, + Oid refclassId, Oid refobjId, + SharedDependencyType deptype) + { + ScanKeyData key[3]; + SysScanDesc scan; + HeapTuple tup; + + 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(shdepRel, SharedDependDependerIndexId, true, + SnapshotNow, 3, key); + + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_shdepend shdepForm = (Form_pg_shdepend) GETSTRUCT(tup); + + /* + * Skip entries not matching the referenced object's class Id + * and object Id, if given. + */ + if (OidIsValid(refclassId) && shdepForm->refclassid != refclassId) + continue; + + if (OidIsValid(refobjId) && shdepForm->refobjid != refobjId) + continue; + + if (deptype != SHARED_DEPENDENCY_INVALID && + shdepForm->deptype != deptype) + continue; + + simple_heap_delete(shdepRel, &tup->t_self); + } + + systable_endscan(scan); + } + + /* + * classIdGetDbId + * + * Get the database Id that should be used in pg_shdepend. For shared + * objects, it's 0 (InvalidOid); for all other objects, it's the + * current database Id. + * + * XXX optimize this. Keep a list of already checked entries somewhere? + */ + static Oid + classIdGetDbId(Oid classId) + { + Relation class; + Oid dbId; + + /* heap_open will fail if the classId doesn't exist */ + class = heap_open(classId, AccessShareLock); + + if (class->rd_rel->relisshared) + dbId = InvalidOid; + else + dbId = MyProc->databaseId; + + heap_close(class, AccessShareLock); + + 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) + { + char *className = NULL; + char *tablespace; + + LockSharedObject(classId, objectId, 0, AccessShareLock); + + switch (classId) + { + case AuthIdRelationId: + if (SearchSysCacheExists(AUTHOID, + ObjectIdGetDatum(objectId), + 0, 0, 0)) + return; + className = "role"; + break; + case TableSpaceRelationId: + tablespace = get_tablespace_name(objectId); + if (tablespace != NULL) + { + pfree(tablespace); + return; + } + className = "tablespace"; + break; + /* + * We do not worry about pg_database entries because + * we currently don't need to lock them. + */ + default: + elog(ERROR, "unrecognized classId %u", classId); + } + + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + /* + * translator: %s is an object class (e.g. "tablespace" or + * "role", %u is a numeric identifier + */ + errmsg("%s %u was concurrently dropped", className, objectId))); + } + + + /* storeObjectDescription + * store the description of a depender object + * + * While searching for dependencies of a shared object, we stash the + * descriptions of depender objects we find in a single string, that 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 + * depender object. When it's REMOTE_OBJECT, we expect "object" to be the + * database object, and count to be nonzero. + * + * XXX what should the entry separator be? + * + * XXX What to do if there are too many entries? A gigantic error message + * hardly seems useful. + */ + static void + storeObjectDescription(StringInfo descs, objectType type, + ObjectAddress *object, int count) + { + char *temp = getObjectDescription(object); + + /* separate entries with a newline */ + if (descs->len != 0) + appendStringInfoChar(descs, '\n'); + + switch (type) + { + case LOCAL_OBJECT: + appendStringInfo(descs, "in this database: %s", temp); + break; + + case SHARED_OBJECT: + appendStringInfo(descs, "%s", temp); + break; + + case REMOTE_OBJECT: + appendStringInfo(descs, "in %s: %d objects", temp, count); + break; + + default: + elog(ERROR, "invalid object type %d", type); + + } + pfree(temp); + } + + + /* + * objectIsSharePinned + * Return whether a given global object has a SHARED_DEPENDENCY_PIN entry. + * + * shdepRel must be the pg_shdepend relation, already opened and suitably + * locked. + */ + static bool + objectIsSharePinned(Oid classId, Oid objectId, Relation shdepRel) + { + ScanKeyData key[3]; + 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(shdepRel, SharedDependReferenceIndexId, true, + SnapshotNow, 2, key); + + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_shdepend shdepForm = (Form_pg_shdepend) GETSTRUCT(tup); + + if (shdepForm->deptype == SHARED_DEPENDENCY_PIN) + { + /* no need to keep looking */ + systable_endscan(scan); + return true; + } + } + + systable_endscan(scan); + + return false; + } Index: src/backend/catalog/pg_type.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/catalog/pg_type.c,v retrieving revision 1.101 diff -c -r1.101 pg_type.c *** src/backend/catalog/pg_type.c 28 Jun 2005 05:08:52 -0000 1.101 --- src/backend/catalog/pg_type.c 2 Jul 2005 02:44:17 -0000 *************** *** 485,490 **** --- 485,493 ---- /* Normal dependency on the default expression. */ if (defaultExpr) recordDependencyOnExpr(&myself, defaultExpr, NIL, DEPENDENCY_NORMAL); + + /* Shared dependency on owner. */ + recordDependencyOnCurrentUser(&myself); } /* Index: src/backend/commands/conversioncmds.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/conversioncmds.c,v retrieving revision 1.19 diff -c -r1.19 conversioncmds.c *** src/backend/commands/conversioncmds.c 28 Jun 2005 05:08:53 -0000 1.19 --- src/backend/commands/conversioncmds.c 2 Jul 2005 02:44:17 -0000 *************** *** 17,22 **** --- 17,23 ---- #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,225 **** --- 221,228 ---- simple_heap_update(rel, &tup->t_self, tup); CatalogUpdateIndexes(rel, tup); + /* Update shared dependency reference */ + shdependChangeOwner(ConversionRelationId, conversionOid, newOwnerId); } heap_close(rel, NoLock); Index: src/backend/commands/dbcommands.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/dbcommands.c,v retrieving revision 1.164 diff -c -r1.164 dbcommands.c *** src/backend/commands/dbcommands.c 30 Jun 2005 00:00:50 -0000 1.164 --- src/backend/commands/dbcommands.c 2 Jul 2005 02:44:17 -0000 *************** *** 28,33 **** --- 28,34 ---- #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" *************** *** 362,367 **** --- 363,369 ---- /* * Iterate through all tablespaces of the template database, and copy * each one to the new database. + * XXX maybe it could be done better using pg_shdepend info. */ rel = heap_open(TableSpaceRelationId, AccessShareLock); scan = heap_beginscan(rel, SnapshotNow, 0, NULL); *************** *** 511,516 **** --- 513,546 ---- /* Update indexes */ CatalogUpdateIndexes(pg_database_rel, tuple); + /* Create pg_shdepend entries */ + copyTemplateDependencies(src_dboid, dboid); + /* Register tablespace and owner dependencies */ + { + ObjectAddress myself; + ObjectAddress referenced; + + myself.classId = DatabaseRelationId; + myself.objectId = dboid; + myself.objectSubId = 0; + + referenced.classId = AuthIdRelationId; + referenced.objectId = datdba; + referenced.objectSubId = 0; + recordSharedDependencyOn(&myself, &referenced, + SHARED_DEPENDENCY_OWNER); + + /* Skip the tablespace if it's pg_default */ + if (dst_deftablespace != DEFAULTTABLESPACE_OID) + { + referenced.classId = TableSpaceRelationId; + referenced.objectId = dst_deftablespace; + referenced.objectSubId = 0; + recordSharedDependencyOn(&myself, &referenced, + SHARED_DEPENDENCY_NORMAL); + } + } + /* Close pg_database, but keep exclusive lock till commit */ heap_close(pg_database_rel, NoLock); *************** *** 680,685 **** --- 710,720 ---- heap_close(pgdbrel, NoLock); /* + * Remove shared dependency references in the database. + */ + dropDatabaseDependencies(db_id); + + /* * Set flag to update flat database file at commit. */ database_file_update_needed(); *************** *** 951,956 **** --- 986,998 ---- CatalogUpdateIndexes(rel, newtuple); heap_freetuple(newtuple); + + /* + * Update shared dependency references + */ + shdependChangeOwner(DatabaseRelationId, + HeapTupleGetOid(tuple), + newOwnerId); } systable_endscan(scan); Index: src/backend/commands/functioncmds.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/functioncmds.c,v retrieving revision 1.62 diff -c -r1.62 functioncmds.c *** src/backend/commands/functioncmds.c 28 Jun 2005 05:08:53 -0000 1.62 --- src/backend/commands/functioncmds.c 2 Jul 2005 02:44:17 -0000 *************** *** 924,929 **** --- 924,932 ---- simple_heap_update(rel, &newtuple->t_self, newtuple); CatalogUpdateIndexes(rel, newtuple); + /* update shared dependency reference */ + shdependChangeOwner(ProcedureRelationId, procOid, newOwnerId); + heap_freetuple(newtuple); } Index: src/backend/commands/opclasscmds.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/opclasscmds.c,v retrieving revision 1.33 diff -c -r1.33 opclasscmds.c *** src/backend/commands/opclasscmds.c 28 Jun 2005 05:08:53 -0000 1.33 --- src/backend/commands/opclasscmds.c 2 Jul 2005 02:44:17 -0000 *************** *** 392,397 **** --- 392,400 ---- recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } + /* dependency on owner */ + recordDependencyOnCurrentUser(&myself); + heap_close(rel, RowExclusiveLock); } *************** *** 964,969 **** --- 967,975 ---- CatalogUpdateIndexes(rel, tup); } + /* Update shared dependency reference */ + shdependChangeOwner(OperatorClassRelationId, amOid, newOwnerId); + heap_close(rel, NoLock); heap_freetuple(tup); } Index: src/backend/commands/operatorcmds.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/operatorcmds.c,v retrieving revision 1.22 diff -c -r1.22 operatorcmds.c *** src/backend/commands/operatorcmds.c 28 Jun 2005 05:08:54 -0000 1.22 --- src/backend/commands/operatorcmds.c 2 Jul 2005 02:44:17 -0000 *************** *** 310,315 **** --- 310,320 ---- simple_heap_update(rel, &tup->t_self, tup); CatalogUpdateIndexes(rel, tup); + + /* + * Update shared dependency references. + */ + shdependChangeOwner(OperatorRelationId, operOid, newOwnerId); } heap_close(rel, NoLock); Index: src/backend/commands/schemacmds.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/schemacmds.c,v retrieving revision 1.31 diff -c -r1.31 schemacmds.c *** src/backend/commands/schemacmds.c 28 Jun 2005 05:08:54 -0000 1.31 --- src/backend/commands/schemacmds.c 3 Jul 2005 03:14:02 -0000 *************** *** 19,24 **** --- 19,25 ---- #include "catalog/dependency.h" #include "catalog/indexing.h" #include "catalog/namespace.h" + #include "catalog/pg_authid.h" #include "catalog/pg_namespace.h" #include "commands/dbcommands.h" #include "commands/schemacmds.h" *************** *** 45,50 **** --- 46,53 ---- Oid owner_uid; Oid saved_uid; AclResult aclresult; + ObjectAddress myself, + owner; saved_uid = GetUserId(); *************** *** 144,149 **** --- 147,163 ---- } } + /* Record dependency on owner*/ + myself.classId = NamespaceRelationId; + myself.objectId = namespaceId; + myself.objectSubId = 0; + + owner.classId = AuthIdRelationId; + owner.objectId = owner_uid; + owner.objectSubId = 0; + + recordSharedDependencyOn(&myself, &owner, SHARED_DEPENDENCY_OWNER); + /* Reset search path to normal state */ PopSpecialNamespace(namespaceId); *************** *** 342,347 **** --- 356,367 ---- CatalogUpdateIndexes(rel, newtuple); heap_freetuple(newtuple); + + /* + * Update shared dependency references. + */ + shdependChangeOwner(NamespaceRelationId, HeapTupleGetOid(tup), + newOwnerId); } ReleaseSysCache(tup); Index: src/backend/commands/tablecmds.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/tablecmds.c,v retrieving revision 1.162 diff -c -r1.162 tablecmds.c *** src/backend/commands/tablecmds.c 28 Jun 2005 05:08:54 -0000 1.162 --- src/backend/commands/tablecmds.c 2 Jul 2005 02:44:17 -0000 *************** *** 5322,5327 **** --- 5322,5332 ---- heap_freetuple(newtuple); /* + * Update shared dependency reference. + */ + shdependChangeOwner(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 * the table's toast table (if it has one) *************** *** 5507,5512 **** --- 5512,5520 ---- if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_TABLESPACE, tablespacename); + /* Update shared dependency reference */ + shdependChangeTablespace(RelationRelationId, tab->relid, tablespaceId); + /* Save info for Phase 3 to do the real work */ if (OidIsValid(tab->newTableSpace)) ereport(ERROR, Index: src/backend/commands/tablespace.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/tablespace.c,v retrieving revision 1.23 diff -c -r1.23 tablespace.c *** src/backend/commands/tablespace.c 28 Jun 2005 05:08:54 -0000 1.23 --- src/backend/commands/tablespace.c 3 Jul 2005 03:14:31 -0000 *************** *** 50,56 **** --- 50,58 ---- #include "access/heapam.h" #include "catalog/catalog.h" + #include "catalog/dependency.h" #include "catalog/indexing.h" + #include "catalog/pg_authid.h" #include "catalog/pg_namespace.h" #include "catalog/pg_tablespace.h" #include "commands/tablespace.h" *************** *** 336,341 **** --- 338,362 ---- set_short_version(location); /* + * Record dependency link from tablespace to owner. + * XXX maybe this should be done after the symlink is created? + */ + { + ObjectAddress myself; + ObjectAddress owner; + + myself.classId = TableSpaceRelationId; + myself.objectId = tablespaceoid; + myself.objectSubId = 0; + + owner.classId = AuthIdRelationId; + owner.objectId = ownerId; + owner.objectSubId = 0; + + recordSharedDependencyOn(&myself, &owner, SHARED_DEPENDENCY_OWNER); + } + + /* * All seems well, create the symlink */ linkloc = (char *) palloc(strlen(DataDir) + 11 + 10 + 1); *************** *** 394,399 **** --- 415,421 ---- HeapTuple tuple; ScanKeyData entry[1]; Oid tablespaceoid; + char *detail; /* don't call this in a transaction block */ PreventTransactionChain((void *) stmt, "DROP TABLESPACE"); *************** *** 435,440 **** --- 457,480 ---- tablespacename); /* + * Lock the tablespace in exclusive mode, so nobody can add dependencies to + * it while we drop it. + */ + LockSharedObject(TableSpaceRelationId, tablespaceoid, 0, + AccessExclusiveLock); + + /* Verify pg_shdepend entries mentioning this tablespace. */ + if ((detail = checkSharedDependencies(TableSpaceRelationId, tablespaceoid)) != NULL) + ereport(ERROR, + (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST), + errmsg("tablespace \"%s\" cannot be dropped because some objects depend on it", + tablespacename), + errdetail(detail))); + + /* Remove shared references from this tablespace to its owner */ + deleteSharedDependencyRecordsFor(TableSpaceRelationId, tablespaceoid); + + /* * Remove the pg_tablespace tuple (this will roll back if we fail * below) */ *************** *** 817,822 **** --- 857,867 ---- simple_heap_update(rel, &newtuple->t_self, newtuple); CatalogUpdateIndexes(rel, newtuple); + /* Update shared dependency reference. */ + shdependChangeOwner(TableSpaceRelationId, + HeapTupleGetOid(tup), + newOwnerId); + heap_freetuple(newtuple); } Index: src/backend/commands/typecmds.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/typecmds.c,v retrieving revision 1.73 diff -c -r1.73 typecmds.c *** src/backend/commands/typecmds.c 28 Jun 2005 05:08:54 -0000 1.73 --- src/backend/commands/typecmds.c 2 Jul 2005 02:44:17 -0000 *************** *** 2080,2085 **** --- 2080,2087 ---- simple_heap_update(rel, &tup->t_self, tup); CatalogUpdateIndexes(rel, tup); + + shdependChangeOwner(TypeRelationId, typeOid, newOwnerId); } /* Clean up */ Index: src/backend/commands/user.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/user.c,v retrieving revision 1.155 diff -c -r1.155 user.c *** src/backend/commands/user.c 29 Jun 2005 20:34:13 -0000 1.155 --- src/backend/commands/user.c 3 Jul 2005 02:48:20 -0000 *************** *** 14,19 **** --- 14,20 ---- #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" *************** *** 742,751 **** const char *role = strVal(lfirst(item)); HeapTuple tuple, tmp_tuple; ! Relation pg_rel; ! TupleDesc pg_dsc; ! ScanKeyData scankey; ! HeapScanDesc scan; SysScanDesc sscan; Oid roleid; --- 743,750 ---- const char *role = strVal(lfirst(item)); HeapTuple tuple, tmp_tuple; ! ScanKeyData scankey; ! char *detail; SysScanDesc sscan; Oid roleid; *************** *** 780,821 **** 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; ! dbname = NameStr(((Form_pg_database) GETSTRUCT(tmp_tuple))->datname); 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). ! */ /* * Remove the role from the pg_authid table --- 779,796 ---- errmsg("must be superuser to drop superusers"))); /* ! * 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); ! /* Verify pg_shdepend entries mentioning this user. */ ! if ((detail = checkSharedDependencies(AuthIdRelationId, roleid)) != NULL) ereport(ERROR, ! (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST), ! errmsg("role \"%s\" cannot be dropped because some objects depend on it", ! role), ! errdetail(detail))); /* * Remove the role from the pg_authid table Index: src/backend/utils/adt/acl.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/utils/adt/acl.c,v retrieving revision 1.117 diff -c -r1.117 acl.c *** src/backend/utils/adt/acl.c 29 Jun 2005 20:34:14 -0000 1.117 --- src/backend/utils/adt/acl.c 3 Jul 2005 04:56:10 -0000 *************** *** 54,59 **** --- 54,60 ---- static void putid(char *p, const char *s); static Acl *allocacl(int n); static const char *aclparse(const char *s, AclItem *aip); + static int oidComparator(const void *arg1, const void *arg2); static bool aclitem_match(const AclItem *a1, const AclItem *a2); static void check_circularity(const Acl *old_acl, const AclItem *mod_aip, Oid ownerId); *************** *** 317,322 **** --- 318,398 ---- } /* + * aclmembers + * Get the list of roleid's mentioned in an Acl. + * + * The list is reported as a sorted array of unique Oids, the length of + * which is returned. + */ + int + aclmembers(const Acl *acl, Oid **roleids) + { + int i, + j; + AclItem *acldat; + Oid *list; + + if (acl == NULL || ACL_NUM(acl) == 0) + { + *roleids = NULL; + return 0; + } + + list = palloc(ACL_NUM(acl) * 2 * sizeof(Oid)); + acldat = ACL_DAT(acl); + + /* + * Walk the ACL collecting mentioned RoleIds. + */ + for (i = 0; i < ACL_NUM(acl); i++) + { + AclItem ai = acldat[i]; + + list[i * 2] = ai.ai_grantee; + list[i * 2 + 1] = ai.ai_grantor; + } + + /* sort the array */ + qsort(list, ACL_NUM(acl) * 2, sizeof(Oid), oidComparator); + + /* Remove duplicates from the list */ + for (j = 0, i = 1; i < ACL_NUM(acl) * 2; i++) + { + if (list[j] != list[i]) + { + list[j + 1] = list[i]; + j++; + } + } + + /* + * We are leaking a bit of memory, because there are more elements + * allocated than really necessary, but we don't care because it will + * be released right away. + */ + *roleids = list; + + return j + 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; + } + + /* * allocacl * Allocates storage for a new Acl with 'n' entries. * Index: src/bin/initdb/initdb.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/bin/initdb/initdb.c,v retrieving revision 1.88 diff -c -r1.88 initdb.c *** src/bin/initdb/initdb.c 28 Jun 2005 15:38:12 -0000 1.88 --- src/bin/initdb/initdb.c 2 Jul 2005 02:27:00 -0000 *************** *** 173,178 **** --- 173,179 ---- static void get_set_pwd(void); static void unlimit_systables(void); static void setup_depend(void); + static void setup_shared_depend(void); static void setup_sysviews(void); static void setup_description(void); static void setup_conversion(void); *************** *** 1562,1567 **** --- 1563,1612 ---- check_ok(); } + static void + setup_shared_depend(void) + { + PG_CMD_DECL; + char **line; + static char *pg_shdepend_setup[] = { + /* + * Fill pg_shdepend with info about existant objects. + * It's enough to record PIN entries for existant roles + * and tablespaces. + * + * Note that we first clear the table to rid of dependencies recorded + * by normal operation so far. + */ + "DELETE FROM pg_shdepend\n", + + /* Entries for owners of database-local objects */ + "INSERT INTO pg_shdepend SELECT 0, 0, 0, tableoid, oid, 'p' " + " FROM pg_authid\n", + "INSERT INTO pg_shdepend SELECT 0, 0, 0, tableoid, oid, 'p' " + " FROM pg_tablespace\n", + NULL + }; + + + fputs(_("initializing pg_shdepend ... "), stdout); + fflush(stdout); + + snprintf(cmd, sizeof(cmd), + "\"%s\" %s template1 >%s", + backend_exec, backend_options, + DEVNULL); + + PG_CMD_OPEN; + + for (line = pg_shdepend_setup; *line != NULL; line++) + PG_CMD_PUTS(*line); + + PG_CMD_CLOSE; + + check_ok(); + } + + /* * set up system views */ *************** *** 2631,2636 **** --- 2676,2683 ---- setup_depend(); + setup_shared_depend(); + setup_sysviews(); setup_description(); Index: src/include/catalog/dependency.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/catalog/dependency.h,v retrieving revision 1.14 diff -c -r1.14 dependency.h *** src/include/catalog/dependency.h 31 Dec 2004 22:03:24 -0000 1.14 --- src/include/catalog/dependency.h 2 Jul 2005 00:16:31 -0000 *************** *** 67,72 **** --- 67,101 ---- 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 if + * the depender object is not 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 to the system. This + * applies to the pg_default and pg_global namespaces, and 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 user is the owner of + * an object. Only pg_authid objects may be marked with such an entry. + * + * (d) a SHARED_DEPENDENCY_NORMAL entry is any other entry. + * + * 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_NORMAL = 'n', + SHARED_DEPENDENCY_OWNER = 'o', + SHARED_DEPENDENCY_PIN = 'p', + SHARED_DEPENDENCY_INVALID = '\0' + } SharedDependencyType; /* * The two objects related by a dependency are identified by ObjectAddresses. *************** *** 81,87 **** /* ! * This enum covers all system catalogs whose OIDs can appear in classId. */ typedef enum ObjectClass { --- 110,117 ---- /* ! * This enum covers all system catalogs whose OIDs can appear in ! * pg_depend.classId or pg_shdepend.classId. */ typedef enum ObjectClass { *************** *** 98,103 **** --- 128,137 ---- OCLASS_REWRITE, /* pg_rewrite */ OCLASS_TRIGGER, /* pg_trigger */ OCLASS_SCHEMA, /* pg_namespace */ + /* shared system catalogs: */ + OCLASS_TBLSPACE, /* pg_tablespace */ + OCLASS_DATABASE, /* pg_database */ + OCLASS_AUTHID, /* pg_authid */ MAX_OCLASS /* MUST BE LAST */ } ObjectClass; *************** *** 136,139 **** --- 170,202 ---- extern long deleteDependencyRecordsFor(Oid classId, Oid objectId); + /* in pg_shdepend.c */ + + extern void recordSharedDependencyOn(ObjectAddress *depender, + ObjectAddress *referenced, + SharedDependencyType deptype); + + extern void recordDependencyOnCurrentUser(ObjectAddress *depender); + + extern void shdependChangeOwner(Oid classId, Oid objectId, + Oid newOwnerId); + + extern void shdependChangeTablespace(Oid classId, Oid objectId, + Oid newTblspc); + + extern void shdependUpdateAclInfo(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); + + extern void deleteSharedDependencyRecordsFor(Oid classId, Oid objectId); + + extern void deleteGlobalDependencyRecordsFor(Oid classId, Oid objectId); + #endif /* DEPENDENCY_H */ Index: src/include/catalog/indexing.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/catalog/indexing.h,v retrieving revision 1.88 diff -c -r1.88 indexing.h *** src/include/catalog/indexing.h 28 Jun 2005 05:09:04 -0000 1.88 --- src/include/catalog/indexing.h 30 Jun 2005 03:30:06 -0000 *************** *** 179,184 **** --- 179,191 ---- 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,2705, on pg_shdepend using btree(dbid oid_ops, classid oid_ops, objid oid_ops)); + #define SharedDependDependerIndexId 2705 + /* This following index is not used for a cache and is not unique */ + DECLARE_INDEX(pg_shdepend_reference_index,2706, on pg_shdepend using btree(refclassid oid_ops, refobjid oid_ops)); + #define SharedDependReferenceIndexId 2706 + DECLARE_UNIQUE_INDEX(pg_statistic_relid_att_index,2696, on pg_statistic using btree(starelid oid_ops, staattnum int2_ops)); #define StatisticRelidAttnumIndexId 2696 Index: src/include/catalog/pg_shdepend.h =================================================================== RCS file: src/include/catalog/pg_shdepend.h diff -N src/include/catalog/pg_shdepend.h *** /dev/null 1 Jan 1970 00:00:00 -0000 --- src/include/catalog/pg_shdepend.h 3 Jul 2005 03:38:37 -0000 *************** *** 0 **** --- 1,84 ---- + /*------------------------------------------------------------------------- + * + * pg_shdepend.h + * definition of the system "shared dependency" relation (pg_shdepend) + * along with the relation's initial contents. + * + * + * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * $PostgreSQL$ + * + * 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(), 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 1038 + CATALOG(pg_shdepend,1038) BKI_SHARED_RELATION BKI_WITHOUT_OIDS + { + /* + * Identification of the dependent (referencing) object. + * + * These fields are all zeroes for a DEPENDENCY_PIN entry. + */ + 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. + */ + 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. + */ + + #endif /* PG_SHDEPEND_H */ Index: src/include/utils/acl.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/utils/acl.h,v retrieving revision 1.80 diff -c -r1.80 acl.h *** src/include/utils/acl.h 29 Jun 2005 20:34:15 -0000 1.80 --- src/include/utils/acl.h 30 Jun 2005 22:26:49 -0000 *************** *** 208,213 **** --- 208,214 ---- extern AclMode aclmask(const Acl *acl, Oid roleid, Oid ownerId, AclMode mask, AclMaskHow how); + extern int aclmembers(const Acl *acl, Oid **roleid); extern bool is_member_of_role(Oid member, Oid role); extern bool is_admin_of_role(Oid member, Oid role); Index: src/test/regress/parallel_schedule =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/test/regress/parallel_schedule,v retrieving revision 1.28 diff -c -r1.28 parallel_schedule *** src/test/regress/parallel_schedule 17 Jun 2005 22:32:50 -0000 1.28 --- src/test/regress/parallel_schedule 30 Jun 2005 03:17:42 -0000 *************** *** 68,74 **** # ---------- # The fifth group of parallel test # ---------- ! test: select_views portals_p2 rules foreign_key cluster # ---------- # The sixth group of parallel test --- 68,74 ---- # ---------- # The fifth group of parallel test # ---------- ! test: select_views portals_p2 rules foreign_key cluster dependency # ---------- # The sixth group of parallel test Index: src/test/regress/serial_schedule =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/test/regress/serial_schedule,v retrieving revision 1.27 diff -c -r1.27 serial_schedule *** src/test/regress/serial_schedule 17 Jun 2005 22:32:50 -0000 1.27 --- src/test/regress/serial_schedule 30 Jun 2005 03:17:42 -0000 *************** *** 98,100 **** --- 98,101 ---- test: rowtypes test: stats test: tablespace + test: dependency Index: src/test/regress/expected/cluster.out =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/test/regress/expected/cluster.out,v retrieving revision 1.16 diff -c -r1.16 cluster.out *** src/test/regress/expected/cluster.out 10 Jun 2004 17:56:01 -0000 1.16 --- src/test/regress/expected/cluster.out 31 Jan 2005 01:23:06 -0000 *************** *** 385,389 **** --- 385,390 ---- -- clean up \c - DROP TABLE clstr_1; + DROP TABLE clstr_2; DROP TABLE clstr_3; DROP USER clstr_user; Index: src/test/regress/expected/dependency.out =================================================================== RCS file: src/test/regress/expected/dependency.out diff -N src/test/regress/expected/dependency.out *** /dev/null 1 Jan 1970 00:00:00 -0000 --- src/test/regress/expected/dependency.out 3 Jul 2005 03:04:48 -0000 *************** *** 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: in this database: table deptest + DROP GROUP regression_group; + ERROR: role "regression_group" cannot be dropped because some objects depend on it + DETAIL: in this database: 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: in this database: 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: in this database: table deptest + -- if we drop the object, we can drop the user too + DROP TABLE deptest; + DROP USER regression_user3; Index: src/test/regress/expected/privileges.out =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/test/regress/expected/privileges.out,v retrieving revision 1.30 diff -c -r1.30 privileges.out *** src/test/regress/expected/privileges.out 28 Jun 2005 05:09:14 -0000 1.30 --- src/test/regress/expected/privileges.out 30 Jun 2005 03:17:42 -0000 *************** *** 601,606 **** --- 601,607 ---- 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; Index: src/test/regress/expected/sanity_check.out =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/test/regress/expected/sanity_check.out,v retrieving revision 1.25 diff -c -r1.25 sanity_check.out *** src/test/regress/expected/sanity_check.out 28 Jun 2005 05:09:14 -0000 1.25 --- src/test/regress/expected/sanity_check.out 3 Jul 2005 03:39:19 -0000 *************** *** 55,60 **** --- 55,61 ---- pg_operator | t pg_proc | t pg_rewrite | t + pg_shdepend | t pg_statistic | t pg_tablespace | t pg_trigger | t *************** *** 63,69 **** shighway | t tenk1 | t tenk2 | t ! (53 rows) -- -- another sanity check: every system catalog that has OIDs should have --- 64,70 ---- shighway | t tenk1 | t tenk2 | t ! (54 rows) -- -- another sanity check: every system catalog that has OIDs should have Index: src/test/regress/output/tablespace.source =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/test/regress/output/tablespace.source,v retrieving revision 1.2 diff -c -r1.2 tablespace.source *** src/test/regress/output/tablespace.source 5 Nov 2004 19:17:13 -0000 1.2 --- src/test/regress/output/tablespace.source 9 Mar 2005 03:25:43 -0000 *************** *** 30,36 **** ERROR: tablespace "nosuchspace" does not exist -- Fail, not empty DROP TABLESPACE testspace; ! ERROR: tablespace "testspace" is not empty DROP SCHEMA testschema CASCADE; NOTICE: drop cascades to table testschema.foo -- Should succeed --- 30,37 ---- ERROR: tablespace "nosuchspace" does not exist -- Fail, not empty DROP TABLESPACE testspace; ! ERROR: tablespace "testspace" cannot be dropped because some objects depend on it ! DETAIL: in this database: table testschema.foo DROP SCHEMA testschema CASCADE; NOTICE: drop cascades to table testschema.foo -- Should succeed Index: src/test/regress/sql/cluster.sql =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/test/regress/sql/cluster.sql,v retrieving revision 1.8 diff -c -r1.8 cluster.sql *** src/test/regress/sql/cluster.sql 2 Jun 2004 21:01:10 -0000 1.8 --- src/test/regress/sql/cluster.sql 31 Jan 2005 00:59:44 -0000 *************** *** 156,160 **** --- 156,161 ---- -- clean up \c - DROP TABLE clstr_1; + DROP TABLE clstr_2; DROP TABLE clstr_3; DROP USER clstr_user; Index: src/test/regress/sql/dependency.sql =================================================================== RCS file: src/test/regress/sql/dependency.sql diff -N src/test/regress/sql/dependency.sql *** /dev/null 1 Jan 1970 00:00:00 -0000 --- src/test/regress/sql/dependency.sql 31 Jan 2005 01:22:32 -0000 *************** *** 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; Index: src/test/regress/sql/privileges.sql =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/test/regress/sql/privileges.sql,v retrieving revision 1.15 diff -c -r1.15 privileges.sql *** src/test/regress/sql/privileges.sql 28 Jun 2005 05:09:14 -0000 1.15 --- src/test/regress/sql/privileges.sql 30 Jun 2005 03:17:43 -0000 *************** *** 339,344 **** --- 339,345 ---- DROP GROUP regressgroup1; DROP GROUP regressgroup2; + REVOKE USAGE ON LANGUAGE sql FROM regressuser1; DROP USER regressuser1; DROP USER regressuser2; DROP USER regressuser3;