Track dependencies on shared objects (which is to say, roles; we already
authorTom Lane
Thu, 7 Jul 2005 20:40:02 +0000 (20:40 +0000)
committerTom Lane
Thu, 7 Jul 2005 20:40:02 +0000 (20:40 +0000)
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.

38 files changed:
doc/src/sgml/catalogs.sgml
src/backend/catalog/Makefile
src/backend/catalog/aclchk.c
src/backend/catalog/dependency.c
src/backend/catalog/heap.c
src/backend/catalog/pg_conversion.c
src/backend/catalog/pg_namespace.c
src/backend/catalog/pg_operator.c
src/backend/catalog/pg_proc.c
src/backend/catalog/pg_shdepend.c [new file with mode: 0644]
src/backend/catalog/pg_type.c
src/backend/commands/conversioncmds.c
src/backend/commands/dbcommands.c
src/backend/commands/functioncmds.c
src/backend/commands/opclasscmds.c
src/backend/commands/operatorcmds.c
src/backend/commands/schemacmds.c
src/backend/commands/tablecmds.c
src/backend/commands/tablespace.c
src/backend/commands/typecmds.c
src/backend/commands/user.c
src/backend/utils/adt/acl.c
src/bin/initdb/initdb.c
src/include/catalog/catversion.h
src/include/catalog/dependency.h
src/include/catalog/indexing.h
src/include/catalog/pg_shdepend.h [new file with mode: 0644]
src/include/catalog/pg_type.h
src/include/utils/acl.h
src/test/regress/expected/cluster.out
src/test/regress/expected/dependency.out [new file with mode: 0644]
src/test/regress/expected/privileges.out
src/test/regress/expected/sanity_check.out
src/test/regress/parallel_schedule
src/test/regress/serial_schedule
src/test/regress/sql/cluster.sql
src/test/regress/sql/dependency.sql [new file with mode: 0644]
src/test/regress/sql/privileges.sql

index 7358dfb5122e3b44d6c01635bb10df404cb1d792..560a6b3f35f5b2a1d8a6d1d376e36bbe049067f0 100644 (file)
@@ -1,6 +1,6 @@
 
 
 
       query rewrite rules
      
 
+     
+      pg_shdepend
+      dependencies on shared objects
+     
+
      
       pg_statistic
       planner statistics
    RESTRICT case.
   
 
+  
+   See also pg_shdepend,
+   which performs a similar function for dependencies involving objects
+   that are shared across a database cluster.
+  
+
   
    <structname>pg_depend</> Columns
 
 
      
       proargmodes
-      "char"[]
+      char[]
       
       
         An array with the modes of the function arguments, encoded as
  
 
 
+  <structname>pg_shdepend</structname>
+
+  
+   pg_shdepend
+  
+
+  
+   The catalog pg_shdepend records the
+   dependency relationships between database objects and shared objects,
+   such as roles.  This information allows
+   PostgreSQL to ensure that those objects are
+   unreferenced before attempting to delete them.
+  
+
+  
+   See also pg_depend,
+   which performs a similar function for dependencies involving objects
+   within a single database.
+  
+
+  
+   Unlike most system catalogs, pg_shdepend
+   is shared across all databases of a cluster: there is only one
+   copy of pg_shdepend per cluster, not
+   one per database.
+  
+
+  
+   <structname>pg_shdepend</> Columns
+
+   
+    
+     
+      Name
+      Type
+      References
+      Description
+     
+    
+
+    
+     
+      dbid
+      oid
+      pg_database.oid
+      The OID of the database the dependent object is in,
+       or zero for a shared object
+     
+
+     
+      classid
+      oid
+      pg_class.oid
+      The OID of the system catalog the dependent object is in
+     
+
+     
+      objid
+      oid
+      any OID column
+      The OID of the specific dependent object
+     
+
+     
+      refclassid
+      oid
+      pg_class.oid
+      The OID of the system catalog the referenced object is in
+       (must be a shared catalog)
+     
+
+     
+      refobjid
+      oid
+      any OID column
+      The OID of the specific referenced object
+     
+
+     
+      deptype
+      char
+      
+      
+       A code defining the specific semantics of this dependency relationship; see text.
+      
+     
+
+    
+   
+  
+
+  
+   In all cases, a pg_shdepend entry indicates that
+   the referenced object may not be dropped without also dropping the dependent
+   object.  However, there are several subflavors identified by
+   deptype:
+
+   
+    
+     SHARED_DEPENDENCY_OWNER (o)
+     
+      
+       The referenced object (which must be a role) is the owner of the
+       dependent object.
+      
+     
+    
+
+    
+     SHARED_DEPENDENCY_ACL (a)
+     
+      
+       The referenced object (which must be a role) is mentioned in the
+       ACL (access control list, i.e., privileges list) of the
+       dependent object.  (A SHARED_DEPENDENCY_ACL entry is
+       not made for the owner of the object, since the owner will have
+       a SHARED_DEPENDENCY_OWNER entry anyway.)
+      
+     
+    
+
+    
+     SHARED_DEPENDENCY_PIN (p)
+     
+      
+       There is no dependent object; this type of entry is a signal
+       that the system itself depends on the referenced object, and so
+       that object must never be deleted.  Entries of this type are
+       created only by initdb.  The columns for the
+       dependent object contain zeroes.
+      
+     
+    
+   
+
+   Other dependency flavors may be needed in future.  Note in particular
+   that the current definition only supports roles as referenced objects.
+  
+
+
+
  
   <structname>pg_statistic</structname>
 
       pg_database.oid
       
        OID of the database in which the object exists, or
-       zero if the object is a globally-shared object, or
+       zero if the object is a shared object, or
        NULL if the object is a transaction ID
       
      
index 09addc4af1cb3968404c52af58ad290b6b5cd93e..fdcb2ff5a5942319bef419de1bd42594fd1dd3f5 100644 (file)
@@ -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 \
     )
