Commit 59d1b3d9 authored by Tom Lane's avatar Tom Lane

Track dependencies on shared objects (which is to say, roles; we already

have adequate mechanisms for tracking the contents of databases and
tablespaces).  This solves the longstanding problem that you can drop a
user who still owns objects and/or has access permissions.
Alvaro Herrera, with some kibitzing from Tom Lane.
parent 442b59dd
<!-- <!--
Documentation of the system catalogs, directed toward PostgreSQL developers 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"> <chapter id="catalogs">
...@@ -173,6 +173,11 @@ ...@@ -173,6 +173,11 @@
<entry>query rewrite rules</entry> <entry>query rewrite rules</entry>
</row> </row>
<row>
<entry><link linkend="catalog-pg-shdepend"><structname>pg_shdepend</structname></link></entry>
<entry>dependencies on shared objects</entry>
</row>
<row> <row>
<entry><link linkend="catalog-pg-statistic"><structname>pg_statistic</structname></link></entry> <entry><link linkend="catalog-pg-statistic"><structname>pg_statistic</structname></link></entry>
<entry>planner statistics</entry> <entry>planner statistics</entry>
...@@ -1890,6 +1895,12 @@ ...@@ -1890,6 +1895,12 @@
RESTRICT</> case. RESTRICT</> case.
</para> </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> <table>
<title><structname>pg_depend</> Columns</title> <title><structname>pg_depend</> Columns</title>
...@@ -3024,7 +3035,7 @@ ...@@ -3024,7 +3035,7 @@
<row> <row>
<entry><structfield>proargmodes</structfield></entry> <entry><structfield>proargmodes</structfield></entry>
<entry><type>"char"[]</type></entry> <entry><type>char[]</type></entry>
<entry></entry> <entry></entry>
<entry> <entry>
An array with the modes of the function arguments, encoded as An array with the modes of the function arguments, encoded as
...@@ -3198,6 +3209,149 @@ ...@@ -3198,6 +3209,149 @@
</sect1> </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"> <sect1 id="catalog-pg-statistic">
<title><structname>pg_statistic</structname></title> <title><structname>pg_statistic</structname></title>
...@@ -4196,7 +4350,7 @@ ...@@ -4196,7 +4350,7 @@
<entry><literal><link linkend="catalog-pg-database"><structname>pg_database</structname></link>.oid</literal></entry> <entry><literal><link linkend="catalog-pg-database"><structname>pg_database</structname></link>.oid</literal></entry>
<entry> <entry>
OID of the database in which the object exists, or 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 NULL if the object is a transaction ID
</entry> </entry>
</row> </row>
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
# #
# Makefile for backend/catalog # 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 ...@@ -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 \ 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_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 BKIFILES = postgres.bki postgres.description
...@@ -31,7 +32,7 @@ POSTGRES_BKI_SRCS := $(addprefix $(top_srcdir)/src/include/catalog/,\ ...@@ -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_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_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_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 \ pg_authid.h pg_auth_members.h pg_tablespace.h pg_depend.h \
indexing.h \ indexing.h \
) )
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * NOTES
* See acl.h. * See acl.h.
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "access/heapam.h" #include "access/heapam.h"
#include "catalog/catalog.h" #include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h" #include "catalog/indexing.h"
#include "catalog/namespace.h" #include "catalog/namespace.h"
#include "catalog/pg_auth_members.h" #include "catalog/pg_auth_members.h"
...@@ -252,6 +253,10 @@ ExecuteGrantStmt_Relation(GrantStmt *stmt) ...@@ -252,6 +253,10 @@ ExecuteGrantStmt_Relation(GrantStmt *stmt)
Datum values[Natts_pg_class]; Datum values[Natts_pg_class];
char nulls[Natts_pg_class]; char nulls[Natts_pg_class];
char replaces[Natts_pg_class]; char replaces[Natts_pg_class];
int noldmembers;
int nnewmembers;
Oid *oldmembers;
Oid *newmembers;
/* open pg_class */ /* open pg_class */
relation = heap_open(RelationRelationId, RowExclusiveLock); relation = heap_open(RelationRelationId, RowExclusiveLock);
...@@ -344,11 +349,19 @@ ExecuteGrantStmt_Relation(GrantStmt *stmt) ...@@ -344,11 +349,19 @@ ExecuteGrantStmt_Relation(GrantStmt *stmt)
/* get a detoasted copy of the ACL */ /* get a detoasted copy of the ACL */
old_acl = DatumGetAclPCopy(aclDatum); 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, new_acl = merge_acl_with_grant(old_acl, stmt->is_grant,
stmt->grant_option, stmt->behavior, stmt->grant_option, stmt->behavior,
stmt->grantees, this_privileges, stmt->grantees, this_privileges,
grantorId, ownerId); grantorId, ownerId);
nnewmembers = aclmembers(new_acl, &newmembers);
/* finished building new ACL value, now insert it */ /* finished building new ACL value, now insert it */
MemSet(values, 0, sizeof(values)); MemSet(values, 0, sizeof(values));
MemSet(nulls, ' ', sizeof(nulls)); MemSet(nulls, ' ', sizeof(nulls));
...@@ -359,13 +372,19 @@ ExecuteGrantStmt_Relation(GrantStmt *stmt) ...@@ -359,13 +372,19 @@ ExecuteGrantStmt_Relation(GrantStmt *stmt)
newtuple = heap_modifytuple(tuple, RelationGetDescr(relation), values, nulls, replaces); newtuple = heap_modifytuple(tuple, RelationGetDescr(relation), values, nulls, replaces);
ReleaseSysCache(tuple);
simple_heap_update(relation, &newtuple->t_self, newtuple); simple_heap_update(relation, &newtuple->t_self, newtuple);
/* keep the catalog indexes up to date */ /* keep the catalog indexes up to date */
CatalogUpdateIndexes(relation, newtuple); 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); pfree(new_acl);
heap_close(relation, RowExclusiveLock); heap_close(relation, RowExclusiveLock);
...@@ -422,6 +441,10 @@ ExecuteGrantStmt_Database(GrantStmt *stmt) ...@@ -422,6 +441,10 @@ ExecuteGrantStmt_Database(GrantStmt *stmt)
Datum values[Natts_pg_database]; Datum values[Natts_pg_database];
char nulls[Natts_pg_database]; char nulls[Natts_pg_database];
char replaces[Natts_pg_database]; char replaces[Natts_pg_database];
int noldmembers;
int nnewmembers;
Oid *oldmembers;
Oid *newmembers;
relation = heap_open(DatabaseRelationId, RowExclusiveLock); relation = heap_open(DatabaseRelationId, RowExclusiveLock);
ScanKeyInit(&entry[0], ScanKeyInit(&entry[0],
...@@ -503,11 +526,19 @@ ExecuteGrantStmt_Database(GrantStmt *stmt) ...@@ -503,11 +526,19 @@ ExecuteGrantStmt_Database(GrantStmt *stmt)
/* get a detoasted copy of the ACL */ /* get a detoasted copy of the ACL */
old_acl = DatumGetAclPCopy(aclDatum); 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, new_acl = merge_acl_with_grant(old_acl, stmt->is_grant,
stmt->grant_option, stmt->behavior, stmt->grant_option, stmt->behavior,
stmt->grantees, this_privileges, stmt->grantees, this_privileges,
grantorId, ownerId); grantorId, ownerId);
nnewmembers = aclmembers(new_acl, &newmembers);
/* finished building new ACL value, now insert it */ /* finished building new ACL value, now insert it */
MemSet(values, 0, sizeof(values)); MemSet(values, 0, sizeof(values));
MemSet(nulls, ' ', sizeof(nulls)); MemSet(nulls, ' ', sizeof(nulls));
...@@ -523,6 +554,12 @@ ExecuteGrantStmt_Database(GrantStmt *stmt) ...@@ -523,6 +554,12 @@ ExecuteGrantStmt_Database(GrantStmt *stmt)
/* keep the catalog indexes up to date */ /* keep the catalog indexes up to date */
CatalogUpdateIndexes(relation, newtuple); CatalogUpdateIndexes(relation, newtuple);
/* Update the shared dependency ACL info */
updateAclDependencies(DatabaseRelationId, HeapTupleGetOid(tuple),
ownerId, stmt->is_grant,
noldmembers, oldmembers,
nnewmembers, newmembers);
pfree(new_acl); pfree(new_acl);
heap_endscan(scan); heap_endscan(scan);
...@@ -580,6 +617,10 @@ ExecuteGrantStmt_Function(GrantStmt *stmt) ...@@ -580,6 +617,10 @@ ExecuteGrantStmt_Function(GrantStmt *stmt)
Datum values[Natts_pg_proc]; Datum values[Natts_pg_proc];
char nulls[Natts_pg_proc]; char nulls[Natts_pg_proc];
char replaces[Natts_pg_proc]; char replaces[Natts_pg_proc];
int noldmembers;
int nnewmembers;
Oid *oldmembers;
Oid *newmembers;
oid = LookupFuncNameTypeNames(func->funcname, func->funcargs, false); oid = LookupFuncNameTypeNames(func->funcname, func->funcargs, false);
...@@ -658,11 +699,19 @@ ExecuteGrantStmt_Function(GrantStmt *stmt) ...@@ -658,11 +699,19 @@ ExecuteGrantStmt_Function(GrantStmt *stmt)
/* get a detoasted copy of the ACL */ /* get a detoasted copy of the ACL */
old_acl = DatumGetAclPCopy(aclDatum); 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, new_acl = merge_acl_with_grant(old_acl, stmt->is_grant,
stmt->grant_option, stmt->behavior, stmt->grant_option, stmt->behavior,
stmt->grantees, this_privileges, stmt->grantees, this_privileges,
grantorId, ownerId); grantorId, ownerId);
nnewmembers = aclmembers(new_acl, &newmembers);
/* finished building new ACL value, now insert it */ /* finished building new ACL value, now insert it */
MemSet(values, 0, sizeof(values)); MemSet(values, 0, sizeof(values));
MemSet(nulls, ' ', sizeof(nulls)); MemSet(nulls, ' ', sizeof(nulls));
...@@ -673,13 +722,19 @@ ExecuteGrantStmt_Function(GrantStmt *stmt) ...@@ -673,13 +722,19 @@ ExecuteGrantStmt_Function(GrantStmt *stmt)
newtuple = heap_modifytuple(tuple, RelationGetDescr(relation), values, nulls, replaces); newtuple = heap_modifytuple(tuple, RelationGetDescr(relation), values, nulls, replaces);
ReleaseSysCache(tuple);
simple_heap_update(relation, &newtuple->t_self, newtuple); simple_heap_update(relation, &newtuple->t_self, newtuple);
/* keep the catalog indexes up to date */ /* keep the catalog indexes up to date */
CatalogUpdateIndexes(relation, newtuple); 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); pfree(new_acl);
heap_close(relation, RowExclusiveLock); heap_close(relation, RowExclusiveLock);
...@@ -734,6 +789,10 @@ ExecuteGrantStmt_Language(GrantStmt *stmt) ...@@ -734,6 +789,10 @@ ExecuteGrantStmt_Language(GrantStmt *stmt)
Datum values[Natts_pg_language]; Datum values[Natts_pg_language];
char nulls[Natts_pg_language]; char nulls[Natts_pg_language];
char replaces[Natts_pg_language]; char replaces[Natts_pg_language];
int noldmembers;
int nnewmembers;
Oid *oldmembers;
Oid *newmembers;
relation = heap_open(LanguageRelationId, RowExclusiveLock); relation = heap_open(LanguageRelationId, RowExclusiveLock);
tuple = SearchSysCache(LANGNAME, tuple = SearchSysCache(LANGNAME,
...@@ -822,11 +881,19 @@ ExecuteGrantStmt_Language(GrantStmt *stmt) ...@@ -822,11 +881,19 @@ ExecuteGrantStmt_Language(GrantStmt *stmt)
/* get a detoasted copy of the ACL */ /* get a detoasted copy of the ACL */
old_acl = DatumGetAclPCopy(aclDatum); 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, new_acl = merge_acl_with_grant(old_acl, stmt->is_grant,
stmt->grant_option, stmt->behavior, stmt->grant_option, stmt->behavior,
stmt->grantees, this_privileges, stmt->grantees, this_privileges,
grantorId, ownerId); grantorId, ownerId);
nnewmembers = aclmembers(new_acl, &newmembers);
/* finished building new ACL value, now insert it */ /* finished building new ACL value, now insert it */
MemSet(values, 0, sizeof(values)); MemSet(values, 0, sizeof(values));
MemSet(nulls, ' ', sizeof(nulls)); MemSet(nulls, ' ', sizeof(nulls));
...@@ -837,13 +904,19 @@ ExecuteGrantStmt_Language(GrantStmt *stmt) ...@@ -837,13 +904,19 @@ ExecuteGrantStmt_Language(GrantStmt *stmt)
newtuple = heap_modifytuple(tuple, RelationGetDescr(relation), values, nulls, replaces); newtuple = heap_modifytuple(tuple, RelationGetDescr(relation), values, nulls, replaces);
ReleaseSysCache(tuple);
simple_heap_update(relation, &newtuple->t_self, newtuple); simple_heap_update(relation, &newtuple->t_self, newtuple);
/* keep the catalog indexes up to date */ /* keep the catalog indexes up to date */
CatalogUpdateIndexes(relation, newtuple); 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); pfree(new_acl);
heap_close(relation, RowExclusiveLock); heap_close(relation, RowExclusiveLock);
...@@ -898,6 +971,10 @@ ExecuteGrantStmt_Namespace(GrantStmt *stmt) ...@@ -898,6 +971,10 @@ ExecuteGrantStmt_Namespace(GrantStmt *stmt)
Datum values[Natts_pg_namespace]; Datum values[Natts_pg_namespace];
char nulls[Natts_pg_namespace]; char nulls[Natts_pg_namespace];
char replaces[Natts_pg_namespace]; char replaces[Natts_pg_namespace];
int noldmembers;
int nnewmembers;
Oid *oldmembers;
Oid *newmembers;
relation = heap_open(NamespaceRelationId, RowExclusiveLock); relation = heap_open(NamespaceRelationId, RowExclusiveLock);
tuple = SearchSysCache(NAMESPACENAME, tuple = SearchSysCache(NAMESPACENAME,
...@@ -977,11 +1054,19 @@ ExecuteGrantStmt_Namespace(GrantStmt *stmt) ...@@ -977,11 +1054,19 @@ ExecuteGrantStmt_Namespace(GrantStmt *stmt)
/* get a detoasted copy of the ACL */ /* get a detoasted copy of the ACL */
old_acl = DatumGetAclPCopy(aclDatum); 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, new_acl = merge_acl_with_grant(old_acl, stmt->is_grant,
stmt->grant_option, stmt->behavior, stmt->grant_option, stmt->behavior,
stmt->grantees, this_privileges, stmt->grantees, this_privileges,
grantorId, ownerId); grantorId, ownerId);
nnewmembers = aclmembers(new_acl, &newmembers);
/* finished building new ACL value, now insert it */ /* finished building new ACL value, now insert it */
MemSet(values, 0, sizeof(values)); MemSet(values, 0, sizeof(values));
MemSet(nulls, ' ', sizeof(nulls)); MemSet(nulls, ' ', sizeof(nulls));
...@@ -992,13 +1077,19 @@ ExecuteGrantStmt_Namespace(GrantStmt *stmt) ...@@ -992,13 +1077,19 @@ ExecuteGrantStmt_Namespace(GrantStmt *stmt)
newtuple = heap_modifytuple(tuple, RelationGetDescr(relation), values, nulls, replaces); newtuple = heap_modifytuple(tuple, RelationGetDescr(relation), values, nulls, replaces);
ReleaseSysCache(tuple);
simple_heap_update(relation, &newtuple->t_self, newtuple); simple_heap_update(relation, &newtuple->t_self, newtuple);
/* keep the catalog indexes up to date */ /* keep the catalog indexes up to date */
CatalogUpdateIndexes(relation, newtuple); 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); pfree(new_acl);
heap_close(relation, RowExclusiveLock); heap_close(relation, RowExclusiveLock);
...@@ -1055,6 +1146,10 @@ ExecuteGrantStmt_Tablespace(GrantStmt *stmt) ...@@ -1055,6 +1146,10 @@ ExecuteGrantStmt_Tablespace(GrantStmt *stmt)
Datum values[Natts_pg_tablespace]; Datum values[Natts_pg_tablespace];
char nulls[Natts_pg_tablespace]; char nulls[Natts_pg_tablespace];
char replaces[Natts_pg_tablespace]; char replaces[Natts_pg_tablespace];
int noldmembers;
int nnewmembers;
Oid *oldmembers;
Oid *newmembers;
relation = heap_open(TableSpaceRelationId, RowExclusiveLock); relation = heap_open(TableSpaceRelationId, RowExclusiveLock);
ScanKeyInit(&entry[0], ScanKeyInit(&entry[0],
...@@ -1136,11 +1231,19 @@ ExecuteGrantStmt_Tablespace(GrantStmt *stmt) ...@@ -1136,11 +1231,19 @@ ExecuteGrantStmt_Tablespace(GrantStmt *stmt)
/* get a detoasted copy of the ACL */ /* get a detoasted copy of the ACL */
old_acl = DatumGetAclPCopy(aclDatum); 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, new_acl = merge_acl_with_grant(old_acl, stmt->is_grant,
stmt->grant_option, stmt->behavior, stmt->grant_option, stmt->behavior,
stmt->grantees, this_privileges, stmt->grantees, this_privileges,
grantorId, ownerId); grantorId, ownerId);
nnewmembers = aclmembers(new_acl, &newmembers);
/* finished building new ACL value, now insert it */ /* finished building new ACL value, now insert it */
MemSet(values, 0, sizeof(values)); MemSet(values, 0, sizeof(values));
MemSet(nulls, ' ', sizeof(nulls)); MemSet(nulls, ' ', sizeof(nulls));
...@@ -1156,6 +1259,12 @@ ExecuteGrantStmt_Tablespace(GrantStmt *stmt) ...@@ -1156,6 +1259,12 @@ ExecuteGrantStmt_Tablespace(GrantStmt *stmt)
/* keep the catalog indexes up to date */ /* keep the catalog indexes up to date */
CatalogUpdateIndexes(relation, newtuple); CatalogUpdateIndexes(relation, newtuple);
/* Update the shared dependency ACL info */
updateAclDependencies(TableSpaceRelationId, HeapTupleGetOid(tuple),
ownerId, stmt->is_grant,
noldmembers, oldmembers,
nnewmembers, newmembers);
pfree(new_acl); pfree(new_acl);
heap_endscan(scan); heap_endscan(scan);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.44 2005/04/14 20:03:23 tgl Exp $ * $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.45 2005/07/07 20:39:57 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -22,9 +22,11 @@ ...@@ -22,9 +22,11 @@
#include "catalog/indexing.h" #include "catalog/indexing.h"
#include "catalog/namespace.h" #include "catalog/namespace.h"
#include "catalog/pg_attrdef.h" #include "catalog/pg_attrdef.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_cast.h" #include "catalog/pg_cast.h"
#include "catalog/pg_constraint.h" #include "catalog/pg_constraint.h"
#include "catalog/pg_conversion.h" #include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
#include "catalog/pg_depend.h" #include "catalog/pg_depend.h"
#include "catalog/pg_language.h" #include "catalog/pg_language.h"
#include "catalog/pg_namespace.h" #include "catalog/pg_namespace.h"
...@@ -32,12 +34,15 @@ ...@@ -32,12 +34,15 @@
#include "catalog/pg_operator.h" #include "catalog/pg_operator.h"
#include "catalog/pg_proc.h" #include "catalog/pg_proc.h"
#include "catalog/pg_rewrite.h" #include "catalog/pg_rewrite.h"
#include "catalog/pg_tablespace.h"
#include "catalog/pg_trigger.h" #include "catalog/pg_trigger.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "commands/comment.h" #include "commands/comment.h"
#include "commands/dbcommands.h"
#include "commands/defrem.h" #include "commands/defrem.h"
#include "commands/proclang.h" #include "commands/proclang.h"
#include "commands/schemacmds.h" #include "commands/schemacmds.h"
#include "commands/tablespace.h"
#include "commands/trigger.h" #include "commands/trigger.h"
#include "commands/typecmds.h" #include "commands/typecmds.h"
#include "lib/stringinfo.h" #include "lib/stringinfo.h"
...@@ -509,6 +514,7 @@ recursiveDeletion(const ObjectAddress *object, ...@@ -509,6 +514,7 @@ recursiveDeletion(const ObjectAddress *object,
break; break;
} }
/* delete the pg_depend tuple */
simple_heap_delete(depRel, &tup->t_self); simple_heap_delete(depRel, &tup->t_self);
} }
...@@ -584,6 +590,14 @@ recursiveDeletion(const ObjectAddress *object, ...@@ -584,6 +590,14 @@ recursiveDeletion(const ObjectAddress *object,
*/ */
DeleteComments(object->objectId, object->classId, object->objectSubId); 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 * CommandCounterIncrement here to ensure that preceding changes are
* all visible. * all visible.
...@@ -1365,6 +1379,18 @@ getObjectClass(const ObjectAddress *object) ...@@ -1365,6 +1379,18 @@ getObjectClass(const ObjectAddress *object)
case NamespaceRelationId: case NamespaceRelationId:
Assert(object->objectSubId == 0); Assert(object->objectSubId == 0);
return OCLASS_SCHEMA; 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 */ /* shouldn't get here */
...@@ -1680,6 +1706,37 @@ getObjectDescription(const ObjectAddress *object) ...@@ -1680,6 +1706,37 @@ getObjectDescription(const ObjectAddress *object)
break; 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: default:
appendStringInfo(&buffer, "unrecognized object %u %u %d", appendStringInfo(&buffer, "unrecognized object %u %u %d",
object->classId, object->classId,
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * INTERFACE ROUTINES
...@@ -755,6 +755,8 @@ heap_create_with_catalog(const char *relname, ...@@ -755,6 +755,8 @@ heap_create_with_catalog(const char *relname,
* make a dependency link to force the relation to be deleted if its * 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 * namespace is. Skip this in bootstrap mode, since we don't make
* dependencies while bootstrapping. * dependencies while bootstrapping.
*
* Also make a dependency link to its owner.
*/ */
if (!IsBootstrapProcessingMode()) if (!IsBootstrapProcessingMode())
{ {
...@@ -768,6 +770,8 @@ heap_create_with_catalog(const char *relname, ...@@ -768,6 +770,8 @@ heap_create_with_catalog(const char *relname,
referenced.objectId = relnamespace; referenced.objectId = relnamespace;
referenced.objectSubId = 0; referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
recordDependencyOnOwner(RelationRelationId, new_rel_oid, GetUserId());
} }
/* /*
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -120,6 +120,10 @@ ConversionCreate(const char *conname, Oid connamespace,
referenced.objectSubId = 0; referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
/* create dependency on owner */
recordDependencyOnOwner(ConversionRelationId, HeapTupleGetOid(tup),
conowner);
heap_freetuple(tup); heap_freetuple(tup);
heap_close(rel, RowExclusiveLock); heap_close(rel, RowExclusiveLock);
......
...@@ -8,13 +8,14 @@ ...@@ -8,13 +8,14 @@
* *
* *
* IDENTIFICATION * 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 "postgres.h"
#include "access/heapam.h" #include "access/heapam.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h" #include "catalog/indexing.h"
#include "catalog/pg_namespace.h" #include "catalog/pg_namespace.h"
#include "utils/builtins.h" #include "utils/builtins.h"
...@@ -72,5 +73,8 @@ NamespaceCreate(const char *nspName, Oid ownerId) ...@@ -72,5 +73,8 @@ NamespaceCreate(const char *nspName, Oid ownerId)
heap_close(nspdesc, RowExclusiveLock); heap_close(nspdesc, RowExclusiveLock);
/* Record dependency on owner */
recordDependencyOnOwner(NamespaceRelationId, nspoid, ownerId);
return nspoid; return nspoid;
} }
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * NOTES
* these routines moved here from commands/define.c and somewhat cleaned up. * these routines moved here from commands/define.c and somewhat cleaned up.
...@@ -889,6 +889,7 @@ makeOperatorDependencies(HeapTuple tuple) ...@@ -889,6 +889,7 @@ makeOperatorDependencies(HeapTuple tuple)
/* In case we are updating a shell, delete any existing entries */ /* In case we are updating a shell, delete any existing entries */
deleteDependencyRecordsFor(myself.classId, myself.objectId); deleteDependencyRecordsFor(myself.classId, myself.objectId);
deleteSharedDependencyRecordsFor(myself.classId, myself.objectId);
/* Dependency on namespace */ /* Dependency on namespace */
if (OidIsValid(oper->oprnamespace)) if (OidIsValid(oper->oprnamespace))
...@@ -962,4 +963,8 @@ makeOperatorDependencies(HeapTuple tuple) ...@@ -962,4 +963,8 @@ makeOperatorDependencies(HeapTuple tuple)
referenced.objectSubId = 0; referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
} }
/* Dependency on owner */
recordDependencyOnOwner(OperatorRelationId, HeapTupleGetOid(tuple),
oper->oprowner);
} }
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -349,7 +349,10 @@ ProcedureCreate(const char *procedureName,
* existing function, first delete any existing pg_depend entries. * existing function, first delete any existing pg_depend entries.
*/ */
if (is_update) if (is_update)
{
deleteDependencyRecordsFor(ProcedureRelationId, retval); deleteDependencyRecordsFor(ProcedureRelationId, retval);
deleteSharedDependencyRecordsFor(ProcedureRelationId, retval);
}
myself.classId = ProcedureRelationId; myself.classId = ProcedureRelationId;
myself.objectId = retval; myself.objectId = retval;
...@@ -382,6 +385,9 @@ ProcedureCreate(const char *procedureName, ...@@ -382,6 +385,9 @@ ProcedureCreate(const char *procedureName,
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
} }
/* dependency on owner */
recordDependencyOnOwner(ProcedureRelationId, retval, GetUserId());
heap_freetuple(tup); heap_freetuple(tup);
heap_close(rel, RowExclusiveLock); heap_close(rel, RowExclusiveLock);
......
/*-------------------------------------------------------------------------
*
* 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;
}
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -75,7 +75,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace)
namestrcpy(&name, typeName); namestrcpy(&name, typeName);
values[i++] = NameGetDatum(&name); /* typname */ values[i++] = NameGetDatum(&name); /* typname */
values[i++] = ObjectIdGetDatum(typeNamespace); /* typnamespace */ values[i++] = ObjectIdGetDatum(typeNamespace); /* typnamespace */
values[i++] = ObjectIdGetDatum(InvalidOid); /* typowner */ values[i++] = ObjectIdGetDatum(GetUserId()); /* typowner */
values[i++] = Int16GetDatum(0); /* typlen */ values[i++] = Int16GetDatum(0); /* typlen */
values[i++] = BoolGetDatum(false); /* typbyval */ values[i++] = BoolGetDatum(false); /* typbyval */
values[i++] = CharGetDatum(0); /* typtype */ values[i++] = CharGetDatum(0); /* typtype */
...@@ -117,6 +117,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace) ...@@ -117,6 +117,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace)
typoid, typoid,
InvalidOid, InvalidOid,
0, 0,
GetUserId(),
InvalidOid, InvalidOid,
InvalidOid, InvalidOid,
InvalidOid, InvalidOid,
...@@ -330,6 +331,7 @@ TypeCreate(const char *typeName, ...@@ -330,6 +331,7 @@ TypeCreate(const char *typeName,
typeObjectId, typeObjectId,
relationOid, relationOid,
relationKind, relationKind,
GetUserId(),
inputProcedure, inputProcedure,
outputProcedure, outputProcedure,
receiveProcedure, receiveProcedure,
...@@ -365,6 +367,7 @@ GenerateTypeDependencies(Oid typeNamespace, ...@@ -365,6 +367,7 @@ GenerateTypeDependencies(Oid typeNamespace,
Oid relationOid, /* only for 'c'atalog Oid relationOid, /* only for 'c'atalog
* types */ * types */
char relationKind, /* ditto */ char relationKind, /* ditto */
Oid owner,
Oid inputProcedure, Oid inputProcedure,
Oid outputProcedure, Oid outputProcedure,
Oid receiveProcedure, Oid receiveProcedure,
...@@ -379,7 +382,10 @@ GenerateTypeDependencies(Oid typeNamespace, ...@@ -379,7 +382,10 @@ GenerateTypeDependencies(Oid typeNamespace,
referenced; referenced;
if (rebuild) if (rebuild)
{
deleteDependencyRecordsFor(TypeRelationId, typeObjectId); deleteDependencyRecordsFor(TypeRelationId, typeObjectId);
deleteSharedDependencyRecordsFor(TypeRelationId, typeObjectId);
}
myself.classId = TypeRelationId; myself.classId = TypeRelationId;
myself.objectId = typeObjectId; myself.objectId = typeObjectId;
...@@ -485,6 +491,9 @@ GenerateTypeDependencies(Oid typeNamespace, ...@@ -485,6 +491,9 @@ GenerateTypeDependencies(Oid typeNamespace,
/* Normal dependency on the default expression. */ /* Normal dependency on the default expression. */
if (defaultExpr) if (defaultExpr)
recordDependencyOnExpr(&myself, defaultExpr, NIL, DEPENDENCY_NORMAL); recordDependencyOnExpr(&myself, defaultExpr, NIL, DEPENDENCY_NORMAL);
/* Shared dependency on owner. */
recordDependencyOnOwner(TypeRelationId, typeObjectId, owner);
} }
/* /*
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 @@ ...@@ -17,6 +17,7 @@
#include "catalog/pg_conversion.h" #include "catalog/pg_conversion.h"
#include "access/heapam.h" #include "access/heapam.h"
#include "catalog/catalog.h" #include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h" #include "catalog/indexing.h"
#include "catalog/namespace.h" #include "catalog/namespace.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
...@@ -220,6 +221,10 @@ AlterConversionOwner(List *name, Oid newOwnerId) ...@@ -220,6 +221,10 @@ AlterConversionOwner(List *name, Oid newOwnerId)
simple_heap_update(rel, &tup->t_self, tup); simple_heap_update(rel, &tup->t_self, tup);
CatalogUpdateIndexes(rel, tup); CatalogUpdateIndexes(rel, tup);
/* Update owner dependency reference */
changeDependencyOnOwner(ConversionRelationId, conversionOid,
newOwnerId);
} }
heap_close(rel, NoLock); heap_close(rel, NoLock);
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* *
* *
* IDENTIFICATION * 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 @@ ...@@ -28,6 +28,7 @@
#include "access/genam.h" #include "access/genam.h"
#include "access/heapam.h" #include "access/heapam.h"
#include "catalog/catalog.h" #include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h" #include "catalog/indexing.h"
#include "catalog/pg_authid.h" #include "catalog/pg_authid.h"
#include "catalog/pg_database.h" #include "catalog/pg_database.h"
...@@ -511,6 +512,12 @@ createdb(const CreatedbStmt *stmt) ...@@ -511,6 +512,12 @@ createdb(const CreatedbStmt *stmt)
/* Update indexes */ /* Update indexes */
CatalogUpdateIndexes(pg_database_rel, tuple); 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 */ /* Close pg_database, but keep exclusive lock till commit */
heap_close(pg_database_rel, NoLock); heap_close(pg_database_rel, NoLock);
...@@ -679,6 +686,11 @@ dropdb(const char *dbname) ...@@ -679,6 +686,11 @@ dropdb(const char *dbname)
/* Close pg_database, but keep exclusive lock till commit */ /* Close pg_database, but keep exclusive lock till commit */
heap_close(pgdbrel, NoLock); heap_close(pgdbrel, NoLock);
/*
* Remove shared dependency references for the database.
*/
dropDatabaseDependencies(db_id);
/* /*
* Set flag to update flat database file at commit. * Set flag to update flat database file at commit.
*/ */
...@@ -951,6 +963,10 @@ AlterDatabaseOwner(const char *dbname, Oid newOwnerId) ...@@ -951,6 +963,10 @@ AlterDatabaseOwner(const char *dbname, Oid newOwnerId)
CatalogUpdateIndexes(rel, newtuple); CatalogUpdateIndexes(rel, newtuple);
heap_freetuple(newtuple); heap_freetuple(newtuple);
/* Update owner dependency reference */
changeDependencyOnOwner(DatabaseRelationId, HeapTupleGetOid(tuple),
newOwnerId);
} }
systable_endscan(scan); systable_endscan(scan);
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * 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 * DESCRIPTION
* These routines take the parse tree and pick out the * These routines take the parse tree and pick out the
...@@ -925,6 +925,9 @@ AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId) ...@@ -925,6 +925,9 @@ AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId)
CatalogUpdateIndexes(rel, newtuple); CatalogUpdateIndexes(rel, newtuple);
heap_freetuple(newtuple); heap_freetuple(newtuple);
/* Update owner dependency reference */
changeDependencyOnOwner(ProcedureRelationId, procOid, newOwnerId);
} }
ReleaseSysCache(tup); ReleaseSysCache(tup);
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -392,6 +392,9 @@ DefineOpClass(CreateOpClassStmt *stmt)
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
} }
/* dependency on owner */
recordDependencyOnOwner(OperatorClassRelationId, opclassoid, GetUserId());
heap_close(rel, RowExclusiveLock); heap_close(rel, RowExclusiveLock);
} }
...@@ -962,6 +965,9 @@ AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId) ...@@ -962,6 +965,9 @@ AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId)
simple_heap_update(rel, &tup->t_self, tup); simple_heap_update(rel, &tup->t_self, tup);
CatalogUpdateIndexes(rel, tup); CatalogUpdateIndexes(rel, tup);
/* Update owner dependency reference */
changeDependencyOnOwner(OperatorClassRelationId, amOid, newOwnerId);
} }
heap_close(rel, NoLock); heap_close(rel, NoLock);
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * 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 * DESCRIPTION
* The "DefineFoo" routines take the parse tree and pick out the * The "DefineFoo" routines take the parse tree and pick out the
...@@ -310,6 +310,9 @@ AlterOperatorOwner(List *name, TypeName *typeName1, TypeName *typeName2, ...@@ -310,6 +310,9 @@ AlterOperatorOwner(List *name, TypeName *typeName1, TypeName *typeName2,
simple_heap_update(rel, &tup->t_self, tup); simple_heap_update(rel, &tup->t_self, tup);
CatalogUpdateIndexes(rel, tup); CatalogUpdateIndexes(rel, tup);
/* Update owner dependency reference */
changeDependencyOnOwner(OperatorRelationId, operOid, newOwnerId);
} }
heap_close(rel, NoLock); heap_close(rel, NoLock);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -342,6 +342,10 @@ AlterSchemaOwner(const char *name, Oid newOwnerId)
CatalogUpdateIndexes(rel, newtuple); CatalogUpdateIndexes(rel, newtuple);
heap_freetuple(newtuple); heap_freetuple(newtuple);
/* Update owner dependency reference */
changeDependencyOnOwner(NamespaceRelationId, HeapTupleGetOid(tup),
newOwnerId);
} }
ReleaseSysCache(tup); ReleaseSysCache(tup);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -5321,6 +5321,9 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId)
heap_freetuple(newtuple); heap_freetuple(newtuple);
/* Update owner dependency reference */
changeDependencyOnOwner(RelationRelationId, relationOid, newOwnerId);
/* /*
* If we are operating on a table, also change the ownership of * If we are operating on a table, also change the ownership of
* any indexes and sequences that belong to the table, as well as * any indexes and sequences that belong to the table, as well as
......
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
* *
* *
* IDENTIFICATION * 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 @@ ...@@ -50,6 +50,7 @@
#include "access/heapam.h" #include "access/heapam.h"
#include "catalog/catalog.h" #include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h" #include "catalog/indexing.h"
#include "catalog/pg_namespace.h" #include "catalog/pg_namespace.h"
#include "catalog/pg_tablespace.h" #include "catalog/pg_tablespace.h"
...@@ -307,6 +308,9 @@ CreateTableSpace(CreateTableSpaceStmt *stmt) ...@@ -307,6 +308,9 @@ CreateTableSpace(CreateTableSpaceStmt *stmt)
heap_freetuple(tuple); heap_freetuple(tuple);
/* Record dependency on owner */
recordDependencyOnOwner(TableSpaceRelationId, tablespaceoid, ownerId);
/* /*
* Attempt to coerce target directory to safe permissions. If this * Attempt to coerce target directory to safe permissions. If this
* fails, it doesn't exist or has the wrong owner. * fails, it doesn't exist or has the wrong owner.
...@@ -818,6 +822,10 @@ AlterTableSpaceOwner(const char *name, Oid newOwnerId) ...@@ -818,6 +822,10 @@ AlterTableSpaceOwner(const char *name, Oid newOwnerId)
CatalogUpdateIndexes(rel, newtuple); CatalogUpdateIndexes(rel, newtuple);
heap_freetuple(newtuple); heap_freetuple(newtuple);
/* Update owner dependency reference */
changeDependencyOnOwner(TableSpaceRelationId, HeapTupleGetOid(tup),
newOwnerId);
} }
heap_endscan(scandesc); heap_endscan(scandesc);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * DESCRIPTION
* The "DefineFoo" routines take the parse tree and pick out the * The "DefineFoo" routines take the parse tree and pick out the
...@@ -1208,6 +1208,7 @@ AlterDomainDefault(List *names, Node *defaultRaw) ...@@ -1208,6 +1208,7 @@ AlterDomainDefault(List *names, Node *defaultRaw)
domainoid, domainoid,
typTup->typrelid, typTup->typrelid,
0, /* relation kind is n/a */ 0, /* relation kind is n/a */
typTup->typowner,
typTup->typinput, typTup->typinput,
typTup->typoutput, typTup->typoutput,
typTup->typreceive, typTup->typreceive,
...@@ -2080,6 +2081,9 @@ AlterTypeOwner(List *names, Oid newOwnerId) ...@@ -2080,6 +2081,9 @@ AlterTypeOwner(List *names, Oid newOwnerId)
simple_heap_update(rel, &tup->t_self, tup); simple_heap_update(rel, &tup->t_self, tup);
CatalogUpdateIndexes(rel, tup); CatalogUpdateIndexes(rel, tup);
/* Update owner dependency reference */
changeDependencyOnOwner(TypeRelationId, typeOid, newOwnerId);
} }
/* Clean up */ /* Clean up */
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/backend/commands/user.c,v 1.155 2005/06/29 20:34:13 tgl Exp $ * $PostgreSQL: pgsql/src/backend/commands/user.c,v 1.156 2005/07/07 20:39:58 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -14,10 +14,10 @@ ...@@ -14,10 +14,10 @@
#include "access/genam.h" #include "access/genam.h"
#include "access/heapam.h" #include "access/heapam.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h" #include "catalog/indexing.h"
#include "catalog/pg_auth_members.h" #include "catalog/pg_auth_members.h"
#include "catalog/pg_authid.h" #include "catalog/pg_authid.h"
#include "catalog/pg_database.h"
#include "commands/user.h" #include "commands/user.h"
#include "libpq/crypt.h" #include "libpq/crypt.h"
#include "miscadmin.h" #include "miscadmin.h"
...@@ -742,10 +742,8 @@ DropRole(DropRoleStmt *stmt) ...@@ -742,10 +742,8 @@ DropRole(DropRoleStmt *stmt)
const char *role = strVal(lfirst(item)); const char *role = strVal(lfirst(item));
HeapTuple tuple, HeapTuple tuple,
tmp_tuple; tmp_tuple;
Relation pg_rel; ScanKeyData scankey;
TupleDesc pg_dsc; char *detail;
ScanKeyData scankey;
HeapScanDesc scan;
SysScanDesc sscan; SysScanDesc sscan;
Oid roleid; Oid roleid;
...@@ -780,42 +778,18 @@ DropRole(DropRoleStmt *stmt) ...@@ -780,42 +778,18 @@ DropRole(DropRoleStmt *stmt)
errmsg("must be superuser to drop superusers"))); errmsg("must be superuser to drop superusers")));
/* /*
* Check if role still owns a database. If so, error out. * Lock the role, so nobody can add dependencies to her while we drop
* * her. We keep the lock until the end of transaction.
* (It used to be that this function would drop the database */
* automatically. This is not only very dangerous for people that LockSharedObject(AuthIdRelationId, roleid, 0, AccessExclusiveLock);
* don't read the manual, it doesn't seem to be the behaviour one
* would expect either.) -- petere 2000/01/14)
*/
pg_rel = heap_open(DatabaseRelationId, AccessShareLock);
pg_dsc = RelationGetDescr(pg_rel);
ScanKeyInit(&scankey,
Anum_pg_database_datdba,
BTEqualStrategyNumber, F_OIDEQ,
roleid);
scan = heap_beginscan(pg_rel, SnapshotNow, 1, &scankey);
if ((tmp_tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
char *dbname;
dbname = NameStr(((Form_pg_database) GETSTRUCT(tmp_tuple))->datname); /* Check for pg_shdepend entries depending on this role */
if ((detail = checkSharedDependencies(AuthIdRelationId, roleid)) != NULL)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_OBJECT_IN_USE), (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
errmsg("role \"%s\" cannot be dropped", role), errmsg("role \"%s\" cannot be dropped because some objects depend on it",
errdetail("The role owns database \"%s\".", dbname))); role),
} errdetail("%s", detail)));
heap_endscan(scan);
heap_close(pg_rel, AccessShareLock);
/*
* Somehow we'd have to check for tables, views, etc. owned by the
* role as well, but those could be spread out over all sorts of
* databases which we don't have access to (easily).
*/
/* /*
* Remove the role from the pg_authid table * Remove the role from the pg_authid table
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -59,6 +59,7 @@ static void check_circularity(const Acl *old_acl, const AclItem *mod_aip,
Oid ownerId); Oid ownerId);
static Acl *recursive_revoke(Acl *acl, Oid grantee, AclMode revoke_privs, static Acl *recursive_revoke(Acl *acl, Oid grantee, AclMode revoke_privs,
Oid ownerId, DropBehavior behavior); Oid ownerId, DropBehavior behavior);
static int oidComparator(const void *arg1, const void *arg2);
static AclMode convert_priv_string(text *priv_type_text); static AclMode convert_priv_string(text *priv_type_text);
...@@ -1052,6 +1053,86 @@ aclmask(const Acl *acl, Oid roleid, Oid ownerId, ...@@ -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) * aclinsert (exported function)
*/ */
......
...@@ -42,7 +42,7 @@ ...@@ -42,7 +42,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* Portions taken from FreeBSD. * 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) ...@@ -1512,6 +1512,10 @@ setup_depend(void)
* must be the only entries for their objects. * must be the only entries for their objects.
*/ */
"DELETE FROM pg_depend;\n", "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' " "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
" FROM pg_class;\n", " FROM pg_class;\n",
"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' " "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
...@@ -1541,10 +1545,13 @@ setup_depend(void) ...@@ -1541,10 +1545,13 @@ setup_depend(void)
"INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' " "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
" FROM pg_namespace " " FROM pg_namespace "
" WHERE nspname LIKE 'pg%';\n", " WHERE nspname LIKE 'pg%';\n",
"INSERT INTO pg_shdepend SELECT 0, 0, 0, tableoid, oid, 'p' "
" FROM pg_authid;\n",
NULL NULL
}; };
fputs(_("initializing pg_depend ... "), stdout); fputs(_("initializing dependencies ... "), stdout);
fflush(stdout); fflush(stdout);
snprintf(cmd, sizeof(cmd), snprintf(cmd, sizeof(cmd),
......
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 @@ ...@@ -53,6 +53,6 @@
*/ */
/* yyyymmddN */ /* yyyymmddN */
#define CATALOG_VERSION_NO 200507011 #define CATALOG_VERSION_NO 200507071
#endif #endif
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 ...@@ -67,6 +67,40 @@ typedef enum DependencyType
DEPENDENCY_PIN = 'p' DEPENDENCY_PIN = 'p'
} DependencyType; } 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. * The two objects related by a dependency are identified by ObjectAddresses.
...@@ -81,7 +115,8 @@ typedef struct ObjectAddress ...@@ -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 typedef enum ObjectClass
{ {
...@@ -98,6 +133,9 @@ typedef enum ObjectClass ...@@ -98,6 +133,9 @@ typedef enum ObjectClass
OCLASS_REWRITE, /* pg_rewrite */ OCLASS_REWRITE, /* pg_rewrite */
OCLASS_TRIGGER, /* pg_trigger */ OCLASS_TRIGGER, /* pg_trigger */
OCLASS_SCHEMA, /* pg_namespace */ OCLASS_SCHEMA, /* pg_namespace */
OCLASS_ROLE, /* pg_authid */
OCLASS_DATABASE, /* pg_database */
OCLASS_TBLSPACE, /* pg_tablespace */
MAX_OCLASS /* MUST BE LAST */ MAX_OCLASS /* MUST BE LAST */
} ObjectClass; } ObjectClass;
...@@ -136,4 +174,28 @@ extern void recordMultipleDependencies(const ObjectAddress *depender, ...@@ -136,4 +174,28 @@ extern void recordMultipleDependencies(const ObjectAddress *depender,
extern long deleteDependencyRecordsFor(Oid classId, Oid objectId); 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 */ #endif /* DEPENDENCY_H */
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 ...@@ -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)); DECLARE_UNIQUE_INDEX(pg_rewrite_rel_rulename_index,2693, on pg_rewrite using btree(ev_class oid_ops, rulename name_ops));
#define RewriteRelRulenameIndexId 2693 #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)); DECLARE_UNIQUE_INDEX(pg_statistic_relid_att_index,2696, on pg_statistic using btree(starelid oid_ops, staattnum int2_ops));
#define StatisticRelidAttnumIndexId 2696 #define StatisticRelidAttnumIndexId 2696
......
/*-------------------------------------------------------------------------
*
* 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 */
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 * NOTES
* the genbki.sh script reads this file and generates .bki * the genbki.sh script reads this file and generates .bki
...@@ -582,6 +582,7 @@ extern void GenerateTypeDependencies(Oid typeNamespace, ...@@ -582,6 +582,7 @@ extern void GenerateTypeDependencies(Oid typeNamespace,
Oid typeObjectId, Oid typeObjectId,
Oid relationOid, Oid relationOid,
char relationKind, char relationKind,
Oid owner,
Oid inputProcedure, Oid inputProcedure,
Oid outputProcedure, Oid outputProcedure,
Oid receiveProcedure, Oid receiveProcedure,
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 * NOTES
* An ACL array is simply an array of AclItems, representing the union * 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); ...@@ -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, extern AclMode aclmask(const Acl *acl, Oid roleid, Oid ownerId,
AclMode mask, AclMaskHow how); 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_member_of_role(Oid member, Oid role);
extern bool is_admin_of_role(Oid member, Oid role); extern bool is_admin_of_role(Oid member, Oid role);
......
...@@ -385,5 +385,6 @@ SELECT * FROM clstr_1; ...@@ -385,5 +385,6 @@ SELECT * FROM clstr_1;
-- clean up -- clean up
\c - \c -
DROP TABLE clstr_1; DROP TABLE clstr_1;
DROP TABLE clstr_2;
DROP TABLE clstr_3; DROP TABLE clstr_3;
DROP USER clstr_user; DROP USER clstr_user;
--
-- 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;
...@@ -601,6 +601,7 @@ DROP TABLE atest3; ...@@ -601,6 +601,7 @@ DROP TABLE atest3;
DROP TABLE atest4; DROP TABLE atest4;
DROP GROUP regressgroup1; DROP GROUP regressgroup1;
DROP GROUP regressgroup2; DROP GROUP regressgroup2;
REVOKE USAGE ON LANGUAGE sql FROM regressuser1;
DROP USER regressuser1; DROP USER regressuser1;
DROP USER regressuser2; DROP USER regressuser2;
DROP USER regressuser3; DROP USER regressuser3;
......
...@@ -56,6 +56,7 @@ SELECT relname, relhasindex ...@@ -56,6 +56,7 @@ SELECT relname, relhasindex
pg_operator | t pg_operator | t
pg_proc | t pg_proc | t
pg_rewrite | t pg_rewrite | t
pg_shdepend | t
pg_statistic | t pg_statistic | t
pg_tablespace | t pg_tablespace | t
pg_trigger | t pg_trigger | t
...@@ -65,7 +66,7 @@ SELECT relname, relhasindex ...@@ -65,7 +66,7 @@ SELECT relname, relhasindex
shighway | t shighway | t
tenk1 | t tenk1 | t
tenk2 | t tenk2 | t
(55 rows) (56 rows)
-- --
-- another sanity check: every system catalog that has OIDs should have -- another sanity check: every system catalog that has OIDs should have
......
...@@ -68,7 +68,7 @@ test: misc ...@@ -68,7 +68,7 @@ test: misc
# ---------- # ----------
# The fifth group of parallel test # 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 # The sixth group of parallel test
......
# $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. # This should probably be in an order similar to parallel_schedule.
test: boolean test: boolean
test: char test: char
...@@ -98,3 +98,4 @@ test: polymorphism ...@@ -98,3 +98,4 @@ test: polymorphism
test: rowtypes test: rowtypes
test: stats test: stats
test: tablespace test: tablespace
test: dependency
...@@ -156,5 +156,6 @@ SELECT * FROM clstr_1; ...@@ -156,5 +156,6 @@ SELECT * FROM clstr_1;
-- clean up -- clean up
\c - \c -
DROP TABLE clstr_1; DROP TABLE clstr_1;
DROP TABLE clstr_2;
DROP TABLE clstr_3; DROP TABLE clstr_3;
DROP USER clstr_user; DROP USER clstr_user;
--
-- 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;
...@@ -339,6 +339,7 @@ DROP TABLE atest4; ...@@ -339,6 +339,7 @@ DROP TABLE atest4;
DROP GROUP regressgroup1; DROP GROUP regressgroup1;
DROP GROUP regressgroup2; DROP GROUP regressgroup2;
REVOKE USAGE ON LANGUAGE sql FROM regressuser1;
DROP USER regressuser1; DROP USER regressuser1;
DROP USER regressuser2; DROP USER regressuser2;
DROP USER regressuser3; DROP USER regressuser3;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment