aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2005-07-07 20:40:02 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2005-07-07 20:40:02 +0000
commit59d1b3d99e690734fa2a2bd0fae12b0cb1084294 (patch)
treeff074126ccc1a6342de38737d79e218f22fb9be4 /src
parent442b59dd8bc4b0efa1d733690f6ba9dae3f61b1f (diff)
downloadpostgresql-59d1b3d99e690734fa2a2bd0fae12b0cb1084294.tar.gz
postgresql-59d1b3d99e690734fa2a2bd0fae12b0cb1084294.zip
Track dependencies on shared objects (which is to say, roles; we already
have adequate mechanisms for tracking the contents of databases and tablespaces). This solves the longstanding problem that you can drop a user who still owns objects and/or has access permissions. Alvaro Herrera, with some kibitzing from Tom Lane.
Diffstat (limited to 'src')
-rw-r--r--src/backend/catalog/Makefile7
-rw-r--r--src/backend/catalog/aclchk.c127
-rw-r--r--src/backend/catalog/dependency.c59
-rw-r--r--src/backend/catalog/heap.c6
-rw-r--r--src/backend/catalog/pg_conversion.c6
-rw-r--r--src/backend/catalog/pg_namespace.c6
-rw-r--r--src/backend/catalog/pg_operator.c7
-rw-r--r--src/backend/catalog/pg_proc.c8
-rw-r--r--src/backend/catalog/pg_shdepend.c1042
-rw-r--r--src/backend/catalog/pg_type.c13
-rw-r--r--src/backend/commands/conversioncmds.c7
-rw-r--r--src/backend/commands/dbcommands.c18
-rw-r--r--src/backend/commands/functioncmds.c5
-rw-r--r--src/backend/commands/opclasscmds.c8
-rw-r--r--src/backend/commands/operatorcmds.c5
-rw-r--r--src/backend/commands/schemacmds.c6
-rw-r--r--src/backend/commands/tablecmds.c5
-rw-r--r--src/backend/commands/tablespace.c10
-rw-r--r--src/backend/commands/typecmds.c6
-rw-r--r--src/backend/commands/user.c54
-rw-r--r--src/backend/utils/adt/acl.c83
-rw-r--r--src/bin/initdb/initdb.c11
-rw-r--r--src/include/catalog/catversion.h4
-rw-r--r--src/include/catalog/dependency.h66
-rw-r--r--src/include/catalog/indexing.h9
-rw-r--r--src/include/catalog/pg_shdepend.h91
-rw-r--r--src/include/catalog/pg_type.h3
-rw-r--r--src/include/utils/acl.h3
-rw-r--r--src/test/regress/expected/cluster.out1
-rw-r--r--src/test/regress/expected/dependency.out39
-rw-r--r--src/test/regress/expected/privileges.out1
-rw-r--r--src/test/regress/expected/sanity_check.out3
-rw-r--r--src/test/regress/parallel_schedule2
-rw-r--r--src/test/regress/serial_schedule3
-rw-r--r--src/test/regress/sql/cluster.sql1
-rw-r--r--src/test/regress/sql/dependency.sql41
-rw-r--r--src/test/regress/sql/privileges.sql1
37 files changed, 1685 insertions, 82 deletions
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 09addc4af1c..fdcb2ff5a59 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -2,7 +2,7 @@
#
# Makefile for backend/catalog
#
-# $PostgreSQL: pgsql/src/backend/catalog/Makefile,v 1.54 2005/06/28 05:08:52 tgl Exp $
+# $PostgreSQL: pgsql/src/backend/catalog/Makefile,v 1.55 2005/07/07 20:39:57 tgl Exp $
#
#-------------------------------------------------------------------------
@@ -12,7 +12,8 @@ include $(top_builddir)/src/Makefile.global
OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \
pg_aggregate.o pg_constraint.o pg_conversion.o pg_depend.o \
- pg_largeobject.o pg_namespace.o pg_operator.o pg_proc.o pg_type.o
+ pg_largeobject.o pg_namespace.o pg_operator.o pg_proc.o pg_shdepend.o \
+ pg_type.o
BKIFILES = postgres.bki postgres.description
@@ -31,7 +32,7 @@ POSTGRES_BKI_SRCS := $(addprefix $(top_srcdir)/src/include/catalog/,\
pg_operator.h pg_opclass.h pg_am.h pg_amop.h pg_amproc.h \
pg_language.h pg_largeobject.h pg_aggregate.h pg_statistic.h \
pg_rewrite.h pg_trigger.h pg_listener.h pg_description.h pg_cast.h \
- pg_namespace.h pg_conversion.h pg_database.h \
+ pg_namespace.h pg_conversion.h pg_database.h pg_shdepend.h \
pg_authid.h pg_auth_members.h pg_tablespace.h pg_depend.h \
indexing.h \
)
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index f095ef24d6b..8053ca73bbf 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.114 2005/06/28 19:51:21 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.115 2005/07/07 20:39:57 tgl Exp $
*
* NOTES
* See acl.h.
@@ -19,6 +19,7 @@
#include "access/heapam.h"
#include "catalog/catalog.h"
+#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/pg_auth_members.h"
@@ -252,6 +253,10 @@ ExecuteGrantStmt_Relation(GrantStmt *stmt)
Datum values[Natts_pg_class];
char nulls[Natts_pg_class];
char replaces[Natts_pg_class];
+ int noldmembers;
+ int nnewmembers;
+ Oid *oldmembers;
+ Oid *newmembers;
/* open pg_class */
relation = heap_open(RelationRelationId, RowExclusiveLock);
@@ -344,11 +349,19 @@ ExecuteGrantStmt_Relation(GrantStmt *stmt)
/* get a detoasted copy of the ACL */
old_acl = DatumGetAclPCopy(aclDatum);
+ /*
+ * We need the members of both old and new ACLs so we can correct
+ * the shared dependency information.
+ */
+ noldmembers = aclmembers(old_acl, &oldmembers);
+
new_acl = merge_acl_with_grant(old_acl, stmt->is_grant,
stmt->grant_option, stmt->behavior,
stmt->grantees, this_privileges,
grantorId, ownerId);
+ nnewmembers = aclmembers(new_acl, &newmembers);
+
/* finished building new ACL value, now insert it */
MemSet(values, 0, sizeof(values));
MemSet(nulls, ' ', sizeof(nulls));
@@ -359,13 +372,19 @@ ExecuteGrantStmt_Relation(GrantStmt *stmt)
newtuple = heap_modifytuple(tuple, RelationGetDescr(relation), values, nulls, replaces);
- ReleaseSysCache(tuple);
-
simple_heap_update(relation, &newtuple->t_self, newtuple);
/* keep the catalog indexes up to date */
CatalogUpdateIndexes(relation, newtuple);
+ /* Update the shared dependency ACL info */
+ updateAclDependencies(RelationRelationId, relOid,
+ ownerId, stmt->is_grant,
+ noldmembers, oldmembers,
+ nnewmembers, newmembers);
+
+ ReleaseSysCache(tuple);
+
pfree(new_acl);
heap_close(relation, RowExclusiveLock);
@@ -422,6 +441,10 @@ ExecuteGrantStmt_Database(GrantStmt *stmt)
Datum values[Natts_pg_database];
char nulls[Natts_pg_database];
char replaces[Natts_pg_database];
+ int noldmembers;
+ int nnewmembers;
+ Oid *oldmembers;
+ Oid *newmembers;
relation = heap_open(DatabaseRelationId, RowExclusiveLock);
ScanKeyInit(&entry[0],
@@ -503,11 +526,19 @@ ExecuteGrantStmt_Database(GrantStmt *stmt)
/* get a detoasted copy of the ACL */
old_acl = DatumGetAclPCopy(aclDatum);
+ /*
+ * We need the members of both old and new ACLs so we can correct
+ * the shared dependency information.
+ */
+ noldmembers = aclmembers(old_acl, &oldmembers);
+
new_acl = merge_acl_with_grant(old_acl, stmt->is_grant,
stmt->grant_option, stmt->behavior,
stmt->grantees, this_privileges,
grantorId, ownerId);
+ nnewmembers = aclmembers(new_acl, &newmembers);
+
/* finished building new ACL value, now insert it */
MemSet(values, 0, sizeof(values));
MemSet(nulls, ' ', sizeof(nulls));
@@ -523,6 +554,12 @@ ExecuteGrantStmt_Database(GrantStmt *stmt)
/* keep the catalog indexes up to date */
CatalogUpdateIndexes(relation, newtuple);
+ /* Update the shared dependency ACL info */
+ updateAclDependencies(DatabaseRelationId, HeapTupleGetOid(tuple),
+ ownerId, stmt->is_grant,
+ noldmembers, oldmembers,
+ nnewmembers, newmembers);
+
pfree(new_acl);
heap_endscan(scan);
@@ -580,6 +617,10 @@ ExecuteGrantStmt_Function(GrantStmt *stmt)
Datum values[Natts_pg_proc];
char nulls[Natts_pg_proc];
char replaces[Natts_pg_proc];
+ int noldmembers;
+ int nnewmembers;
+ Oid *oldmembers;
+ Oid *newmembers;
oid = LookupFuncNameTypeNames(func->funcname, func->funcargs, false);
@@ -658,11 +699,19 @@ ExecuteGrantStmt_Function(GrantStmt *stmt)
/* get a detoasted copy of the ACL */
old_acl = DatumGetAclPCopy(aclDatum);
+ /*
+ * We need the members of both old and new ACLs so we can correct
+ * the shared dependency information.
+ */
+ noldmembers = aclmembers(old_acl, &oldmembers);
+
new_acl = merge_acl_with_grant(old_acl, stmt->is_grant,
stmt->grant_option, stmt->behavior,
stmt->grantees, this_privileges,
grantorId, ownerId);
+ nnewmembers = aclmembers(new_acl, &newmembers);
+
/* finished building new ACL value, now insert it */
MemSet(values, 0, sizeof(values));
MemSet(nulls, ' ', sizeof(nulls));
@@ -673,13 +722,19 @@ ExecuteGrantStmt_Function(GrantStmt *stmt)
newtuple = heap_modifytuple(tuple, RelationGetDescr(relation), values, nulls, replaces);
- ReleaseSysCache(tuple);
-
simple_heap_update(relation, &newtuple->t_self, newtuple);
/* keep the catalog indexes up to date */
CatalogUpdateIndexes(relation, newtuple);
+ /* Update the shared dependency ACL info */
+ updateAclDependencies(ProcedureRelationId, oid,
+ ownerId, stmt->is_grant,
+ noldmembers, oldmembers,
+ nnewmembers, newmembers);
+
+ ReleaseSysCache(tuple);
+
pfree(new_acl);
heap_close(relation, RowExclusiveLock);
@@ -734,6 +789,10 @@ ExecuteGrantStmt_Language(GrantStmt *stmt)
Datum values[Natts_pg_language];
char nulls[Natts_pg_language];
char replaces[Natts_pg_language];
+ int noldmembers;
+ int nnewmembers;
+ Oid *oldmembers;
+ Oid *newmembers;
relation = heap_open(LanguageRelationId, RowExclusiveLock);
tuple = SearchSysCache(LANGNAME,
@@ -822,11 +881,19 @@ ExecuteGrantStmt_Language(GrantStmt *stmt)
/* get a detoasted copy of the ACL */
old_acl = DatumGetAclPCopy(aclDatum);
+ /*
+ * We need the members of both old and new ACLs so we can correct
+ * the shared dependency information.
+ */
+ noldmembers = aclmembers(old_acl, &oldmembers);
+
new_acl = merge_acl_with_grant(old_acl, stmt->is_grant,
stmt->grant_option, stmt->behavior,
stmt->grantees, this_privileges,
grantorId, ownerId);
+ nnewmembers = aclmembers(new_acl, &newmembers);
+
/* finished building new ACL value, now insert it */
MemSet(values, 0, sizeof(values));
MemSet(nulls, ' ', sizeof(nulls));
@@ -837,13 +904,19 @@ ExecuteGrantStmt_Language(GrantStmt *stmt)
newtuple = heap_modifytuple(tuple, RelationGetDescr(relation), values, nulls, replaces);
- ReleaseSysCache(tuple);
-
simple_heap_update(relation, &newtuple->t_self, newtuple);
/* keep the catalog indexes up to date */
CatalogUpdateIndexes(relation, newtuple);
+ /* Update the shared dependency ACL info */
+ updateAclDependencies(LanguageRelationId, HeapTupleGetOid(tuple),
+ ownerId, stmt->is_grant,
+ noldmembers, oldmembers,
+ nnewmembers, newmembers);
+
+ ReleaseSysCache(tuple);
+
pfree(new_acl);
heap_close(relation, RowExclusiveLock);
@@ -898,6 +971,10 @@ ExecuteGrantStmt_Namespace(GrantStmt *stmt)
Datum values[Natts_pg_namespace];
char nulls[Natts_pg_namespace];
char replaces[Natts_pg_namespace];
+ int noldmembers;
+ int nnewmembers;
+ Oid *oldmembers;
+ Oid *newmembers;
relation = heap_open(NamespaceRelationId, RowExclusiveLock);
tuple = SearchSysCache(NAMESPACENAME,
@@ -977,11 +1054,19 @@ ExecuteGrantStmt_Namespace(GrantStmt *stmt)
/* get a detoasted copy of the ACL */
old_acl = DatumGetAclPCopy(aclDatum);
+ /*
+ * We need the members of both old and new ACLs so we can correct
+ * the shared dependency information.
+ */
+ noldmembers = aclmembers(old_acl, &oldmembers);
+
new_acl = merge_acl_with_grant(old_acl, stmt->is_grant,
stmt->grant_option, stmt->behavior,
stmt->grantees, this_privileges,
grantorId, ownerId);
+ nnewmembers = aclmembers(new_acl, &newmembers);
+
/* finished building new ACL value, now insert it */
MemSet(values, 0, sizeof(values));
MemSet(nulls, ' ', sizeof(nulls));
@@ -992,13 +1077,19 @@ ExecuteGrantStmt_Namespace(GrantStmt *stmt)
newtuple = heap_modifytuple(tuple, RelationGetDescr(relation), values, nulls, replaces);
- ReleaseSysCache(tuple);
-
simple_heap_update(relation, &newtuple->t_self, newtuple);
/* keep the catalog indexes up to date */
CatalogUpdateIndexes(relation, newtuple);
+ /* Update the shared dependency ACL info */
+ updateAclDependencies(NamespaceRelationId, HeapTupleGetOid(tuple),
+ ownerId, stmt->is_grant,
+ noldmembers, oldmembers,
+ nnewmembers, newmembers);
+
+ ReleaseSysCache(tuple);
+
pfree(new_acl);
heap_close(relation, RowExclusiveLock);
@@ -1055,6 +1146,10 @@ ExecuteGrantStmt_Tablespace(GrantStmt *stmt)
Datum values[Natts_pg_tablespace];
char nulls[Natts_pg_tablespace];
char replaces[Natts_pg_tablespace];
+ int noldmembers;
+ int nnewmembers;
+ Oid *oldmembers;
+ Oid *newmembers;
relation = heap_open(TableSpaceRelationId, RowExclusiveLock);
ScanKeyInit(&entry[0],
@@ -1136,11 +1231,19 @@ ExecuteGrantStmt_Tablespace(GrantStmt *stmt)
/* get a detoasted copy of the ACL */
old_acl = DatumGetAclPCopy(aclDatum);
+ /*
+ * We need the members of both old and new ACLs so we can correct
+ * the shared dependency information.
+ */
+ noldmembers = aclmembers(old_acl, &oldmembers);
+
new_acl = merge_acl_with_grant(old_acl, stmt->is_grant,
stmt->grant_option, stmt->behavior,
stmt->grantees, this_privileges,
grantorId, ownerId);
+ nnewmembers = aclmembers(new_acl, &newmembers);
+
/* finished building new ACL value, now insert it */
MemSet(values, 0, sizeof(values));
MemSet(nulls, ' ', sizeof(nulls));
@@ -1156,6 +1259,12 @@ ExecuteGrantStmt_Tablespace(GrantStmt *stmt)
/* keep the catalog indexes up to date */
CatalogUpdateIndexes(relation, newtuple);
+ /* Update the shared dependency ACL info */
+ updateAclDependencies(TableSpaceRelationId, HeapTupleGetOid(tuple),
+ ownerId, stmt->is_grant,
+ noldmembers, oldmembers,
+ nnewmembers, newmembers);
+
pfree(new_acl);
heap_endscan(scan);
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index febd0b42dcf..227b21c6564 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.44 2005/04/14 20:03:23 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.45 2005/07/07 20:39:57 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -22,9 +22,11 @@
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/pg_attrdef.h"
+#include "catalog/pg_authid.h"
#include "catalog/pg_cast.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_conversion.h"
+#include "catalog/pg_database.h"
#include "catalog/pg_depend.h"
#include "catalog/pg_language.h"
#include "catalog/pg_namespace.h"
@@ -32,12 +34,15 @@
#include "catalog/pg_operator.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_rewrite.h"
+#include "catalog/pg_tablespace.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
#include "commands/comment.h"
+#include "commands/dbcommands.h"
#include "commands/defrem.h"
#include "commands/proclang.h"
#include "commands/schemacmds.h"
+#include "commands/tablespace.h"
#include "commands/trigger.h"
#include "commands/typecmds.h"
#include "lib/stringinfo.h"
@@ -509,6 +514,7 @@ recursiveDeletion(const ObjectAddress *object,
break;
}
+ /* delete the pg_depend tuple */
simple_heap_delete(depRel, &tup->t_self);
}
@@ -585,6 +591,14 @@ recursiveDeletion(const ObjectAddress *object,
DeleteComments(object->objectId, object->classId, object->objectSubId);
/*
+ * Delete shared dependency references related to this object.
+ * Sub-objects (columns) don't have dependencies on global objects,
+ * so skip them.
+ */
+ if (object->objectSubId == 0)
+ deleteSharedDependencyRecordsFor(object->classId, object->objectId);
+
+ /*
* CommandCounterIncrement here to ensure that preceding changes are
* all visible.
*/
@@ -1365,6 +1379,18 @@ getObjectClass(const ObjectAddress *object)
case NamespaceRelationId:
Assert(object->objectSubId == 0);
return OCLASS_SCHEMA;
+
+ case AuthIdRelationId:
+ Assert(object->objectSubId == 0);
+ return OCLASS_ROLE;
+
+ case DatabaseRelationId:
+ Assert(object->objectSubId == 0);
+ return OCLASS_DATABASE;
+
+ case TableSpaceRelationId:
+ Assert(object->objectSubId == 0);
+ return OCLASS_TBLSPACE;
}
/* shouldn't get here */
@@ -1680,6 +1706,37 @@ getObjectDescription(const ObjectAddress *object)
break;
}
+ case OCLASS_ROLE:
+ {
+ appendStringInfo(&buffer, _("role %s"),
+ GetUserNameFromId(object->objectId));
+ break;
+ }
+
+ case OCLASS_DATABASE:
+ {
+ char *datname;
+
+ datname = get_database_name(object->objectId);
+ if (!datname)
+ elog(ERROR, "cache lookup failed for database %u",
+ object->objectId);
+ appendStringInfo(&buffer, _("database %s"), datname);
+ break;
+ }
+
+ case OCLASS_TBLSPACE:
+ {
+ char *tblspace;
+
+ tblspace = get_tablespace_name(object->objectId);
+ if (!tblspace)
+ elog(ERROR, "cache lookup failed for tablespace %u",
+ object->objectId);
+ appendStringInfo(&buffer, _("tablespace %s"), tblspace);
+ break;
+ }
+
default:
appendStringInfo(&buffer, "unrecognized object %u %u %d",
object->classId,
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 8c6f6bec255..29fd3b0b6d7 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.285 2005/06/05 00:38:07 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.286 2005/07/07 20:39:57 tgl Exp $
*
*
* INTERFACE ROUTINES
@@ -755,6 +755,8 @@ heap_create_with_catalog(const char *relname,
* make a dependency link to force the relation to be deleted if its
* namespace is. Skip this in bootstrap mode, since we don't make
* dependencies while bootstrapping.
+ *
+ * Also make a dependency link to its owner.
*/
if (!IsBootstrapProcessingMode())
{
@@ -768,6 +770,8 @@ heap_create_with_catalog(const char *relname,
referenced.objectId = relnamespace;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+ recordDependencyOnOwner(RelationRelationId, new_rel_oid, GetUserId());
}
/*
diff --git a/src/backend/catalog/pg_conversion.c b/src/backend/catalog/pg_conversion.c
index 61a1a53902a..568b77dfed8 100644
--- a/src/backend/catalog/pg_conversion.c
+++ b/src/backend/catalog/pg_conversion.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/pg_conversion.c,v 1.24 2005/06/28 05:08:52 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/pg_conversion.c,v 1.25 2005/07/07 20:39:57 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -120,6 +120,10 @@ ConversionCreate(const char *conname, Oid connamespace,
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ /* create dependency on owner */
+ recordDependencyOnOwner(ConversionRelationId, HeapTupleGetOid(tup),
+ conowner);
+
heap_freetuple(tup);
heap_close(rel, RowExclusiveLock);
diff --git a/src/backend/catalog/pg_namespace.c b/src/backend/catalog/pg_namespace.c
index 8144d64136b..b0145f62a59 100644
--- a/src/backend/catalog/pg_namespace.c
+++ b/src/backend/catalog/pg_namespace.c
@@ -8,13 +8,14 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/pg_namespace.c,v 1.14 2005/06/28 05:08:52 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/pg_namespace.c,v 1.15 2005/07/07 20:39:57 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/heapam.h"
+#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/pg_namespace.h"
#include "utils/builtins.h"
@@ -72,5 +73,8 @@ NamespaceCreate(const char *nspName, Oid ownerId)
heap_close(nspdesc, RowExclusiveLock);
+ /* Record dependency on owner */
+ recordDependencyOnOwner(NamespaceRelationId, nspoid, ownerId);
+
return nspoid;
}
diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c
index 55fa9e127cf..903a46ac0f0 100644
--- a/src/backend/catalog/pg_operator.c
+++ b/src/backend/catalog/pg_operator.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/pg_operator.c,v 1.92 2005/06/28 05:08:52 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/pg_operator.c,v 1.93 2005/07/07 20:39:57 tgl Exp $
*
* NOTES
* these routines moved here from commands/define.c and somewhat cleaned up.
@@ -889,6 +889,7 @@ makeOperatorDependencies(HeapTuple tuple)
/* In case we are updating a shell, delete any existing entries */
deleteDependencyRecordsFor(myself.classId, myself.objectId);
+ deleteSharedDependencyRecordsFor(myself.classId, myself.objectId);
/* Dependency on namespace */
if (OidIsValid(oper->oprnamespace))
@@ -962,4 +963,8 @@ makeOperatorDependencies(HeapTuple tuple)
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+
+ /* Dependency on owner */
+ recordDependencyOnOwner(OperatorRelationId, HeapTupleGetOid(tuple),
+ oper->oprowner);
}
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index f8ed892ebfe..e610d447bd2 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.131 2005/06/28 19:51:21 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.132 2005/07/07 20:39:57 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -349,7 +349,10 @@ ProcedureCreate(const char *procedureName,
* existing function, first delete any existing pg_depend entries.
*/
if (is_update)
+ {
deleteDependencyRecordsFor(ProcedureRelationId, retval);
+ deleteSharedDependencyRecordsFor(ProcedureRelationId, retval);
+ }
myself.classId = ProcedureRelationId;
myself.objectId = retval;
@@ -382,6 +385,9 @@ ProcedureCreate(const char *procedureName,
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+ /* dependency on owner */
+ recordDependencyOnOwner(ProcedureRelationId, retval, GetUserId());
+
heap_freetuple(tup);
heap_close(rel, RowExclusiveLock);
diff --git a/src/backend/catalog/pg_shdepend.c b/src/backend/catalog/pg_shdepend.c
new file mode 100644
index 00000000000..b5013e88445
--- /dev/null
+++ b/src/backend/catalog/pg_shdepend.c
@@ -0,0 +1,1042 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_shdepend.c
+ * routines to support manipulation of the pg_shdepend relation
+ *
+ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $PostgreSQL: pgsql/src/backend/catalog/pg_shdepend.c,v 1.1 2005/07/07 20:39:57 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_authid.h"
+#include "catalog/pg_database.h"
+#include "catalog/pg_shdepend.h"
+#include "lib/stringinfo.h"
+#include "miscadmin.h"
+#include "utils/fmgroids.h"
+#include "utils/inval.h"
+#include "utils/syscache.h"
+
+
+typedef enum
+{
+ LOCAL_OBJECT,
+ SHARED_OBJECT,
+ REMOTE_OBJECT
+} objectType;
+
+static int getOidListDiff(Oid *list1, int nlist1, Oid *list2, int nlist2,
+ Oid **diff);
+static Oid classIdGetDbId(Oid classId);
+static void shdepLockAndCheckObject(Oid classId, Oid objectId);
+static void shdepChangeDep(Relation sdepRel, Oid classid, Oid objid,
+ Oid refclassid, Oid refobjid,
+ SharedDependencyType deptype);
+static void shdepAddDependency(Relation sdepRel, Oid classId, Oid objectId,
+ Oid refclassId, Oid refobjId,
+ SharedDependencyType deptype);
+static void shdepDropDependency(Relation sdepRel, Oid classId, Oid objectId,
+ Oid refclassId, Oid refobjId,
+ SharedDependencyType deptype);
+static void storeObjectDescription(StringInfo descs, objectType type,
+ ObjectAddress *object,
+ SharedDependencyType deptype,
+ int count);
+static bool isSharedObjectPinned(Oid classId, Oid objectId, Relation sdepRel);
+
+
+/*
+ * recordSharedDependencyOn
+ *
+ * Record a dependency between 2 objects via their respective ObjectAddresses.
+ * The first argument is the dependent object, the second the one it
+ * references (which must be a shared object).
+ *
+ * This locks the referenced object and makes sure it still exists.
+ * Then it creates an entry in pg_shdepend. The lock is kept until
+ * the end of the transaction.
+ *
+ * Dependencies on pinned objects are not recorded.
+ */
+void
+recordSharedDependencyOn(ObjectAddress *depender,
+ ObjectAddress *referenced,
+ SharedDependencyType deptype)
+{
+ Relation sdepRel;
+
+ /*
+ * Objects in pg_shdepend can't have SubIds.
+ */
+ Assert(depender->objectSubId == 0);
+ Assert(referenced->objectSubId == 0);
+
+ /*
+ * During bootstrap, do nothing since pg_shdepend may not exist yet.
+ * initdb will fill in appropriate pg_shdepend entries after bootstrap.
+ */
+ if (IsBootstrapProcessingMode())
+ return;
+
+ sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
+
+ /* If the referenced object is pinned, do nothing. */
+ if (!isSharedObjectPinned(referenced->classId, referenced->objectId,
+ sdepRel))
+ {
+ shdepAddDependency(sdepRel, depender->classId, depender->objectId,
+ referenced->classId, referenced->objectId,
+ deptype);
+ }
+
+ heap_close(sdepRel, RowExclusiveLock);
+}
+
+/*
+ * recordDependencyOnOwner
+ *
+ * A convenient wrapper of recordSharedDependencyOn -- register the specified
+ * user as owner of the given object.
+ *
+ * Note: it's the caller's responsibility to ensure that there isn't an owner
+ * entry for the object already.
+ */
+void
+recordDependencyOnOwner(Oid classId, Oid objectId, Oid owner)
+{
+ ObjectAddress myself,
+ referenced;
+
+ myself.classId = classId;
+ myself.objectId = objectId;
+ myself.objectSubId = 0;
+
+ referenced.classId = AuthIdRelationId;
+ referenced.objectId = owner;
+ referenced.objectSubId = 0;
+
+ recordSharedDependencyOn(&myself, &referenced, SHARED_DEPENDENCY_OWNER);
+}
+
+/*
+ * shdepChangeDep
+ *
+ * Update shared dependency records to account for an updated referenced
+ * object. This is an internal workhorse for operations such as changing
+ * an object's owner.
+ *
+ * There must be no more than one existing entry for the given dependent
+ * object and dependency type! So in practice this can only be used for
+ * updating SHARED_DEPENDENCY_OWNER entries, which should have that property.
+ *
+ * If there is no previous entry, we assume it was referencing a PINned
+ * object, so we create a new entry. If the new referenced object is
+ * PINned, we don't create an entry (and drop the old one, if any).
+ *
+ * sdepRel must be the pg_shdepend relation, already opened and suitably
+ * locked.
+ */
+static void
+shdepChangeDep(Relation sdepRel, Oid classid, Oid objid,
+ Oid refclassid, Oid refobjid,
+ SharedDependencyType deptype)
+{
+ Oid dbid = classIdGetDbId(classid);
+ HeapTuple oldtup = NULL;
+ HeapTuple scantup;
+ ScanKeyData key[3];
+ SysScanDesc scan;
+
+ /*
+ * Make sure the new referenced object doesn't go away while we record
+ * the dependency.
+ */
+ shdepLockAndCheckObject(refclassid, refobjid);
+
+ /*
+ * Look for a previous entry
+ */
+ ScanKeyInit(&key[0],
+ Anum_pg_shdepend_dbid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(dbid));
+ ScanKeyInit(&key[1],
+ Anum_pg_shdepend_classid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(classid));
+ ScanKeyInit(&key[2],
+ Anum_pg_shdepend_objid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(objid));
+
+ scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
+ SnapshotNow, 3, key);
+
+ while ((scantup = systable_getnext(scan)) != NULL)
+ {
+ /* Ignore if not of the target dependency type */
+ if (((Form_pg_shdepend) GETSTRUCT(scantup))->deptype != deptype)
+ continue;
+ /* Caller screwed up if multiple matches */
+ if (oldtup)
+ elog(ERROR,
+ "multiple pg_shdepend entries for object %u/%u deptype %c",
+ classid, objid, deptype);
+ oldtup = heap_copytuple(scantup);
+ }
+
+ systable_endscan(scan);
+
+ if (isSharedObjectPinned(refclassid, refobjid, sdepRel))
+ {
+ /* No new entry needed, so just delete existing entry if any */
+ if (oldtup)
+ simple_heap_delete(sdepRel, &oldtup->t_self);
+ }
+ else if (oldtup)
+ {
+ /* Need to update existing entry */
+ Form_pg_shdepend shForm = (Form_pg_shdepend) GETSTRUCT(oldtup);
+
+ /* Since oldtup is a copy, we can just modify it in-memory */
+ shForm->refclassid = refclassid;
+ shForm->refobjid = refobjid;
+
+ simple_heap_update(sdepRel, &oldtup->t_self, oldtup);
+
+ /* keep indexes current */
+ CatalogUpdateIndexes(sdepRel, oldtup);
+ }
+ else
+ {
+ /* Need to insert new entry */
+ Datum values[Natts_pg_shdepend];
+ bool nulls[Natts_pg_shdepend];
+
+ memset(nulls, 0, sizeof(nulls));
+
+ values[Anum_pg_shdepend_dbid - 1] = ObjectIdGetDatum(dbid);
+ values[Anum_pg_shdepend_classid - 1] = ObjectIdGetDatum(classid);
+ values[Anum_pg_shdepend_objid - 1] = ObjectIdGetDatum(objid);
+
+ values[Anum_pg_shdepend_refclassid - 1] = ObjectIdGetDatum(refclassid);
+ values[Anum_pg_shdepend_refobjid - 1] = ObjectIdGetDatum(refobjid);
+ values[Anum_pg_shdepend_deptype - 1] = CharGetDatum(deptype);
+
+ /*
+ * we are reusing oldtup just to avoid declaring a new variable,
+ * but it's certainly a new tuple
+ */
+ oldtup = heap_form_tuple(RelationGetDescr(sdepRel), values, nulls);
+ simple_heap_insert(sdepRel, oldtup);
+
+ /* keep indexes current */
+ CatalogUpdateIndexes(sdepRel, oldtup);
+ }
+
+ if (oldtup)
+ heap_freetuple(oldtup);
+}
+
+/*
+ * changeDependencyOnOwner
+ *
+ * Update the shared dependencies to account for the new owner.
+ */
+void
+changeDependencyOnOwner(Oid classId, Oid objectId, Oid newOwnerId)
+{
+ Relation sdepRel;
+
+ sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
+
+ /* Adjust the SHARED_DEPENDENCY_OWNER entry */
+ shdepChangeDep(sdepRel, classId, objectId,
+ AuthIdRelationId, newOwnerId,
+ SHARED_DEPENDENCY_OWNER);
+
+ /*----------
+ * There should never be a SHARED_DEPENDENCY_ACL entry for the owner,
+ * so get rid of it if there is one. This can happen if the new owner
+ * was previously granted some rights to the object.
+ *
+ * This step is analogous to aclnewowner's removal of duplicate entries
+ * in the ACL. We have to do it to handle this scenario:
+ * A grants some rights on an object to B
+ * ALTER OWNER changes the object's owner to B
+ * ALTER OWNER changes the object's owner to C
+ * The third step would remove all mention of B from the object's ACL,
+ * but we'd still have a SHARED_DEPENDENCY_ACL for B if we did not do
+ * things this way.
+ *
+ * The rule against having a SHARED_DEPENDENCY_ACL entry for the owner
+ * allows us to fix things up in just this one place, without having
+ * to make the various ALTER OWNER routines each know about it.
+ *----------
+ */
+ shdepDropDependency(sdepRel, classId, objectId,
+ AuthIdRelationId, newOwnerId,
+ SHARED_DEPENDENCY_ACL);
+
+ heap_close(sdepRel, RowExclusiveLock);
+}
+
+/*
+ * getOidListDiff
+ * Helper for updateAclDependencies.
+ *
+ * Takes two Oid arrays and returns elements from the first not found in the
+ * second. We assume both arrays are sorted and de-duped, and that the
+ * second array does not contain any values not found in the first.
+ *
+ * NOTE: Both input arrays are pfreed.
+ */
+static int
+getOidListDiff(Oid *list1, int nlist1, Oid *list2, int nlist2, Oid **diff)
+{
+ Oid *result;
+ int i,
+ j,
+ k = 0;
+
+ AssertArg(nlist1 >= nlist2 && nlist2 >= 0);
+
+ result = palloc(sizeof(Oid) * (nlist1 - nlist2));
+ *diff = result;
+
+ for (i = 0, j = 0; i < nlist1 && j < nlist2; )
+ {
+ if (list1[i] == list2[j])
+ {
+ i++;
+ j++;
+ }
+ else if (list1[i] < list2[j])
+ {
+ result[k++] = list1[i];
+ i++;
+ }
+ else
+ {
+ /* can't happen */
+ elog(WARNING, "invalid element %u in shorter list", list2[j]);
+ j++;
+ }
+ }
+
+ for (; i < nlist1; i++)
+ result[k++] = list1[i];
+
+ /* We should have copied the exact number of elements */
+ AssertState(k == (nlist1 - nlist2));
+
+ if (list1)
+ pfree(list1);
+ if (list2)
+ pfree(list2);
+
+ return k;
+}
+
+/*
+ * updateAclDependencies
+ * Update the pg_shdepend info for an object's ACL during GRANT/REVOKE.
+ *
+ * classId, objectId: identify the object whose ACL this is
+ * ownerId: role owning the object
+ * isGrant: are we adding or removing ACL entries?
+ * noldmembers, oldmembers: array of roleids appearing in old ACL
+ * nnewmembers, newmembers: array of roleids appearing in new ACL
+ *
+ * We calculate the difference between the new and old lists of roles,
+ * and then insert (if it's a grant) or delete (if it's a revoke) from
+ * pg_shdepend as appropiate.
+ *
+ * Note that we can't insert blindly at grant, because we would end up with
+ * duplicate registered dependencies. We could check for existence of the
+ * tuple before inserting, but that seems to be more expensive than what we are
+ * doing now. On the other hand, we can't just delete the tuples blindly at
+ * revoke, because the user may still have other privileges.
+ *
+ * NOTE: Both input arrays must be sorted and de-duped. They are pfreed
+ * before return.
+ */
+void
+updateAclDependencies(Oid classId, Oid objectId, Oid ownerId, bool isGrant,
+ int noldmembers, Oid *oldmembers,
+ int nnewmembers, Oid *newmembers)
+{
+ Relation sdepRel;
+ Oid *diff;
+ int ndiff,
+ i;
+
+ /*
+ * Calculate the differences between the old and new lists.
+ */
+ if (isGrant)
+ ndiff = getOidListDiff(newmembers, nnewmembers,
+ oldmembers, noldmembers, &diff);
+ else
+ ndiff = getOidListDiff(oldmembers, noldmembers,
+ newmembers, nnewmembers, &diff);
+
+ if (ndiff > 0)
+ {
+ sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
+
+ /* Add or drop the respective dependency */
+ for (i = 0; i < ndiff; i++)
+ {
+ Oid roleid = diff[i];
+
+ /*
+ * Skip the owner: he has an OWNER shdep entry instead.
+ * (This is not just a space optimization; it makes ALTER OWNER
+ * easier. See notes in changeDependencyOnOwner.)
+ */
+ if (roleid == ownerId)
+ continue;
+
+ /* Skip pinned roles */
+ if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel))
+ continue;
+
+ if (isGrant)
+ shdepAddDependency(sdepRel, classId, objectId,
+ AuthIdRelationId, roleid,
+ SHARED_DEPENDENCY_ACL);
+ else
+ shdepDropDependency(sdepRel, classId, objectId,
+ AuthIdRelationId, roleid,
+ SHARED_DEPENDENCY_ACL);
+ }
+
+ heap_close(sdepRel, RowExclusiveLock);
+ }
+
+ pfree(diff);
+}
+
+/*
+ * A struct to keep track of dependencies found in other databases.
+ */
+typedef struct
+{
+ Oid dbOid;
+ int count;
+} remoteDep;
+
+/*
+ * checkSharedDependencies
+ *
+ * Check whether there are shared dependency entries for a given shared
+ * object. Returns a string containing a newline-separated list of object
+ * descriptions that depend on the shared object, or NULL if none is found.
+ *
+ * We can find three different kinds of dependencies: dependencies on objects
+ * of the current database; dependencies on shared objects; and dependencies
+ * on objects local to other databases. We can (and do) provide descriptions
+ * of the two former kinds of objects, but we can't do that for "remote"
+ * objects, so we just provide a count of them.
+ *
+ * If we find a SHARED_DEPENDENCY_PIN entry, we can error out early.
+ */
+char *
+checkSharedDependencies(Oid classId, Oid objectId)
+{
+ Relation sdepRel;
+ ScanKeyData key[2];
+ SysScanDesc scan;
+ HeapTuple tup;
+ int totalDeps = 0;
+ int numLocalDeps = 0;
+ int numSharedDeps = 0;
+ List *remDeps = NIL;
+ ListCell *cell;
+ ObjectAddress object;
+ StringInfoData descs;
+
+ /*
+ * We try to limit the number of reported dependencies to something
+ * sane, both for the user's sake and to avoid blowing out memory.
+ */
+#define MAX_REPORTED_DEPS 100
+
+ initStringInfo(&descs);
+
+ sdepRel = heap_open(SharedDependRelationId, AccessShareLock);
+
+ ScanKeyInit(&key[0],
+ Anum_pg_shdepend_refclassid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(classId));
+ ScanKeyInit(&key[1],
+ Anum_pg_shdepend_refobjid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(objectId));
+
+ scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
+ SnapshotNow, 2, key);
+
+ while (HeapTupleIsValid(tup = systable_getnext(scan)))
+ {
+ Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tup);
+
+ /* This case can be dispatched quickly */
+ if (sdepForm->deptype == SHARED_DEPENDENCY_PIN)
+ {
+ object.classId = classId;
+ object.objectId = objectId;
+ object.objectSubId = 0;
+ ereport(ERROR,
+ (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
+ errmsg("cannot drop %s because it is required by the database system",
+ getObjectDescription(&object))));
+ }
+
+ object.classId = sdepForm->classid;
+ object.objectId = sdepForm->objid;
+ object.objectSubId = 0;
+
+ /*
+ * If it's a dependency local to this database or it's a shared
+ * object, describe it.
+ *
+ * If it's a remote dependency, keep track of it so we can report
+ * the number of them later.
+ */
+ if (sdepForm->dbid == MyDatabaseId)
+ {
+ numLocalDeps++;
+ if (++totalDeps <= MAX_REPORTED_DEPS)
+ storeObjectDescription(&descs, LOCAL_OBJECT, &object,
+ sdepForm->deptype, 0);
+ }
+ else if (sdepForm->dbid == InvalidOid)
+ {
+ numSharedDeps++;
+ if (++totalDeps <= MAX_REPORTED_DEPS)
+ storeObjectDescription(&descs, SHARED_OBJECT, &object,
+ sdepForm->deptype, 0);
+ }
+ else
+ {
+ /* It's not local nor shared, so it must be remote. */
+ remoteDep *dep;
+ bool stored = false;
+
+ /*
+ * XXX this info is kept on a simple List. Maybe it's not good
+ * for performance, but using a hash table seems needlessly
+ * complex. The expected number of databases is not high
+ * anyway, I suppose.
+ */
+ foreach(cell, remDeps)
+ {
+ dep = lfirst(cell);
+ if (dep->dbOid == sdepForm->dbid)
+ {
+ dep->count++;
+ stored = true;
+ break;
+ }
+ }
+ if (!stored)
+ {
+ dep = (remoteDep *) palloc(sizeof(remoteDep));
+ dep->dbOid = sdepForm->dbid;
+ dep->count = 1;
+ remDeps = lappend(remDeps, dep);
+ totalDeps++;
+ }
+ }
+ }
+
+ systable_endscan(scan);
+
+ heap_close(sdepRel, AccessShareLock);
+
+ if (totalDeps > MAX_REPORTED_DEPS)
+ {
+ /*
+ * Report seems unreasonably long, so reduce it to per-database info
+ *
+ * Note: we don't ever suppress per-database totals, which should
+ * be OK as long as there aren't too many databases ...
+ */
+ descs.len = 0; /* reset to empty */
+ descs.data[0] = '\0';
+
+ if (numLocalDeps > 0)
+ {
+ appendStringInfo(&descs, _("%d objects in this database"),
+ numLocalDeps);
+ if (numSharedDeps > 0)
+ appendStringInfoChar(&descs, '\n');
+ }
+ if (numSharedDeps > 0)
+ appendStringInfo(&descs, _("%d shared objects"),
+ numSharedDeps);
+ }
+
+ foreach(cell, remDeps)
+ {
+ remoteDep *dep = lfirst(cell);
+
+ object.classId = DatabaseRelationId;
+ object.objectId = dep->dbOid;
+ object.objectSubId = 0;
+
+ storeObjectDescription(&descs, REMOTE_OBJECT, &object,
+ SHARED_DEPENDENCY_INVALID, dep->count);
+ }
+
+ list_free_deep(remDeps);
+
+ if (descs.len == 0)
+ {
+ pfree(descs.data);
+ return NULL;
+ }
+
+ return descs.data;
+}
+
+/*
+ * copyTemplateDependencies
+ *
+ * Routine to create the initial shared dependencies of a new database.
+ * We simply copy the dependencies from the template database.
+ */
+void
+copyTemplateDependencies(Oid templateDbId, Oid newDbId)
+{
+ Relation sdepRel;
+ TupleDesc sdepDesc;
+ ScanKeyData key[1];
+ SysScanDesc scan;
+ HeapTuple tup;
+ CatalogIndexState indstate;
+ Datum values[Natts_pg_shdepend];
+ bool nulls[Natts_pg_shdepend];
+ bool replace[Natts_pg_shdepend];
+
+ sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
+ sdepDesc = RelationGetDescr(sdepRel);
+
+ indstate = CatalogOpenIndexes(sdepRel);
+
+ /* Scan all entries with dbid = templateDbId */
+ ScanKeyInit(&key[0],
+ Anum_pg_shdepend_dbid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(templateDbId));
+
+ scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
+ SnapshotNow, 1, key);
+
+ /* Set up to copy the tuples except for inserting newDbId */
+ memset(values, 0, sizeof(values));
+ memset(nulls, false, sizeof(nulls));
+ memset(replace, false, sizeof(replace));
+
+ replace[Anum_pg_shdepend_dbid - 1] = true;
+ values[Anum_pg_shdepend_dbid - 1] = ObjectIdGetDatum(newDbId);
+
+ /*
+ * Copy the entries of the original database, changing the database Id
+ * to that of the new database. Note that because we are not copying
+ * rows with dbId == 0 (ie, rows describing dependent shared objects)
+ * we won't copy the ownership dependency of the template database
+ * itself; this is what we want.
+ */
+ while (HeapTupleIsValid(tup = systable_getnext(scan)))
+ {
+ HeapTuple newtup;
+
+ newtup = heap_modify_tuple(tup, sdepDesc, values, nulls, replace);
+ simple_heap_insert(sdepRel, newtup);
+
+ /* Keep indexes current */
+ CatalogIndexInsert(indstate, newtup);
+
+ heap_freetuple(newtup);
+ }
+
+ systable_endscan(scan);
+
+ CatalogCloseIndexes(indstate);
+ heap_close(sdepRel, RowExclusiveLock);
+}
+
+/*
+ * dropDatabaseDependencies
+ *
+ * Delete pg_shdepend entries corresponding to a database that's being
+ * dropped.
+ */
+void
+dropDatabaseDependencies(Oid databaseId)
+{
+ Relation sdepRel;
+ ScanKeyData key[1];
+ SysScanDesc scan;
+ HeapTuple tup;
+
+ sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
+
+ /*
+ * First, delete all the entries that have the database Oid in the
+ * dbid field.
+ */
+ ScanKeyInit(&key[0],
+ Anum_pg_shdepend_dbid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(databaseId));
+ /* We leave the other index fields unspecified */
+
+ scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
+ SnapshotNow, 1, key);
+
+ while (HeapTupleIsValid(tup = systable_getnext(scan)))
+ {
+ simple_heap_delete(sdepRel, &tup->t_self);
+ }
+
+ systable_endscan(scan);
+
+ /* Now delete all entries corresponding to the database itself */
+ shdepDropDependency(sdepRel, DatabaseRelationId, databaseId,
+ InvalidOid, InvalidOid,
+ SHARED_DEPENDENCY_INVALID);
+
+ heap_close(sdepRel, RowExclusiveLock);
+}
+
+/*
+ * deleteSharedDependencyRecordsFor
+ *
+ * Delete all pg_shdepend entries corresponding to a database-local object
+ * that's being dropped or modified.
+ */
+void
+deleteSharedDependencyRecordsFor(Oid classId, Oid objectId)
+{
+ Relation sdepRel;
+
+ sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
+
+ shdepDropDependency(sdepRel, classId, objectId,
+ InvalidOid, InvalidOid,
+ SHARED_DEPENDENCY_INVALID);
+
+ heap_close(sdepRel, RowExclusiveLock);
+}
+
+/*
+ * shdepAddDependency
+ * Internal workhorse for inserting into pg_shdepend
+ *
+ * sdepRel must be the pg_shdepend relation, already opened and suitably
+ * locked.
+ */
+static void
+shdepAddDependency(Relation sdepRel, Oid classId, Oid objectId,
+ Oid refclassId, Oid refobjId,
+ SharedDependencyType deptype)
+{
+ HeapTuple tup;
+ Datum values[Natts_pg_shdepend];
+ bool nulls[Natts_pg_shdepend];
+
+ /*
+ * Make sure the object doesn't go away while we record the dependency
+ * on it. DROP routines should lock the object exclusively before they
+ * check shared dependencies.
+ */
+ shdepLockAndCheckObject(refclassId, refobjId);
+
+ memset(nulls, false, sizeof(nulls));
+
+ /*
+ * Form the new tuple and record the dependency.
+ */
+ values[Anum_pg_shdepend_dbid - 1] = ObjectIdGetDatum(classIdGetDbId(classId));
+ values[Anum_pg_shdepend_classid - 1] = ObjectIdGetDatum(classId);
+ values[Anum_pg_shdepend_objid - 1] = ObjectIdGetDatum(objectId);
+
+ values[Anum_pg_shdepend_refclassid - 1] = ObjectIdGetDatum(refclassId);
+ values[Anum_pg_shdepend_refobjid - 1] = ObjectIdGetDatum(refobjId);
+ values[Anum_pg_shdepend_deptype - 1] = CharGetDatum(deptype);
+
+ tup = heap_form_tuple(sdepRel->rd_att, values, nulls);
+
+ simple_heap_insert(sdepRel, tup);
+
+ /* keep indexes current */
+ CatalogUpdateIndexes(sdepRel, tup);
+
+ /* clean up */
+ heap_freetuple(tup);
+}
+
+/*
+ * shdepDropDependency
+ * Internal workhorse for deleting entries from pg_shdepend.
+ *
+ * We drop entries having the following properties:
+ * dependent object is the one identified by classId/objectId
+ * if refclassId isn't InvalidOid, it must match the entry's refclassid
+ * if refobjId isn't InvalidOid, it must match the entry's refobjid
+ * if deptype isn't SHARED_DEPENDENCY_INVALID, it must match entry's deptype
+ *
+ * sdepRel must be the pg_shdepend relation, already opened and suitably
+ * locked.
+ */
+static void
+shdepDropDependency(Relation sdepRel, Oid classId, Oid objectId,
+ Oid refclassId, Oid refobjId,
+ SharedDependencyType deptype)
+{
+ ScanKeyData key[3];
+ SysScanDesc scan;
+ HeapTuple tup;
+
+ /* Scan for entries matching the dependent object */
+ ScanKeyInit(&key[0],
+ Anum_pg_shdepend_dbid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(classIdGetDbId(classId)));
+ ScanKeyInit(&key[1],
+ Anum_pg_shdepend_classid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(classId));
+ ScanKeyInit(&key[2],
+ Anum_pg_shdepend_objid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(objectId));
+
+ scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
+ SnapshotNow, 3, key);
+
+ while (HeapTupleIsValid(tup = systable_getnext(scan)))
+ {
+ Form_pg_shdepend shdepForm = (Form_pg_shdepend) GETSTRUCT(tup);
+
+ /* Filter entries according to additional parameters */
+ if (OidIsValid(refclassId) && shdepForm->refclassid != refclassId)
+ continue;
+ if (OidIsValid(refobjId) && shdepForm->refobjid != refobjId)
+ continue;
+ if (deptype != SHARED_DEPENDENCY_INVALID &&
+ shdepForm->deptype != deptype)
+ continue;
+
+ /* OK, delete it */
+ simple_heap_delete(sdepRel, &tup->t_self);
+ }
+
+ systable_endscan(scan);
+}
+
+/*
+ * classIdGetDbId
+ *
+ * Get the database Id that should be used in pg_shdepend, given the OID
+ * of the catalog containing the object. For shared objects, it's 0
+ * (InvalidOid); for all other objects, it's the current database Id.
+ *
+ * XXX it's awfully tempting to hard-wire this instead of doing a syscache
+ * lookup ... but resist the temptation, unless you can prove it's a
+ * bottleneck.
+ */
+static Oid
+classIdGetDbId(Oid classId)
+{
+ Oid dbId;
+ HeapTuple tup;
+
+ tup = SearchSysCache(RELOID,
+ ObjectIdGetDatum(classId),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for relation %u", classId);
+
+ if (((Form_pg_class) GETSTRUCT(tup))->relisshared)
+ dbId = InvalidOid;
+ else
+ dbId = MyDatabaseId;
+
+ ReleaseSysCache(tup);
+
+ return dbId;
+}
+
+/*
+ * shdepLockAndCheckObject
+ *
+ * Lock the object that we are about to record a dependency on.
+ * After it's locked, verify that it hasn't been dropped while we
+ * weren't looking. If the object has been dropped, this function
+ * does not return!
+ */
+static void
+shdepLockAndCheckObject(Oid classId, Oid objectId)
+{
+ /* AccessShareLock should be OK, since we are not modifying the object */
+ LockSharedObject(classId, objectId, 0, AccessShareLock);
+
+ /*
+ * We have to recognize sinval updates here, else our local syscache
+ * may still contain the object even if it was just dropped.
+ */
+ AcceptInvalidationMessages();
+
+ switch (classId)
+ {
+ case AuthIdRelationId:
+ if (!SearchSysCacheExists(AUTHOID,
+ ObjectIdGetDatum(objectId),
+ 0, 0, 0))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("role %u was concurrently dropped",
+ objectId)));
+ break;
+
+ /*
+ * Currently, this routine need not support any other shared object
+ * types besides roles. If we wanted to record explicit dependencies
+ * on databases or tablespaces, we'd need code along these lines:
+ */
+#ifdef NOT_USED
+ case TableSpaceRelationId:
+ {
+ /* For lack of a syscache on pg_tablespace, do this: */
+ char *tablespace = get_tablespace_name(objectId);
+
+ if (tablespace == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("tablespace %u was concurrently dropped",
+ objectId)));
+ pfree(tablespace);
+ break;
+ }
+#endif
+
+ default:
+ elog(ERROR, "unrecognized shared classId: %u", classId);
+ }
+}
+
+
+/*
+ * storeObjectDescription
+ * Append the description of a dependent object to "descs"
+ *
+ * While searching for dependencies of a shared object, we stash the
+ * descriptions of dependent objects we find in a single string, which we
+ * later pass to ereport() in the DETAIL field when somebody attempts to
+ * drop a referenced shared object.
+ *
+ * When type is LOCAL_OBJECT or SHARED_OBJECT, we expect object to be the
+ * dependent object, deptype is the dependency type, and count is not used.
+ * When type is REMOTE_OBJECT, we expect object to be the database object,
+ * and count to be nonzero; deptype is not used in this case.
+ */
+static void
+storeObjectDescription(StringInfo descs, objectType type,
+ ObjectAddress *object,
+ SharedDependencyType deptype,
+ int count)
+{
+ char *objdesc = getObjectDescription(object);
+
+ /* separate entries with a newline */
+ if (descs->len != 0)
+ appendStringInfoChar(descs, '\n');
+
+ switch (type)
+ {
+ case LOCAL_OBJECT:
+ case SHARED_OBJECT:
+ if (deptype == SHARED_DEPENDENCY_OWNER)
+ appendStringInfo(descs, _("owner of %s"), objdesc);
+ else if (deptype == SHARED_DEPENDENCY_ACL)
+ appendStringInfo(descs, _("access to %s"), objdesc);
+ else
+ elog(ERROR, "unrecognized dependency type: %d",
+ (int) deptype);
+ break;
+
+ case REMOTE_OBJECT:
+ /* translator: %s will always be "database %s" */
+ appendStringInfo(descs, _("%d objects in %s"), count, objdesc);
+ break;
+
+ default:
+ elog(ERROR, "unrecognized object type: %d", type);
+ }
+
+ pfree(objdesc);
+}
+
+
+/*
+ * isSharedObjectPinned
+ * Return whether a given shared object has a SHARED_DEPENDENCY_PIN entry.
+ *
+ * sdepRel must be the pg_shdepend relation, already opened and suitably
+ * locked.
+ */
+static bool
+isSharedObjectPinned(Oid classId, Oid objectId, Relation sdepRel)
+{
+ bool result = false;
+ ScanKeyData key[2];
+ SysScanDesc scan;
+ HeapTuple tup;
+
+ ScanKeyInit(&key[0],
+ Anum_pg_shdepend_refclassid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(classId));
+ ScanKeyInit(&key[1],
+ Anum_pg_shdepend_refobjid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(objectId));
+
+ scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
+ SnapshotNow, 2, key);
+
+ /*
+ * Since we won't generate additional pg_shdepend entries for pinned
+ * objects, there can be at most one entry referencing a pinned
+ * object. Hence, it's sufficient to look at the first returned
+ * tuple; we don't need to loop.
+ */
+ tup = systable_getnext(scan);
+ if (HeapTupleIsValid(tup))
+ {
+ Form_pg_shdepend shdepForm = (Form_pg_shdepend) GETSTRUCT(tup);
+
+ if (shdepForm->deptype == SHARED_DEPENDENCY_PIN)
+ result = true;
+ }
+
+ systable_endscan(scan);
+
+ return result;
+}
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index fa4068bf9f6..0144e10fdbb 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/pg_type.c,v 1.101 2005/06/28 05:08:52 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/pg_type.c,v 1.102 2005/07/07 20:39:57 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -75,7 +75,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace)
namestrcpy(&name, typeName);
values[i++] = NameGetDatum(&name); /* typname */
values[i++] = ObjectIdGetDatum(typeNamespace); /* typnamespace */
- values[i++] = ObjectIdGetDatum(InvalidOid); /* typowner */
+ values[i++] = ObjectIdGetDatum(GetUserId()); /* typowner */
values[i++] = Int16GetDatum(0); /* typlen */
values[i++] = BoolGetDatum(false); /* typbyval */
values[i++] = CharGetDatum(0); /* typtype */
@@ -117,6 +117,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace)
typoid,
InvalidOid,
0,
+ GetUserId(),
InvalidOid,
InvalidOid,
InvalidOid,
@@ -330,6 +331,7 @@ TypeCreate(const char *typeName,
typeObjectId,
relationOid,
relationKind,
+ GetUserId(),
inputProcedure,
outputProcedure,
receiveProcedure,
@@ -365,6 +367,7 @@ GenerateTypeDependencies(Oid typeNamespace,
Oid relationOid, /* only for 'c'atalog
* types */
char relationKind, /* ditto */
+ Oid owner,
Oid inputProcedure,
Oid outputProcedure,
Oid receiveProcedure,
@@ -379,7 +382,10 @@ GenerateTypeDependencies(Oid typeNamespace,
referenced;
if (rebuild)
+ {
deleteDependencyRecordsFor(TypeRelationId, typeObjectId);
+ deleteSharedDependencyRecordsFor(TypeRelationId, typeObjectId);
+ }
myself.classId = TypeRelationId;
myself.objectId = typeObjectId;
@@ -485,6 +491,9 @@ GenerateTypeDependencies(Oid typeNamespace,
/* Normal dependency on the default expression. */
if (defaultExpr)
recordDependencyOnExpr(&myself, defaultExpr, NIL, DEPENDENCY_NORMAL);
+
+ /* Shared dependency on owner. */
+ recordDependencyOnOwner(TypeRelationId, typeObjectId, owner);
}
/*
diff --git a/src/backend/commands/conversioncmds.c b/src/backend/commands/conversioncmds.c
index c2aa48614e6..9cc72ec7327 100644
--- a/src/backend/commands/conversioncmds.c
+++ b/src/backend/commands/conversioncmds.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/conversioncmds.c,v 1.19 2005/06/28 05:08:53 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/conversioncmds.c,v 1.20 2005/07/07 20:39:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -17,6 +17,7 @@
#include "catalog/pg_conversion.h"
#include "access/heapam.h"
#include "catalog/catalog.h"
+#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/pg_type.h"
@@ -220,6 +221,10 @@ AlterConversionOwner(List *name, Oid newOwnerId)
simple_heap_update(rel, &tup->t_self, tup);
CatalogUpdateIndexes(rel, tup);
+
+ /* Update owner dependency reference */
+ changeDependencyOnOwner(ConversionRelationId, conversionOid,
+ newOwnerId);
}
heap_close(rel, NoLock);
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index ba5ce9200b4..486afb2e21f 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -15,7 +15,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.164 2005/06/30 00:00:50 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.165 2005/07/07 20:39:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -28,6 +28,7 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "catalog/catalog.h"
+#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_database.h"
@@ -511,6 +512,12 @@ createdb(const CreatedbStmt *stmt)
/* Update indexes */
CatalogUpdateIndexes(pg_database_rel, tuple);
+ /* Register owner dependency */
+ recordDependencyOnOwner(DatabaseRelationId, dboid, datdba);
+
+ /* Create pg_shdepend entries for objects within database */
+ copyTemplateDependencies(src_dboid, dboid);
+
/* Close pg_database, but keep exclusive lock till commit */
heap_close(pg_database_rel, NoLock);
@@ -680,6 +687,11 @@ dropdb(const char *dbname)
heap_close(pgdbrel, NoLock);
/*
+ * Remove shared dependency references for the database.
+ */
+ dropDatabaseDependencies(db_id);
+
+ /*
* Set flag to update flat database file at commit.
*/
database_file_update_needed();
@@ -951,6 +963,10 @@ AlterDatabaseOwner(const char *dbname, Oid newOwnerId)
CatalogUpdateIndexes(rel, newtuple);
heap_freetuple(newtuple);
+
+ /* Update owner dependency reference */
+ changeDependencyOnOwner(DatabaseRelationId, HeapTupleGetOid(tuple),
+ newOwnerId);
}
systable_endscan(scan);
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 3329822fe62..8867538f8eb 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.62 2005/06/28 05:08:53 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.63 2005/07/07 20:39:58 tgl Exp $
*
* DESCRIPTION
* These routines take the parse tree and pick out the
@@ -925,6 +925,9 @@ AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId)
CatalogUpdateIndexes(rel, newtuple);
heap_freetuple(newtuple);
+
+ /* Update owner dependency reference */
+ changeDependencyOnOwner(ProcedureRelationId, procOid, newOwnerId);
}
ReleaseSysCache(tup);
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index 3610269644a..a0d0abd553d 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/opclasscmds.c,v 1.33 2005/06/28 05:08:53 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/opclasscmds.c,v 1.34 2005/07/07 20:39:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -392,6 +392,9 @@ DefineOpClass(CreateOpClassStmt *stmt)
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+ /* dependency on owner */
+ recordDependencyOnOwner(OperatorClassRelationId, opclassoid, GetUserId());
+
heap_close(rel, RowExclusiveLock);
}
@@ -962,6 +965,9 @@ AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId)
simple_heap_update(rel, &tup->t_self, tup);
CatalogUpdateIndexes(rel, tup);
+
+ /* Update owner dependency reference */
+ changeDependencyOnOwner(OperatorClassRelationId, amOid, newOwnerId);
}
heap_close(rel, NoLock);
diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c
index adea1b2b5d7..ae93e9230ee 100644
--- a/src/backend/commands/operatorcmds.c
+++ b/src/backend/commands/operatorcmds.c
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/operatorcmds.c,v 1.22 2005/06/28 05:08:54 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/operatorcmds.c,v 1.23 2005/07/07 20:39:58 tgl Exp $
*
* DESCRIPTION
* The "DefineFoo" routines take the parse tree and pick out the
@@ -310,6 +310,9 @@ AlterOperatorOwner(List *name, TypeName *typeName1, TypeName *typeName2,
simple_heap_update(rel, &tup->t_self, tup);
CatalogUpdateIndexes(rel, tup);
+
+ /* Update owner dependency reference */
+ changeDependencyOnOwner(OperatorRelationId, operOid, newOwnerId);
}
heap_close(rel, NoLock);
diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c
index d92812f1fcf..c4c20ddeb39 100644
--- a/src/backend/commands/schemacmds.c
+++ b/src/backend/commands/schemacmds.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/schemacmds.c,v 1.31 2005/06/28 05:08:54 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/schemacmds.c,v 1.32 2005/07/07 20:39:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -342,6 +342,10 @@ AlterSchemaOwner(const char *name, Oid newOwnerId)
CatalogUpdateIndexes(rel, newtuple);
heap_freetuple(newtuple);
+
+ /* Update owner dependency reference */
+ changeDependencyOnOwner(NamespaceRelationId, HeapTupleGetOid(tup),
+ newOwnerId);
}
ReleaseSysCache(tup);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index dda9532baf8..2b50a402fc8 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.162 2005/06/28 05:08:54 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.163 2005/07/07 20:39:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -5321,6 +5321,9 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId)
heap_freetuple(newtuple);
+ /* Update owner dependency reference */
+ changeDependencyOnOwner(RelationRelationId, relationOid, newOwnerId);
+
/*
* If we are operating on a table, also change the ownership of
* any indexes and sequences that belong to the table, as well as
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index b925825062f..a2cce14335c 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -37,7 +37,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/tablespace.c,v 1.24 2005/07/04 04:51:46 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/tablespace.c,v 1.25 2005/07/07 20:39:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -50,6 +50,7 @@
#include "access/heapam.h"
#include "catalog/catalog.h"
+#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_tablespace.h"
@@ -307,6 +308,9 @@ CreateTableSpace(CreateTableSpaceStmt *stmt)
heap_freetuple(tuple);
+ /* Record dependency on owner */
+ recordDependencyOnOwner(TableSpaceRelationId, tablespaceoid, ownerId);
+
/*
* Attempt to coerce target directory to safe permissions. If this
* fails, it doesn't exist or has the wrong owner.
@@ -818,6 +822,10 @@ AlterTableSpaceOwner(const char *name, Oid newOwnerId)
CatalogUpdateIndexes(rel, newtuple);
heap_freetuple(newtuple);
+
+ /* Update owner dependency reference */
+ changeDependencyOnOwner(TableSpaceRelationId, HeapTupleGetOid(tup),
+ newOwnerId);
}
heap_endscan(scandesc);
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 53570c77409..e5f2a2f762b 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.73 2005/06/28 05:08:54 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.74 2005/07/07 20:39:58 tgl Exp $
*
* DESCRIPTION
* The "DefineFoo" routines take the parse tree and pick out the
@@ -1208,6 +1208,7 @@ AlterDomainDefault(List *names, Node *defaultRaw)
domainoid,
typTup->typrelid,
0, /* relation kind is n/a */
+ typTup->typowner,
typTup->typinput,
typTup->typoutput,
typTup->typreceive,
@@ -2080,6 +2081,9 @@ AlterTypeOwner(List *names, Oid newOwnerId)
simple_heap_update(rel, &tup->t_self, tup);
CatalogUpdateIndexes(rel, tup);
+
+ /* Update owner dependency reference */
+ changeDependencyOnOwner(TypeRelationId, typeOid, newOwnerId);
}
/* Clean up */
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 982c286cde7..4a46343d5d8 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/backend/commands/user.c,v 1.155 2005/06/29 20:34:13 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/user.c,v 1.156 2005/07/07 20:39:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -14,10 +14,10 @@
#include "access/genam.h"
#include "access/heapam.h"
+#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/pg_auth_members.h"
#include "catalog/pg_authid.h"
-#include "catalog/pg_database.h"
#include "commands/user.h"
#include "libpq/crypt.h"
#include "miscadmin.h"
@@ -742,10 +742,8 @@ DropRole(DropRoleStmt *stmt)
const char *role = strVal(lfirst(item));
HeapTuple tuple,
tmp_tuple;
- Relation pg_rel;
- TupleDesc pg_dsc;
- ScanKeyData scankey;
- HeapScanDesc scan;
+ ScanKeyData scankey;
+ char *detail;
SysScanDesc sscan;
Oid roleid;
@@ -780,42 +778,18 @@ DropRole(DropRoleStmt *stmt)
errmsg("must be superuser to drop superusers")));
/*
- * Check if role still owns a database. If so, error out.
- *
- * (It used to be that this function would drop the database
- * automatically. This is not only very dangerous for people that
- * don't read the manual, it doesn't seem to be the behaviour one
- * would expect either.) -- petere 2000/01/14)
- */
- pg_rel = heap_open(DatabaseRelationId, AccessShareLock);
- pg_dsc = RelationGetDescr(pg_rel);
-
- ScanKeyInit(&scankey,
- Anum_pg_database_datdba,
- BTEqualStrategyNumber, F_OIDEQ,
- roleid);
-
- scan = heap_beginscan(pg_rel, SnapshotNow, 1, &scankey);
-
- if ((tmp_tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
- {
- char *dbname;
+ * Lock the role, so nobody can add dependencies to her while we drop
+ * her. We keep the lock until the end of transaction.
+ */
+ LockSharedObject(AuthIdRelationId, roleid, 0, AccessExclusiveLock);
- dbname = NameStr(((Form_pg_database) GETSTRUCT(tmp_tuple))->datname);
+ /* Check for pg_shdepend entries depending on this role */
+ if ((detail = checkSharedDependencies(AuthIdRelationId, roleid)) != NULL)
ereport(ERROR,
- (errcode(ERRCODE_OBJECT_IN_USE),
- errmsg("role \"%s\" cannot be dropped", role),
- errdetail("The role owns database \"%s\".", dbname)));
- }
-
- heap_endscan(scan);
- heap_close(pg_rel, AccessShareLock);
-
- /*
- * Somehow we'd have to check for tables, views, etc. owned by the
- * role as well, but those could be spread out over all sorts of
- * databases which we don't have access to (easily).
- */
+ (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
+ errmsg("role \"%s\" cannot be dropped because some objects depend on it",
+ role),
+ errdetail("%s", detail)));
/*
* Remove the role from the pg_authid table
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index 5b65099696e..a416def8155 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.117 2005/06/29 20:34:14 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.118 2005/07/07 20:39:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -59,6 +59,7 @@ static void check_circularity(const Acl *old_acl, const AclItem *mod_aip,
Oid ownerId);
static Acl *recursive_revoke(Acl *acl, Oid grantee, AclMode revoke_privs,
Oid ownerId, DropBehavior behavior);
+static int oidComparator(const void *arg1, const void *arg2);
static AclMode convert_priv_string(text *priv_type_text);
@@ -1053,6 +1054,86 @@ aclmask(const Acl *acl, Oid roleid, Oid ownerId,
/*
+ * aclmembers
+ * Find out all the roleids mentioned in an Acl.
+ * Note that we do not distinguish grantors from grantees.
+ *
+ * *roleids is set to point to a palloc'd array containing distinct OIDs
+ * in sorted order. The length of the array is the function result.
+ */
+int
+aclmembers(const Acl *acl, Oid **roleids)
+{
+ Oid *list;
+ const AclItem *acldat;
+ int i,
+ j,
+ k;
+
+ if (acl == NULL || ACL_NUM(acl) == 0)
+ {
+ *roleids = NULL;
+ return 0;
+ }
+
+ /* Allocate the worst-case space requirement */
+ list = palloc(ACL_NUM(acl) * 2 * sizeof(Oid));
+ acldat = ACL_DAT(acl);
+
+ /*
+ * Walk the ACL collecting mentioned RoleIds.
+ */
+ j = 0;
+ for (i = 0; i < ACL_NUM(acl); i++)
+ {
+ const AclItem *ai = &acldat[i];
+
+ if (ai->ai_grantee != ACL_ID_PUBLIC)
+ list[j++] = ai->ai_grantee;
+ /* grantor is currently never PUBLIC, but let's check anyway */
+ if (ai->ai_grantor != ACL_ID_PUBLIC)
+ list[j++] = ai->ai_grantor;
+ }
+
+ /* Sort the array */
+ qsort(list, j, sizeof(Oid), oidComparator);
+
+ /* Remove duplicates from the array */
+ k = 0;
+ for (i = 1; i < j; i++)
+ {
+ if (list[k] != list[i])
+ list[++k] = list[i];
+ }
+
+ /*
+ * We could repalloc the array down to minimum size, but it's hardly
+ * worth it since it's only transient memory.
+ */
+ *roleids = list;
+
+ return k + 1;
+}
+
+/*
+ * oidComparator
+ * qsort comparison function for Oids
+ */
+static int
+oidComparator(const void *arg1, const void *arg2)
+{
+ Oid oid1 = * (const Oid *) arg1;
+ Oid oid2 = * (const Oid *) arg2;
+
+ if (oid1 > oid2)
+ return 1;
+ if (oid1 < oid2)
+ return -1;
+ return 0;
+}
+
+
+/*
* aclinsert (exported function)
*/
Datum
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index b61c56d7218..f5ea903ca34 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -42,7 +42,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
* Portions taken from FreeBSD.
*
- * $PostgreSQL: pgsql/src/bin/initdb/initdb.c,v 1.90 2005/07/02 17:01:50 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/initdb/initdb.c,v 1.91 2005/07/07 20:39:59 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1512,6 +1512,10 @@ setup_depend(void)
* must be the only entries for their objects.
*/
"DELETE FROM pg_depend;\n",
+ "VACUUM pg_depend;\n",
+ "DELETE FROM pg_shdepend;\n",
+ "VACUUM pg_shdepend;\n",
+
"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
" FROM pg_class;\n",
"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
@@ -1541,10 +1545,13 @@ setup_depend(void)
"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
" FROM pg_namespace "
" WHERE nspname LIKE 'pg%';\n",
+
+ "INSERT INTO pg_shdepend SELECT 0, 0, 0, tableoid, oid, 'p' "
+ " FROM pg_authid;\n",
NULL
};
- fputs(_("initializing pg_depend ... "), stdout);
+ fputs(_("initializing dependencies ... "), stdout);
fflush(stdout);
snprintf(cmd, sizeof(cmd),
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index a465037e4b3..9a9266ff41d 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.284 2005/07/01 19:19:02 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.285 2005/07/07 20:39:59 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 200507011
+#define CATALOG_VERSION_NO 200507071
#endif
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 98f6f088328..06fdf02a108 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/dependency.h,v 1.14 2004/12/31 22:03:24 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/dependency.h,v 1.15 2005/07/07 20:39:59 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -67,6 +67,40 @@ typedef enum DependencyType
DEPENDENCY_PIN = 'p'
} DependencyType;
+/*
+ * There is also a SharedDependencyType enum type that determines the exact
+ * semantics of an entry in pg_shdepend. Just like regular dependency entries,
+ * any pg_shdepend entry means that the referenced object cannot be dropped
+ * unless the dependent object is dropped at the same time. There are some
+ * additional rules however:
+ *
+ * (a) For a SHARED_DEPENDENCY_PIN entry, there is no dependent object --
+ * rather, the referenced object is an essential part of the system. This
+ * applies to the initdb-created superuser. Entries of this type are only
+ * created by initdb; objects in this category don't need further pg_shdepend
+ * entries if more objects come to depend on them.
+ *
+ * (b) a SHARED_DEPENDENCY_OWNER entry means that the referenced object is
+ * the role owning the dependent object. The referenced object must be
+ * a pg_authid entry.
+ *
+ * (c) a SHARED_DEPENDENCY_ACL entry means that the referenced object is
+ * a role mentioned in the ACL field of the dependent object. The referenced
+ * object must be a pg_authid entry. (SHARED_DEPENDENCY_ACL entries are not
+ * created for the owner of an object; hence two objects may be linked by
+ * one or the other, but not both, of these dependency types.)
+ *
+ * SHARED_DEPENDENCY_INVALID is a value used as a parameter in internal
+ * routines, and is not valid in the catalog itself.
+ */
+typedef enum SharedDependencyType
+{
+ SHARED_DEPENDENCY_PIN = 'p',
+ SHARED_DEPENDENCY_OWNER = 'o',
+ SHARED_DEPENDENCY_ACL = 'a',
+ SHARED_DEPENDENCY_INVALID = 0
+} SharedDependencyType;
+
/*
* The two objects related by a dependency are identified by ObjectAddresses.
@@ -81,7 +115,8 @@ typedef struct ObjectAddress
/*
- * This enum covers all system catalogs whose OIDs can appear in classId.
+ * This enum covers all system catalogs whose OIDs can appear in
+ * pg_depend.classId or pg_shdepend.classId.
*/
typedef enum ObjectClass
{
@@ -98,6 +133,9 @@ typedef enum ObjectClass
OCLASS_REWRITE, /* pg_rewrite */
OCLASS_TRIGGER, /* pg_trigger */
OCLASS_SCHEMA, /* pg_namespace */
+ OCLASS_ROLE, /* pg_authid */
+ OCLASS_DATABASE, /* pg_database */
+ OCLASS_TBLSPACE, /* pg_tablespace */
MAX_OCLASS /* MUST BE LAST */
} ObjectClass;
@@ -136,4 +174,28 @@ extern void recordMultipleDependencies(const ObjectAddress *depender,
extern long deleteDependencyRecordsFor(Oid classId, Oid objectId);
+/* in pg_shdepend.c */
+
+extern void recordSharedDependencyOn(ObjectAddress *depender,
+ ObjectAddress *referenced,
+ SharedDependencyType deptype);
+
+extern void deleteSharedDependencyRecordsFor(Oid classId, Oid objectId);
+
+extern void recordDependencyOnOwner(Oid classId, Oid objectId, Oid owner);
+
+extern void changeDependencyOnOwner(Oid classId, Oid objectId,
+ Oid newOwnerId);
+
+extern void updateAclDependencies(Oid classId, Oid objectId,
+ Oid ownerId, bool isGrant,
+ int noldmembers, Oid *oldmembers,
+ int nnewmembers, Oid *newmembers);
+
+extern char *checkSharedDependencies(Oid classId, Oid objectId);
+
+extern void copyTemplateDependencies(Oid templateDbId, Oid newDbId);
+
+extern void dropDatabaseDependencies(Oid databaseId);
+
#endif /* DEPENDENCY_H */
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 757a2095792..1ab33708f0c 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/indexing.h,v 1.88 2005/06/28 05:09:04 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/indexing.h,v 1.89 2005/07/07 20:39:59 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -179,6 +179,13 @@ DECLARE_UNIQUE_INDEX(pg_rewrite_oid_index,2692, on pg_rewrite using btree(oid oi
DECLARE_UNIQUE_INDEX(pg_rewrite_rel_rulename_index,2693, on pg_rewrite using btree(ev_class oid_ops, rulename name_ops));
#define RewriteRelRulenameIndexId 2693
+/* This following index is not used for a cache and is not unique */
+DECLARE_INDEX(pg_shdepend_depender_index,1232, on pg_shdepend using btree(dbid oid_ops, classid oid_ops, objid oid_ops));
+#define SharedDependDependerIndexId 1232
+/* This following index is not used for a cache and is not unique */
+DECLARE_INDEX(pg_shdepend_reference_index,1233, on pg_shdepend using btree(refclassid oid_ops, refobjid oid_ops));
+#define SharedDependReferenceIndexId 1233
+
DECLARE_UNIQUE_INDEX(pg_statistic_relid_att_index,2696, on pg_statistic using btree(starelid oid_ops, staattnum int2_ops));
#define StatisticRelidAttnumIndexId 2696
diff --git a/src/include/catalog/pg_shdepend.h b/src/include/catalog/pg_shdepend.h
new file mode 100644
index 00000000000..f3c905bd2f5
--- /dev/null
+++ b/src/include/catalog/pg_shdepend.h
@@ -0,0 +1,91 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_shdepend.h
+ * definition of the system "shared dependency" relation (pg_shdepend)
+ * along with the relation's initial contents.
+ *
+ *
+ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $PostgreSQL: pgsql/src/include/catalog/pg_shdepend.h,v 1.1 2005/07/07 20:39:59 tgl Exp $
+ *
+ * NOTES
+ * the genbki.sh script reads this file and generates .bki
+ * information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_SHDEPEND_H
+#define PG_SHDEPEND_H
+
+/* ----------------
+ * postgres.h contains the system type definitions and the
+ * CATALOG(), BKI_BOOTSTRAP and DATA() sugar words so this file
+ * can be read by both genbki.sh and the C compiler.
+ * ----------------
+ */
+
+/* ----------------
+ * pg_shdepend definition. cpp turns this into
+ * typedef struct FormData_pg_shdepend
+ * ----------------
+ */
+#define SharedDependRelationId 1214
+CATALOG(pg_shdepend,1214) BKI_SHARED_RELATION BKI_WITHOUT_OIDS
+{
+ /*
+ * Identification of the dependent (referencing) object.
+ *
+ * These fields are all zeroes for a DEPENDENCY_PIN entry. Also,
+ * dbid can be zero to denote a shared object.
+ */
+ Oid dbid; /* OID of database containing object */
+ Oid classid; /* OID of table containing object */
+ Oid objid; /* OID of object itself */
+
+ /*
+ * Identification of the independent (referenced) object. This is
+ * always a shared object, so we need no database ID field.
+ */
+ Oid refclassid; /* OID of table containing object */
+ Oid refobjid; /* OID of object itself */
+
+ /*
+ * Precise semantics of the relationship are specified by the deptype
+ * field. See SharedDependencyType in catalog/dependency.h.
+ */
+ char deptype; /* see codes in dependency.h */
+} FormData_pg_shdepend;
+
+/* ----------------
+ * Form_pg_shdepend corresponds to a pointer to a row with
+ * the format of pg_shdepend relation.
+ * ----------------
+ */
+typedef FormData_pg_shdepend *Form_pg_shdepend;
+
+/* ----------------
+ * compiler constants for pg_shdepend
+ * ----------------
+ */
+#define Natts_pg_shdepend 6
+#define Anum_pg_shdepend_dbid 1
+#define Anum_pg_shdepend_classid 2
+#define Anum_pg_shdepend_objid 3
+#define Anum_pg_shdepend_refclassid 4
+#define Anum_pg_shdepend_refobjid 5
+#define Anum_pg_shdepend_deptype 6
+
+
+/*
+ * pg_shdepend has no preloaded contents; system-defined dependencies are
+ * loaded into it during a late stage of the initdb process.
+ *
+ * NOTE: we do not represent all possible dependency pairs in pg_shdepend;
+ * for example, there's not much value in creating an explicit dependency
+ * from a relation to its database. Currently, only dependencies on roles
+ * are explicitly stored in pg_shdepend.
+ */
+
+#endif /* PG_SHDEPEND_H */
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 44b8cf0cedb..9feeccedfdc 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/pg_type.h,v 1.162 2005/06/28 05:09:12 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_type.h,v 1.163 2005/07/07 20:39:59 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
@@ -582,6 +582,7 @@ extern void GenerateTypeDependencies(Oid typeNamespace,
Oid typeObjectId,
Oid relationOid,
char relationKind,
+ Oid owner,
Oid inputProcedure,
Oid outputProcedure,
Oid receiveProcedure,
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index 0b560e062c1..7defb4e1e28 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/utils/acl.h,v 1.80 2005/06/29 20:34:15 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/utils/acl.h,v 1.81 2005/07/07 20:40:00 tgl Exp $
*
* NOTES
* An ACL array is simply an array of AclItems, representing the union
@@ -208,6 +208,7 @@ extern Acl *aclnewowner(const Acl *old_acl, Oid oldOwnerId, Oid newOwnerId);
extern AclMode aclmask(const Acl *acl, Oid roleid, Oid ownerId,
AclMode mask, AclMaskHow how);
+extern int aclmembers(const Acl *acl, Oid **roleids);
extern bool is_member_of_role(Oid member, Oid role);
extern bool is_admin_of_role(Oid member, Oid role);
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 7fa83a5723d..aa8e967269d 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -385,5 +385,6 @@ SELECT * FROM clstr_1;
-- clean up
\c -
DROP TABLE clstr_1;
+DROP TABLE clstr_2;
DROP TABLE clstr_3;
DROP USER clstr_user;
diff --git a/src/test/regress/expected/dependency.out b/src/test/regress/expected/dependency.out
new file mode 100644
index 00000000000..4ee3e8b6a8f
--- /dev/null
+++ b/src/test/regress/expected/dependency.out
@@ -0,0 +1,39 @@
+--
+-- DEPENDENCIES
+--
+CREATE USER regression_user;
+CREATE USER regression_user2;
+CREATE USER regression_user3;
+CREATE GROUP regression_group;
+CREATE TABLE deptest ();
+GRANT SELECT ON TABLE deptest TO GROUP regression_group;
+GRANT ALL ON TABLE deptest TO regression_user, regression_user2;
+-- can't drop neither because they have privileges somewhere
+DROP USER regression_user;
+ERROR: role "regression_user" cannot be dropped because some objects depend on it
+DETAIL: access to table deptest
+DROP GROUP regression_group;
+ERROR: role "regression_group" cannot be dropped because some objects depend on it
+DETAIL: access to table deptest
+-- if we revoke the privileges we can drop the group
+REVOKE SELECT ON deptest FROM GROUP regression_group;
+DROP GROUP regression_group;
+-- can't drop the user if we revoke the privileges partially
+REVOKE SELECT, INSERT, UPDATE, DELETE, RULE, REFERENCES ON deptest FROM regression_user;
+DROP USER regression_user;
+ERROR: role "regression_user" cannot be dropped because some objects depend on it
+DETAIL: access to table deptest
+-- now we are OK to drop him
+REVOKE TRIGGER ON deptest FROM regression_user;
+DROP USER regression_user;
+-- we are OK too if we drop the privileges all at once
+REVOKE ALL ON deptest FROM regression_user2;
+DROP USER regression_user2;
+-- can't drop the owner of an object
+ALTER TABLE deptest OWNER TO regression_user3;
+DROP USER regression_user3;
+ERROR: role "regression_user3" cannot be dropped because some objects depend on it
+DETAIL: owner of table deptest
+-- if we drop the object, we can drop the user too
+DROP TABLE deptest;
+DROP USER regression_user3;
diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out
index 8fa8bb18ce3..293d84e8f4d 100644
--- a/src/test/regress/expected/privileges.out
+++ b/src/test/regress/expected/privileges.out
@@ -601,6 +601,7 @@ DROP TABLE atest3;
DROP TABLE atest4;
DROP GROUP regressgroup1;
DROP GROUP regressgroup2;
+REVOKE USAGE ON LANGUAGE sql FROM regressuser1;
DROP USER regressuser1;
DROP USER regressuser2;
DROP USER regressuser3;
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 3593bc356bd..5c0e5ca8951 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -56,6 +56,7 @@ SELECT relname, relhasindex
pg_operator | t
pg_proc | t
pg_rewrite | t
+ pg_shdepend | t
pg_statistic | t
pg_tablespace | t
pg_trigger | t
@@ -65,7 +66,7 @@ SELECT relname, relhasindex
shighway | t
tenk1 | t
tenk2 | t
-(55 rows)
+(56 rows)
--
-- another sanity check: every system catalog that has OIDs should have
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 9a3f7927328..3e52f6f558a 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -68,7 +68,7 @@ test: misc
# ----------
# The fifth group of parallel test
# ----------
-test: select_views portals_p2 rules foreign_key cluster
+test: select_views portals_p2 rules foreign_key cluster dependency
# ----------
# The sixth group of parallel test
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index bb60dc0a105..bf9517fee20 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -1,4 +1,4 @@
-# $PostgreSQL: pgsql/src/test/regress/serial_schedule,v 1.27 2005/06/17 22:32:50 tgl Exp $
+# $PostgreSQL: pgsql/src/test/regress/serial_schedule,v 1.28 2005/07/07 20:40:01 tgl Exp $
# This should probably be in an order similar to parallel_schedule.
test: boolean
test: char
@@ -98,3 +98,4 @@ test: polymorphism
test: rowtypes
test: stats
test: tablespace
+test: dependency
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index f669922b031..db300b19981 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -156,5 +156,6 @@ SELECT * FROM clstr_1;
-- clean up
\c -
DROP TABLE clstr_1;
+DROP TABLE clstr_2;
DROP TABLE clstr_3;
DROP USER clstr_user;
diff --git a/src/test/regress/sql/dependency.sql b/src/test/regress/sql/dependency.sql
new file mode 100644
index 00000000000..6d52b62dee1
--- /dev/null
+++ b/src/test/regress/sql/dependency.sql
@@ -0,0 +1,41 @@
+--
+-- DEPENDENCIES
+--
+
+CREATE USER regression_user;
+CREATE USER regression_user2;
+CREATE USER regression_user3;
+CREATE GROUP regression_group;
+
+CREATE TABLE deptest ();
+
+GRANT SELECT ON TABLE deptest TO GROUP regression_group;
+GRANT ALL ON TABLE deptest TO regression_user, regression_user2;
+
+-- can't drop neither because they have privileges somewhere
+DROP USER regression_user;
+DROP GROUP regression_group;
+
+-- if we revoke the privileges we can drop the group
+REVOKE SELECT ON deptest FROM GROUP regression_group;
+DROP GROUP regression_group;
+
+-- can't drop the user if we revoke the privileges partially
+REVOKE SELECT, INSERT, UPDATE, DELETE, RULE, REFERENCES ON deptest FROM regression_user;
+DROP USER regression_user;
+
+-- now we are OK to drop him
+REVOKE TRIGGER ON deptest FROM regression_user;
+DROP USER regression_user;
+
+-- we are OK too if we drop the privileges all at once
+REVOKE ALL ON deptest FROM regression_user2;
+DROP USER regression_user2;
+
+-- can't drop the owner of an object
+ALTER TABLE deptest OWNER TO regression_user3;
+DROP USER regression_user3;
+
+-- if we drop the object, we can drop the user too
+DROP TABLE deptest;
+DROP USER regression_user3;
diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql
index aa65bf599d2..ce65fefe61b 100644
--- a/src/test/regress/sql/privileges.sql
+++ b/src/test/regress/sql/privileges.sql
@@ -339,6 +339,7 @@ DROP TABLE atest4;
DROP GROUP regressgroup1;
DROP GROUP regressgroup2;
+REVOKE USAGE ON LANGUAGE sql FROM regressuser1;
DROP USER regressuser1;
DROP USER regressuser2;
DROP USER regressuser3;