index f095ef24d6bf604c3cd1f3986e2faf80da88fae0..8053ca73bbff7a1acff6b55521d4ff80e72e6cc3 100644 (file)
@@ -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);
index febd0b42dcfc0b347252e653860a6112364af25f..227b21c6564b15ca8d979ac20494da79a27dbe47 100644 (file)
@@ -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 $
  *
  *-------------------------------------------------------------------------
  */
 #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"
 #include "catalog/pg_operator.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_rewrite.h"
+#include "catalog/pg_tablespace.h"
 #include "catalog/pg_trigger.h"
 #include "catalog/pg_type.h"
 #include "commands/comment.h"
+#include "commands/dbcommands.h"
 #include "commands/defrem.h"
 #include "commands/proclang.h"
 #include "commands/schemacmds.h"
+#include "commands/tablespace.h"
 #include "commands/trigger.h"
 #include "commands/typecmds.h"
 #include "lib/stringinfo.h"
@@ -509,6 +514,7 @@ recursiveDeletion(const ObjectAddress *object,
                break;
        }
 
+       /* delete the pg_depend tuple */
        simple_heap_delete(depRel, &tup->t_self);
    }
 
@@ -584,6 +590,14 @@ recursiveDeletion(const ObjectAddress *object,
     */
    DeleteComments(object->objectId, object->classId, object->objectSubId);
 
+   /*
+    * Delete shared dependency references related to this object.
+    * Sub-objects (columns) don't have dependencies on global objects,
+    * so skip them.
+    */
+   if (object->objectSubId == 0)
+       deleteSharedDependencyRecordsFor(object->classId, object->objectId);
+
    /*
     * CommandCounterIncrement here to ensure that preceding changes are
     * all visible.
@@ -1365,6 +1379,18 @@ getObjectClass(const ObjectAddress *object)
        case NamespaceRelationId:
            Assert(object->objectSubId == 0);
            return OCLASS_SCHEMA;
+
+       case AuthIdRelationId:
+           Assert(object->objectSubId == 0);
+           return OCLASS_ROLE;
+
+       case DatabaseRelationId:
+           Assert(object->objectSubId == 0);
+           return OCLASS_DATABASE;
+
+       case TableSpaceRelationId:
+           Assert(object->objectSubId == 0);
+           return OCLASS_TBLSPACE;
    }
 
    /* shouldn't get here */
@@ -1680,6 +1706,37 @@ getObjectDescription(const ObjectAddress *object)
                break;
            }
 
+       case OCLASS_ROLE:
+           {
+               appendStringInfo(&buffer, _("role %s"),
+                                GetUserNameFromId(object->objectId));
+               break;
+           }
+
+       case OCLASS_DATABASE:
+           {
+               char       *datname;
+
+               datname = get_database_name(object->objectId);
+               if (!datname)
+                   elog(ERROR, "cache lookup failed for database %u",
+                        object->objectId);
+               appendStringInfo(&buffer, _("database %s"), datname);
+               break;
+           }
+
+       case OCLASS_TBLSPACE:
+           {
+               char       *tblspace;
+
+               tblspace = get_tablespace_name(object->objectId);
+               if (!tblspace)
+                   elog(ERROR, "cache lookup failed for tablespace %u",
+                        object->objectId);
+               appendStringInfo(&buffer, _("tablespace %s"), tblspace);
+               break;
+           }
+
        default:
            appendStringInfo(&buffer, "unrecognized object %u %u %d",
                             object->classId,
index 8c6f6bec2557154f66da32d8829ad62c32a93b11..29fd3b0b6d75f158d68a3e508ce9dc4297fa110a 100644 (file)
@@ -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());
    }
 
    /*
index 61a1a53902ab998e6eef43b77165e883b64b0fff..568b77dfed81063beb7f9ef0cb74aff702ddf6f7 100644 (file)
@@ -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);
 
index 8144d64136b5c2bef83fe94294353ec02f91239c..b0145f62a59f7edddd901632b051928db83f6c8a 100644 (file)
@@ -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;
 }
index 55fa9e127cf14d2d7e91456ef8cd1a718663a0c6..903a46ac0f0093b7680ec4ac5790267e7dd48a17 100644 (file)
@@ -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);
 }
index f8ed892ebfe3f02bdb1e07f5cca1b7d9cb517639..e610d447bd2d0b6e73e428b54c32dcdd96db7aa8 100644 (file)
@@ -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 (file)
index 0000000..b5013e8
--- /dev/null
@@ -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;
+}
index fa4068bf9f60b6cdf6f165cdae0c113935fc0929..0144e10fdbb3e6411c2bd6bda2f54ac1016657a2 100644 (file)
@@ -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);
 }
 
 /*
index c2aa48614e6afbab43ae6febb0963db42f42149a..9cc72ec7327a0fa30f4bf26b3c696b622c1f01ed 100644 (file)
@@ -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);
index ba5ce9200b45ea52590470129594b3c8fcff4c9b..486afb2e21f5b392ac9753f791409229db1bffb5 100644 (file)
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.164 2005/06/30 00:00:50 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.165 2005/07/07 20:39:58 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -28,6 +28,7 @@
 #include "access/genam.h"
 #include "access/heapam.h"
 #include "catalog/catalog.h"
+#include "catalog/dependency.h"
 #include "catalog/indexing.h"
 #include "catalog/pg_authid.h"
 #include "catalog/pg_database.h"
@@ -511,6 +512,12 @@ createdb(const CreatedbStmt *stmt)
    /* Update indexes */
    CatalogUpdateIndexes(pg_database_rel, tuple);
 
+   /* Register owner dependency */
+   recordDependencyOnOwner(DatabaseRelationId, dboid, datdba);
+
+   /* Create pg_shdepend entries for objects within database */
+   copyTemplateDependencies(src_dboid, dboid);
+
    /* Close pg_database, but keep exclusive lock till commit */
    heap_close(pg_database_rel, NoLock);
 
