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_statisticplanner statistics
***************
*** 3198,3203 ****
--- 3203,3339 ----
+
+
+
+ pg_shdepend
+
+
+ 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.
+
+
+
+ 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.
+
+
+
+
+
+
pg_statistic
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;