]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Track dependencies on shared objects (which is to say, roles; we already
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 7 Jul 2005 20:40:02 +0000 (20:40 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
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 @@
 <!--
  Documentation of the system catalogs, directed toward PostgreSQL developers
- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.106 2005/06/28 05:08:50 tgl Exp $
+ $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.107 2005/07/07 20:39:56 tgl Exp $
  -->
 
 <chapter id="catalogs">
       <entry>query rewrite rules</entry>
      </row>
 
+     <row>
+      <entry><link linkend="catalog-pg-shdepend"><structname>pg_shdepend</structname></link></entry>
+      <entry>dependencies on shared objects</entry>
+     </row>
+
      <row>
       <entry><link linkend="catalog-pg-statistic"><structname>pg_statistic</structname></link></entry>
       <entry>planner statistics</entry>
    RESTRICT</> case.
   </para>
 
+  <para>
+   See also <link linkend="catalog-pg-shdepend"><structname>pg_shdepend</structname></link>,
+   which performs a similar function for dependencies involving objects
+   that are shared across a database cluster.
+  </para>
+
   <table>
    <title><structname>pg_depend</> Columns</title>
 
 
      <row>
       <entry><structfield>proargmodes</structfield></entry>
-      <entry><type>"char"[]</type></entry>
+      <entry><type>char[]</type></entry>
       <entry></entry>
       <entry>
         An array with the modes of the function arguments, encoded as
  </sect1>
 
 
+ <sect1 id="catalog-pg-shdepend">
+  <title><structname>pg_shdepend</structname></title>
+
+  <indexterm zone="catalog-pg-shdepend">
+   <primary>pg_shdepend</primary>
+  </indexterm>
+
+  <para>
+   The catalog <structname>pg_shdepend</structname> records the
+   dependency relationships between database objects and shared objects,
+   such as roles.  This information allows
+   <productname>PostgreSQL</productname> to ensure that those objects are
+   unreferenced before attempting to delete them.
+  </para>
+
+  <para>
+   See also <link linkend="catalog-pg-depend"><structname>pg_depend</structname></link>,
+   which performs a similar function for dependencies involving objects
+   within a single database.
+  </para>
+
+  <para>
+   Unlike most system catalogs, <structname>pg_shdepend</structname>
+   is shared across all databases of a cluster: there is only one
+   copy of <structname>pg_shdepend</structname> per cluster, not
+   one per database.
+  </para>
+
+  <table>
+   <title><structname>pg_shdepend</> Columns</title>
+
+   <tgroup cols=4>
+    <thead>
+     <row>
+      <entry>Name</entry>
+      <entry>Type</entry>
+      <entry>References</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+    <tbody>
+     <row>
+      <entry><structfield>dbid</structfield></entry>
+      <entry><type>oid</type></entry>
+      <entry><literal><link linkend="catalog-pg-database"><structname>pg_database</structname></link>.oid</literal></entry>
+      <entry>The OID of the database the dependent object is in,
+       or zero for a shared object</entry>
+     </row>
+
+     <row>
+      <entry><structfield>classid</structfield></entry>
+      <entry><type>oid</type></entry>
+      <entry><literal><link linkend="catalog-pg-class"><structname>pg_class</structname></link>.oid</literal></entry>
+      <entry>The OID of the system catalog the dependent object is in</entry>
+     </row>
+
+     <row>
+      <entry><structfield>objid</structfield></entry>
+      <entry><type>oid</type></entry>
+      <entry>any OID column</entry>
+      <entry>The OID of the specific dependent object</entry>
+     </row>
+
+     <row>
+      <entry><structfield>refclassid</structfield></entry>
+      <entry><type>oid</type></entry>
+      <entry><literal><link linkend="catalog-pg-class"><structname>pg_class</structname></link>.oid</literal></entry>
+      <entry>The OID of the system catalog the referenced object is in
+       (must be a shared catalog)</entry>
+     </row>
+
+     <row>
+      <entry><structfield>refobjid</structfield></entry>
+      <entry><type>oid</type></entry>
+      <entry>any OID column</entry>
+      <entry>The OID of the specific referenced object</entry>
+     </row>
+
+     <row>
+      <entry><structfield>deptype</structfield></entry>
+      <entry><type>char</type></entry>
+      <entry></entry>
+      <entry>
+       A code defining the specific semantics of this dependency relationship; see text.
+      </entry>
+     </row>
+
+    </tbody>
+   </tgroup>
+  </table>
+
+  <para>
+   In all cases, a <structname>pg_shdepend</structname> entry indicates that
+   the referenced object may not be dropped without also dropping the dependent
+   object.  However, there are several subflavors identified by
+   <structfield>deptype</>:
+
+   <variablelist>
+    <varlistentry>
+     <term><symbol>SHARED_DEPENDENCY_OWNER</> (<literal>o</>)</term>
+     <listitem>
+      <para>
+       The referenced object (which must be a role) is the owner of the
+       dependent object.
+      </para>
+     </listitem>
+    </varlistentry>
+
+    <varlistentry>
+     <term><symbol>SHARED_DEPENDENCY_ACL</> (<literal>a</>)</term>
+     <listitem>
+      <para>
+       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 <symbol>SHARED_DEPENDENCY_ACL</> entry is
+       not made for the owner of the object, since the owner will have
+       a <symbol>SHARED_DEPENDENCY_OWNER</> entry anyway.)
+      </para>
+     </listitem>
+    </varlistentry>
+
+    <varlistentry>
+     <term><symbol>SHARED_DEPENDENCY_PIN</> (<literal>p</>)</term>
+     <listitem>
+      <para>
+       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 <command>initdb</command>.  The columns for the
+       dependent object contain zeroes.
+      </para>
+     </listitem>
+    </varlistentry>
+   </variablelist>
+
+   Other dependency flavors may be needed in future.  Note in particular
+   that the current definition only supports roles as referenced objects.
+  </para>
+
+ </sect1>
+
+
  <sect1 id="catalog-pg-statistic">
   <title><structname>pg_statistic</structname></title>
 
       <entry><literal><link linkend="catalog-pg-database"><structname>pg_database</structname></link>.oid</literal></entry>
       <entry>
        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
       </entry>
      </row>
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;