@@ -679,6 +686,11 @@ dropdb(const char *dbname)
    /* Close pg_database, but keep exclusive lock till commit */
    heap_close(pgdbrel, NoLock);
 
+   /*
+    * Remove shared dependency references for the database.
+    */
+   dropDatabaseDependencies(db_id);
+
    /*
     * Set flag to update flat database file at commit.
     */
@@ -951,6 +963,10 @@ AlterDatabaseOwner(const char *dbname, Oid newOwnerId)
        CatalogUpdateIndexes(rel, newtuple);
 
        heap_freetuple(newtuple);
+
+       /* Update owner dependency reference */
+       changeDependencyOnOwner(DatabaseRelationId, HeapTupleGetOid(tuple),
+                               newOwnerId);
    }
 
    systable_endscan(scan);
index 3329822fe623e6a8df678a264d0f86c07d1fc62e..8867538f8eb2c04290094a4c8ba74801b3808dbe 100644 (file)
@@ -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);
index 3610269644a47eebede50bff8072a749b7194771..a0d0abd553d03c700a1dace74c574791200df8d6 100644 (file)
@@ -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);
index adea1b2b5d737a41694b1ecb1c7ca9e60304bfb2..ae93e9230eed54b29ebe572865c006adcfb43af4 100644 (file)
@@ -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);
index d92812f1fcfc4994a8170795799fc5ffcebd0fb9..c4c20ddeb39ecba592d7845d1129c44200376a9d 100644 (file)
@@ -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);
index dda9532baf857b6721cafcecdc6581f75bd9488c..2b50a402fc882cae383e69b0eda96ed18929b8a6 100644 (file)
@@ -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
index b925825062fc17a560cd838f21e42db142ed9405..a2cce14335c0c6ffe87c1c6346a115f5d3ed8b41 100644 (file)
@@ -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);
index 53570c77409bc13498d80b6e1de73678dc266a92..e5f2a2f762bc7281827c5b51c58d023bd3752548 100644 (file)
@@ -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 */
index 982c286cde7e05d1f76defa919e3b0cc3d879b1b..4a46343d5d873d001decfad52a938b41089d1602 100644 (file)
@@ -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 $
  *
  *-------------------------------------------------------------------------
  */
 
 #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
index 5b65099696e70c3c9880b22c21852b3449d9054c..a416def815593f562738a49af6a19d6bbf917c7a 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.117 2005/06/29 20:34:14 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.118 2005/07/07 20:39:58 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -59,6 +59,7 @@ static void check_circularity(const Acl *old_acl, const AclItem *mod_aip,
                  Oid ownerId);
 static Acl *recursive_revoke(Acl *acl, Oid grantee, AclMode revoke_privs,
                 Oid ownerId, DropBehavior behavior);
+static int oidComparator(const void *arg1, const void *arg2);
 
 static AclMode convert_priv_string(text *priv_type_text);
 
@@ -1052,6 +1053,86 @@ aclmask(const Acl *acl, Oid roleid, Oid ownerId,
 }
 
 
+/*
+ * aclmembers
+ *     Find out all the roleids mentioned in an Acl.
+ *     Note that we do not distinguish grantors from grantees.
+ *
+ * *roleids is set to point to a palloc'd array containing distinct OIDs
+ * in sorted order.  The length of the array is the function result.
+ */
+int
+aclmembers(const Acl *acl, Oid **roleids)
+{
+   Oid    *list;
+   const AclItem *acldat;
+   int     i,
+           j,
+           k;
+
+   if (acl == NULL || ACL_NUM(acl) == 0)
+   {
+       *roleids = NULL;
+       return 0;
+   }
+
+   /* Allocate the worst-case space requirement */
+   list = palloc(ACL_NUM(acl) * 2 * sizeof(Oid));
+   acldat = ACL_DAT(acl);
+
+   /*
+    * Walk the ACL collecting mentioned RoleIds.
+    */
+   j = 0;
+   for (i = 0; i < ACL_NUM(acl); i++)
+   {
+       const AclItem *ai = &acldat[i];
+
+       if (ai->ai_grantee != ACL_ID_PUBLIC)
+           list[j++] = ai->ai_grantee;
+       /* grantor is currently never PUBLIC, but let's check anyway */
+       if (ai->ai_grantor != ACL_ID_PUBLIC)
+           list[j++] = ai->ai_grantor;
+   }
+
+   /* Sort the array */
+   qsort(list, j, sizeof(Oid), oidComparator);
+
+   /* Remove duplicates from the array */
+   k = 0;
+   for (i = 1; i < j; i++)
+   {
+       if (list[k] != list[i])
+           list[++k] = list[i];
+   }
+
+   /*
+    * We could repalloc the array down to minimum size, but it's hardly
+    * worth it since it's only transient memory.
+    */
+   *roleids = list;
+
+   return k + 1;
+}
+
+/*
+ * oidComparator
+ *     qsort comparison function for Oids
+ */
+static int
+oidComparator(const void *arg1, const void *arg2)
+{
+   Oid oid1 = * (const Oid *) arg1;
+   Oid oid2 = * (const Oid *) arg2;
+
+   if (oid1 > oid2)
+       return 1;
+   if (oid1 < oid2)
+       return -1;
+   return 0;
+}
+
+
 /*
  * aclinsert (exported function)
  */
index b61c56d72180f18c8c2d327ee3db66ca8ed44874..f5ea903ca346b53303ad896b1e6fa6149b7b4a2f 100644 (file)
@@ -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),
index a465037e4b390d9538ba8083cd7a2f282581fe2a..9a9266ff41d157016c7f14ee1540b287d7cd105c 100644 (file)
@@ -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
index 98f6f088328fbbb5fbbd1268364266731dc2a552..06fdf02a1083ac50e2360d795303574e9ce5f29a 100644 (file)
@@ -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 */
index 757a2095792f6bbde753916e4977a851927d0892..1ab33708f0cd8a242bfd34d2933bf3028b905ad6 100644 (file)
@@ -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 (file)
index 0000000..f3c905b
--- /dev/null
@@ -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 */
index 44b8cf0cedb430ebaff43f9d523640a7a7cfd98a..9feeccedfdc5a4bbeaa56f8c91eec4456537687d 100644 (file)
@@ -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,
index 0b560e062c13836b98847e747740f2329124d42a..7defb4e1e2870feafdb2563c47989ea8717c9199 100644 (file)
@@ -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);
index 7fa83a5723db502801cf22aa1bcb7672cc19ac2e..aa8e967269deb317b3e7fce4f4aa97305ec6c7a9 100644 (file)
@@ -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 (file)
index 0000000..4ee3e8b
--- /dev/null
@@ -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;
index 8fa8bb18ce38bbbd4631c91db64f69a1dff12d81..293d84e8f4d451d74a47fd162e3ff77c45ef7341 100644 (file)
@@ -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;
index 3593bc356bd18adda4b4a881d1c552cbeeca9749..5c0e5ca89511e5d7c857c2d425315328df000437 100644 (file)
@@ -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
index 9a3f7927328c5ca8a7e127b061753f47da8cee39..3e52f6f558a71bc3e9e9412dc59058fe60668067 100644 (file)
@@ -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
index bb60dc0a10573641229a003d5e4c1578fc97ab1b..bf9517fee20b94ea29ad260235567394aaa73373 100644 (file)
@@ -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
index f669922b03106130af5046650b3b2e1eab71c30f..db300b199810b74ba96e7b07e6acb7e0a6f20af8 100644 (file)
@@ -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 (file)
index 0000000..6d52b62
--- /dev/null
@@ -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;
index aa65bf599d24700fc17511747a3551e79f3673b0..ce65fefe61b88530699653e750292b659155bee2 100644 (file)
@@ -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;