Hackers, Here is the current shared dependency patch I promised. (The new files are src/include/catalog/pg_shdepend.h and src/backend/catalog/pg_shdepend.c).
The big problem with the current patch is this: -- session 1 BEGIN; DROP USER foo; -- checks dependencies, all is OK -- session 2 ALTER TABLE foo OWNER TO foo; COMMIT; Everything works, a dependency on user foo is recorded, but now it's useless (it will be never checked). Of course, there needs to be a lock to protect this from happening. But I'm not sure what should be locked. The whole pg_shadow relation? That might be overkill. I was trying to find out if I could lock the user (and have the ALTER TABLE get a shared lock on the user before checking its existance, and the DROP USER get an exclusive lock which would be release at transaction end. So everything would remain consistant.) However the LOCKTAG does not have provisions to lock arbitrary objects, only relations (I could end up locking some completely unrelated table, I guess). Any ideas on how to handle this? -- Alvaro Herrera (<alvherre[a]dcc.uchile.cl>) "Aprende a avergonzarte más ante ti que ante los demás" (Demócrito)
Index: doc/src/sgml/catalogs.sgml =================================================================== RCS file: /home/alvherre/cvs/pgsql/doc/src/sgml/catalogs.sgml,v retrieving revision 2.94 diff -c -r2.94 catalogs.sgml *** doc/src/sgml/catalogs.sgml 13 Dec 2004 18:05:07 -0000 2.94 --- doc/src/sgml/catalogs.sgml 13 Dec 2004 20:15:32 -0000 *************** *** 174,179 **** --- 174,183 ---- </row> <row> + <entry><link linkend="catalog-pg-shdepend"><structname>pg_shdepend</structname></link</entry> + <entry>cross-database dependencies between objects</entry> + </row> + <row> <entry><link linkend="catalog-pg-statistic"><structname>pg_statistic</structname></link></entry> <entry>planner statistics</entry> </row> *************** *** 3095,3100 **** --- 3099,3174 ---- </sect1> + <sect1 id="catalog-pg-shdepend"> + <title><structname>pg_shdepend</structname></title> + + <indexterm zone="catalog-pg-shdepend"> + <primary>pg_shdepend</primary> + </indexterm> + + <para> + The shared catalog <structname>pg_shdepend</structname> records the + dependency relationships between database objects and global objects, + such as users and tablespaces. This information allows <command>DROP + USER</command> and <command>DROP TABLESPACE</command> to ensure that + those objects are unreferenced before attempting to delete them. + </para> + + <table> + <title><structname>pg_depend</> 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</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</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> + + </tbody> + </tgroup> + </table> + </sect1> + + <sect1 id="catalog-pg-statistic"> <title><structname>pg_statistic</structname></title> Index: src/backend/catalog/Makefile =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/catalog/Makefile,v retrieving revision 1.53 diff -c -r1.53 Makefile *** src/backend/catalog/Makefile 21 Jul 2004 20:34:45 -0000 1.53 --- src/backend/catalog/Makefile 10 Dec 2004 14:21:47 -0000 *************** *** 12,18 **** OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \ pg_aggregate.o pg_constraint.o pg_conversion.o pg_depend.o \ ! pg_largeobject.o pg_namespace.o pg_operator.o pg_proc.o pg_type.o BKIFILES = postgres.bki postgres.description --- 12,19 ---- OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \ pg_aggregate.o pg_constraint.o pg_conversion.o pg_depend.o \ ! pg_largeobject.o pg_namespace.o pg_operator.o pg_proc.o pg_shdepend.o \ ! pg_type.o BKIFILES = postgres.bki postgres.description *************** *** 32,38 **** pg_language.h pg_largeobject.h pg_aggregate.h pg_statistic.h \ pg_rewrite.h pg_trigger.h pg_listener.h pg_description.h pg_cast.h \ pg_namespace.h pg_conversion.h pg_database.h pg_shadow.h pg_group.h \ ! pg_tablespace.h pg_depend.h indexing.h \ ) pg_includes := $(sort -I$(top_srcdir)/src/include -I$(top_builddir)/src/include) --- 33,39 ---- pg_language.h pg_largeobject.h pg_aggregate.h pg_statistic.h \ pg_rewrite.h pg_trigger.h pg_listener.h pg_description.h pg_cast.h \ pg_namespace.h pg_conversion.h pg_database.h pg_shadow.h pg_group.h \ ! pg_tablespace.h pg_depend.h pg_shdepend.h indexing.h \ ) pg_includes := $(sort -I$(top_srcdir)/src/include -I$(top_builddir)/src/include) Index: src/backend/catalog/catalog.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/catalog/catalog.c,v retrieving revision 1.56 diff -c -r1.56 catalog.c *** src/backend/catalog/catalog.c 29 Aug 2004 04:12:26 -0000 1.56 --- src/backend/catalog/catalog.c 10 Dec 2004 20:48:52 -0000 *************** *** 9,15 **** * * * IDENTIFICATION ! * $PostgreSQL: pgsql/src/backend/catalog/catalog.c,v 1.56 2004/08/29 04:12:26 momjian Exp $ * *------------------------------------------------------------------------- */ --- 9,15 ---- * * * IDENTIFICATION ! * $PostgreSQL: pgsql-server/src/backend/catalog/catalog.c,v 1.55 2004/08/04 21:33:47 tgl Exp $ * *------------------------------------------------------------------------- */ Index: src/backend/catalog/dependency.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/catalog/dependency.c,v retrieving revision 1.40 diff -c -r1.40 dependency.c *** src/backend/catalog/dependency.c 12 Oct 2004 21:54:36 -0000 1.40 --- src/backend/catalog/dependency.c 10 Dec 2004 14:32:52 -0000 *************** *** 32,40 **** --- 32,42 ---- #include "catalog/pg_rewrite.h" #include "catalog/pg_trigger.h" #include "commands/comment.h" + #include "commands/dbcommands.h" #include "commands/defrem.h" #include "commands/proclang.h" #include "commands/schemacmds.h" + #include "commands/tablespace.h" #include "commands/trigger.h" #include "commands/typecmds.h" #include "lib/stringinfo.h" *************** *** 496,501 **** --- 498,504 ---- break; } + /* delete the pg_depend tuple */ simple_heap_delete(depRel, &tup->t_self); } *************** *** 572,577 **** --- 575,588 ---- DeleteComments(object->objectId, object->classId, object->objectSubId); /* + * Delete shared dependency references related to this object. + * Sub-objects (columns) don't have dependencies on global objects, + * so skip them. + */ + if (object->objectSubId == 0) + deleteSharedDependencyRecordsFor(object->classId, object->objectId); + + /* * CommandCounterIncrement here to ensure that preceding changes are * all visible. */ *************** *** 1305,1313 **** --- 1316,1327 ---- static void init_object_classes(void) { + object_classes[OCLASS_AM] = get_system_catalog_relid(AccessMethodRelationName); object_classes[OCLASS_CLASS] = RelOid_pg_class; object_classes[OCLASS_PROC] = RelOid_pg_proc; object_classes[OCLASS_TYPE] = RelOid_pg_type; + object_classes[OCLASS_DATABASE] = RelOid_pg_database; + object_classes[OCLASS_SHADOW] = RelOid_pg_shadow; object_classes[OCLASS_CAST] = get_system_catalog_relid(CastRelationName); object_classes[OCLASS_CONSTRAINT] = get_system_catalog_relid(ConstraintRelationName); object_classes[OCLASS_CONVERSION] = get_system_catalog_relid(ConversionRelationName); *************** *** 1318,1323 **** --- 1332,1338 ---- object_classes[OCLASS_REWRITE] = get_system_catalog_relid(RewriteRelationName); object_classes[OCLASS_TRIGGER] = get_system_catalog_relid(TriggerRelationName); object_classes[OCLASS_SCHEMA] = get_system_catalog_relid(NamespaceRelationName); + object_classes[OCLASS_TBLSPACE] = RelOid_pg_tablespace; object_classes_initialized = true; } *************** *** 1344,1349 **** --- 1359,1376 ---- case RelOid_pg_type: Assert(object->objectSubId == 0); return OCLASS_TYPE; + + case RelOid_pg_database: + Assert(object->objectSubId == 0); + return OCLASS_DATABASE; + + case RelOid_pg_tablespace: + Assert(object->objectSubId == 0); + return OCLASS_TBLSPACE; + + case RelOid_pg_shadow: + Assert(object->objectSubId == 0); + return OCLASS_SHADOW; } /* *************** *** 1352,1357 **** --- 1379,1389 ---- if (!object_classes_initialized) init_object_classes(); + if (object->classId == object_classes[OCLASS_AM]) + { + Assert(object->objectSubId == 0); + return OCLASS_AM; + } if (object->classId == object_classes[OCLASS_CAST]) { Assert(object->objectSubId == 0); *************** *** 1439,1444 **** --- 1471,1510 ---- format_type_be(object->objectId)); break; + case OCLASS_AM: + { + Relation amDesc; + ScanKeyData skey[1]; + SysScanDesc rcscan; + HeapTuple tup; + Form_pg_am amForm; + + amDesc = heap_openr(AccessMethodRelationName, AccessShareLock); + + ScanKeyInit(&skey[0], + ObjectIdAttributeNumber, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(object->objectId)); + + rcscan = systable_beginscan(amDesc, AmOidIndex, true, + SnapshotNow, 1, skey); + + tup = systable_getnext(rcscan); + + if (!HeapTupleIsValid(tup)) + elog(ERROR, "could not find tuple for access method %u", + object->objectId); + + amForm = (Form_pg_am) GETSTRUCT(tup); + + appendStringInfo(&buffer, gettext("access method %s"), + amForm->amname.data); + + systable_endscan(rcscan); + heap_close(amDesc, AccessShareLock); + break; + } + case OCLASS_CAST: { Relation castDesc; *************** *** 1715,1720 **** --- 1781,1817 ---- break; } + case OCLASS_TBLSPACE: + { + char *tblspace; + + tblspace = get_tablespace_name(object->objectId); + if (!tblspace) + elog(ERROR, "cache lookup failed for tablespace %u", + object->objectId); + appendStringInfo(&buffer, gettext("tablespace %s"), tblspace); + break; + } + + case OCLASS_DATABASE: + { + char *datname; + + datname = get_database_name(object->objectId); + if (!datname) + elog(ERROR, "cache lookup failed for database %u", + object->objectId); + appendStringInfo(&buffer, gettext("database %s"), datname); + break; + } + + case OCLASS_SHADOW: + { + appendStringInfo(&buffer, gettext("user %s"), + GetUserNameFromId((AclId)object->objectId)); + break; + } + default: appendStringInfo(&buffer, "unrecognized object %u %u %d", object->classId, Index: src/backend/catalog/heap.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/catalog/heap.c,v retrieving revision 1.277 diff -c -r1.277 heap.c *** src/backend/catalog/heap.c 1 Dec 2004 19:00:39 -0000 1.277 --- src/backend/catalog/heap.c 10 Dec 2004 14:33:13 -0000 *************** *** 332,338 **** /* ---------------------------------------------------------------- * heap_create_with_catalog - Create a cataloged relation * ! * this is done in 6 steps: * * 1) CheckAttributeNamesTypes() is used to make certain the tuple * descriptor contains a valid set of attribute names and types --- 332,338 ---- /* ---------------------------------------------------------------- * heap_create_with_catalog - Create a cataloged relation * ! * this is done in 8 steps: * * 1) CheckAttributeNamesTypes() is used to make certain the tuple * descriptor contains a valid set of attribute names and types *************** *** 789,794 **** --- 789,796 ---- * make a dependency link to force the relation to be deleted if its * namespace is. Skip this in bootstrap mode, since we don't make * dependencies while bootstrapping. + * + * Also make dependency links to its owner and tablespace, as needed. */ if (!IsBootstrapProcessingMode()) { *************** *** 798,807 **** --- 800,826 ---- myself.classId = RelOid_pg_class; myself.objectId = new_rel_oid; myself.objectSubId = 0; + + /* the relation depends on its namespace */ referenced.classId = get_system_catalog_relid(NamespaceRelationName); referenced.objectId = relnamespace; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + + /* the relation depends on its tablespace, if specified */ + if (reltablespace != 0) + { + referenced.classId = get_system_catalog_relid(TableSpaceRelationName); + referenced.objectId = reltablespace; + referenced.objectSubId = 0; + recordSharedDependencyOn(&myself, &referenced); + } + + /* the relation depends on its owner */ + referenced.classId = get_system_catalog_relid(ShadowRelationName); + referenced.objectId = GetUserId(); + referenced.objectSubId = 0; + recordSharedDependencyOn(&myself, &referenced); } /* Index: src/backend/catalog/pg_conversion.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/catalog/pg_conversion.c,v retrieving revision 1.19 diff -c -r1.19 pg_conversion.c *** src/backend/catalog/pg_conversion.c 29 Aug 2004 04:12:28 -0000 1.19 --- src/backend/catalog/pg_conversion.c 30 Oct 2004 15:18:49 -0000 *************** *** 121,126 **** --- 121,132 ---- referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + /* record shared dependency on owner */ + referenced.classId = get_system_catalog_relid(ShadowRelationName); + referenced.objectId = conowner; + referenced.objectSubId = 0; + recordSharedDependencyOn(&myself, &referenced); + heap_freetuple(tup); heap_close(rel, RowExclusiveLock); Index: src/backend/catalog/pg_operator.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/catalog/pg_operator.c,v retrieving revision 1.86 diff -c -r1.86 pg_operator.c *** src/backend/catalog/pg_operator.c 29 Aug 2004 04:12:29 -0000 1.86 --- src/backend/catalog/pg_operator.c 30 Oct 2004 15:19:29 -0000 *************** *** 891,896 **** --- 891,897 ---- /* In case we are updating a shell, delete any existing entries */ deleteDependencyRecordsFor(myself.classId, myself.objectId); + deleteSharedDependencyRecordsFor(myself.classId, myself.objectId); /* Dependency on namespace */ if (OidIsValid(oper->oprnamespace)) *************** *** 964,967 **** --- 965,974 ---- referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } + + /* Dependency on owner */ + referenced.classId = get_system_catalog_relid(ShadowRelationName); + referenced.objectId = oper->oprowner; + referenced.objectSubId = 0; + recordSharedDependencyOn(&myself, &referenced); } Index: src/backend/catalog/pg_proc.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/catalog/pg_proc.c,v retrieving revision 1.121 diff -c -r1.121 pg_proc.c *** src/backend/catalog/pg_proc.c 18 Oct 2004 01:45:38 -0000 1.121 --- src/backend/catalog/pg_proc.c 30 Oct 2004 04:01:36 -0000 *************** *** 295,300 **** --- 295,303 ---- recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } + /* dependency on owner */ + recordDependencyOnCurrentUser(&myself); + heap_freetuple(tup); heap_close(rel, RowExclusiveLock); Index: src/backend/catalog/pg_type.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/catalog/pg_type.c,v retrieving revision 1.96 diff -c -r1.96 pg_type.c *** src/backend/catalog/pg_type.c 29 Aug 2004 05:06:41 -0000 1.96 --- src/backend/catalog/pg_type.c 8 Dec 2004 05:44:26 -0000 *************** *** 488,493 **** --- 488,496 ---- /* Normal dependency on the default expression. */ if (defaultExpr) recordDependencyOnExpr(&myself, defaultExpr, NIL, DEPENDENCY_NORMAL); + + /* Shared dependency on owner. */ + recordDependencyOnCurrentUser(&myself); } /* Index: src/backend/commands/conversioncmds.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/conversioncmds.c,v retrieving revision 1.15 diff -c -r1.15 conversioncmds.c *** src/backend/commands/conversioncmds.c 29 Aug 2004 05:06:41 -0000 1.15 --- src/backend/commands/conversioncmds.c 30 Oct 2004 15:21:47 -0000 *************** *** 18,23 **** --- 18,24 ---- #include "access/heapam.h" #include "catalog/catalog.h" #include "catalog/catname.h" + #include "catalog/dependency.h" #include "catalog/indexing.h" #include "catalog/namespace.h" #include "catalog/pg_type.h" *************** *** 221,226 **** --- 222,231 ---- simple_heap_update(rel, &tup->t_self, tup); CatalogUpdateIndexes(rel, tup); + /* Update shared dependency reference */ + shdependChangeOwner(get_system_catalog_relid(ConversionRelationName), + conversionOid, + newOwnerSysId); } heap_close(rel, NoLock); Index: src/backend/commands/dbcommands.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/dbcommands.c,v retrieving revision 1.147 diff -c -r1.147 dbcommands.c *** src/backend/commands/dbcommands.c 18 Nov 2004 01:14:26 -0000 1.147 --- src/backend/commands/dbcommands.c 10 Dec 2004 03:58:29 -0000 *************** *** 23,28 **** --- 23,29 ---- #include "access/heapam.h" #include "catalog/catname.h" #include "catalog/catalog.h" + #include "catalog/dependency.h" #include "catalog/pg_database.h" #include "catalog/pg_shadow.h" #include "catalog/pg_tablespace.h" *************** *** 343,348 **** --- 344,350 ---- /* * Iterate through all tablespaces of the template database, and copy * each one to the new database. + * XXX maybe it could be done better using pg_shdepend info. */ rel = heap_openr(TableSpaceRelationName, AccessShareLock); scan = heap_beginscan(rel, SnapshotNow, 0, NULL); *************** *** 497,502 **** --- 499,526 ---- /* Update indexes */ CatalogUpdateIndexes(pg_database_rel, tuple); + /* Create pg_shdepend entries */ + copyTemplateDependencies(src_dboid, dboid); + /* Register tablespace and owner dependencies */ + { + ObjectAddress myself, + referenced; + + myself.classId = RelOid_pg_database; + myself.objectId = dboid; + myself.objectSubId = 0; + + referenced.classId = RelOid_pg_shadow; + referenced.objectId = datdba; + referenced.objectSubId = 0; + recordSharedDependencyOn(&myself, &referenced); + + referenced.classId = RelOid_pg_tablespace; + referenced.objectId = dst_deftablespace; + referenced.objectSubId = 0; + recordSharedDependencyOn(&myself, &referenced); + } + /* * Force dirty buffers out to disk, so that newly-connecting backends * will see the new database in pg_database right away. (They'll see *************** *** 633,638 **** --- 657,667 ---- remove_dbtablespaces(db_id); /* + * Remove shared dependency references in the database. + */ + dropDatabaseDependencies(db_id); + + /* * Force dirty buffers out to disk, so that newly-connecting backends * will see the database tuple marked dead in pg_database right away. * (They'll see an uncommitted deletion, but they don't care; see *************** *** 917,922 **** --- 946,958 ---- heap_freetuple(newtuple); + /* + * Update shared dependency references + */ + shdependChangeOwner(RelOid_pg_database, + HeapTupleGetOid(tuple), + newOwnerSysId); + /* must release buffer pins before FlushRelationBuffers */ systable_endscan(scan); Index: src/backend/commands/functioncmds.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/functioncmds.c,v retrieving revision 1.52 diff -c -r1.52 functioncmds.c *** src/backend/commands/functioncmds.c 29 Aug 2004 05:06:41 -0000 1.52 --- src/backend/commands/functioncmds.c 30 Oct 2004 14:32:21 -0000 *************** *** 798,803 **** --- 798,808 ---- simple_heap_update(rel, &newtuple->t_self, newtuple); CatalogUpdateIndexes(rel, newtuple); + /* update shared dependency reference */ + shdependChangeOwner(get_system_catalog_relid(ProcedureRelationName), + procOid, + newOwnerSysId); + heap_freetuple(newtuple); } Index: src/backend/commands/opclasscmds.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/opclasscmds.c,v retrieving revision 1.28 diff -c -r1.28 opclasscmds.c *** src/backend/commands/opclasscmds.c 29 Aug 2004 05:06:41 -0000 1.28 --- src/backend/commands/opclasscmds.c 30 Oct 2004 15:22:27 -0000 *************** *** 392,397 **** --- 392,400 ---- recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } + /* dependency on owner */ + recordDependencyOnCurrentUser(&myself); + heap_close(rel, RowExclusiveLock); } *************** *** 964,969 **** --- 967,977 ---- CatalogUpdateIndexes(rel, tup); } + /* Update shared dependency reference */ + shdependChangeOwner(get_system_catalog_relid(OperatorClassRelationName), + amOid, + newOwnerSysId); + heap_close(rel, NoLock); heap_freetuple(tup); } Index: src/backend/commands/operatorcmds.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/operatorcmds.c,v retrieving revision 1.19 diff -c -r1.19 operatorcmds.c *** src/backend/commands/operatorcmds.c 29 Aug 2004 05:06:41 -0000 1.19 --- src/backend/commands/operatorcmds.c 30 Oct 2004 14:33:34 -0000 *************** *** 311,316 **** --- 311,323 ---- simple_heap_update(rel, &tup->t_self, tup); CatalogUpdateIndexes(rel, tup); + + /* + * Update shared dependency references. + */ + shdependChangeOwner(get_system_catalog_relid(OperatorRelationName), + operOid, + newOwnerSysId); } heap_close(rel, NoLock); Index: src/backend/commands/schemacmds.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/schemacmds.c,v retrieving revision 1.26 diff -c -r1.26 schemacmds.c *** src/backend/commands/schemacmds.c 5 Nov 2004 19:15:57 -0000 1.26 --- src/backend/commands/schemacmds.c 6 Nov 2004 18:08:35 -0000 *************** *** 47,52 **** --- 47,53 ---- AclId owner_userid; AclId saved_userid; AclResult aclresult; + ObjectAddress myself; saved_userid = GetUserId(); *************** *** 147,152 **** --- 148,160 ---- } } + /* Record dependency on owner*/ + myself.classId = get_system_catalog_relid(NamespaceRelationName); + myself.objectId = namespaceId; + myself.objectSubId = 0; + + recordDependencyOnCurrentUser(&myself); + /* Reset search path to normal state */ PopSpecialNamespace(namespaceId); *************** *** 345,350 **** --- 353,365 ---- CatalogUpdateIndexes(rel, newtuple); heap_freetuple(newtuple); + + /* + * Update shared dependency references. + */ + shdependChangeOwner(get_system_catalog_relid(NamespaceRelationName), + HeapTupleGetOid(tup), + newOwnerSysId); } ReleaseSysCache(tup); Index: src/backend/commands/tablecmds.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/tablecmds.c,v retrieving revision 1.140 diff -c -r1.140 tablecmds.c *** src/backend/commands/tablecmds.c 16 Nov 2004 23:34:22 -0000 1.140 --- src/backend/commands/tablecmds.c 2 Dec 2004 22:17:19 -0000 *************** *** 5256,5261 **** --- 5256,5266 ---- heap_freetuple(newtuple); /* + * Update shared dependency reference. + */ + shdependChangeOwner(RelOid_pg_class, relationOid, newOwnerSysId); + + /* * If we are operating on a table, also change the ownership of * any indexes and sequences that belong to the table, as well as * the table's toast table (if it has one) *************** *** 5439,5444 **** --- 5444,5452 ---- if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_TABLESPACE, tablespacename); + /* Update shared dependency reference */ + shdependChangeTablespace(RelOid_pg_class, tab->relid, tablespaceId); + /* Save info for Phase 3 to do the real work */ if (OidIsValid(tab->newTableSpace)) ereport(ERROR, Index: src/backend/commands/tablespace.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/tablespace.c,v retrieving revision 1.14 diff -c -r1.14 tablespace.c *** src/backend/commands/tablespace.c 5 Nov 2004 19:15:57 -0000 1.14 --- src/backend/commands/tablespace.c 9 Dec 2004 05:23:38 -0000 *************** *** 51,56 **** --- 51,57 ---- #include "access/heapam.h" #include "catalog/catalog.h" #include "catalog/catname.h" + #include "catalog/dependency.h" #include "catalog/indexing.h" #include "catalog/pg_namespace.h" #include "catalog/pg_tablespace.h" *************** *** 340,345 **** --- 341,360 ---- set_short_version(location); /* + * Record dependency link from tablespace to owner. + * XXX maybe this should be done after the symlink is created? + */ + { + ObjectAddress myself; + + myself.classId = RelOid_pg_tablespace; + myself.objectId = tablespaceoid; + myself.objectSubId = 0; + + recordDependencyOnCurrentUser(&myself); + } + + /* * All seems well, create the symlink */ linkloc = (char *) palloc(strlen(DataDir) + 11 + 10 + 1); *************** *** 439,444 **** --- 454,472 ---- aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_TABLESPACE, tablespacename); + /* Verify pg_shdepend entries mentioning this tablespace. */ + if (checkSharedDependencies(RelOid_pg_tablespace, tablespaceoid) > 0) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_IN_USE), + errmsg("cannot drop tablespace \"%s\"", tablespacename), + errdetail("Some objects depend on this tablespace."))); + + /* Remove shared dependency references to this tablespace */ + deleteGlobalDependencyRecordsFor(RelOid_pg_tablespace, tablespaceoid); + + /* Remove shared references from this tablespace to its owner */ + deleteSharedDependencyRecordsFor(RelOid_pg_tablespace, tablespaceoid); + /* * Remove the pg_tablespace tuple (this will roll back if we fail * below) *************** *** 865,870 **** --- 893,903 ---- simple_heap_update(rel, &newtuple->t_self, newtuple); CatalogUpdateIndexes(rel, newtuple); + /* Update shared dependency reference. */ + shdependChangeOwner(RelOid_pg_tablespace, + HeapTupleGetOid(tup), + newOwnerSysId); + heap_freetuple(newtuple); } Index: src/backend/commands/typecmds.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/typecmds.c,v retrieving revision 1.64 diff -c -r1.64 typecmds.c *** src/backend/commands/typecmds.c 7 Oct 2004 15:21:52 -0000 1.64 --- src/backend/commands/typecmds.c 30 Oct 2004 03:24:21 -0000 *************** *** 2111,2116 **** --- 2111,2120 ---- simple_heap_update(rel, &tup->t_self, tup); CatalogUpdateIndexes(rel, tup); + + shdependChangeOwner(get_system_catalog_relid(TypeRelationName), + typeOid, + newOwnerSysId); } /* Clean up */ Index: src/backend/commands/user.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/user.c,v retrieving revision 1.146 diff -c -r1.146 user.c *** src/backend/commands/user.c 27 Sep 2004 04:01:23 -0000 1.146 --- src/backend/commands/user.c 8 Dec 2004 04:23:56 -0000 *************** *** 19,24 **** --- 19,25 ---- #include "access/heapam.h" #include "catalog/catname.h" + #include "catalog/dependency.h" #include "catalog/indexing.h" #include "catalog/pg_database.h" #include "catalog/pg_group.h" *************** *** 1091,1097 **** tmp_tuple; Relation pg_rel; TupleDesc pg_dsc; - ScanKeyData scankey; HeapScanDesc scan; AclId usesysid; --- 1092,1097 ---- *************** *** 1114,1156 **** (errcode(ERRCODE_OBJECT_IN_USE), errmsg("session user cannot be dropped"))); ! /* ! * Check if user still owns a database. If so, error out. ! * ! * (It used to be that this function would drop the database ! * automatically. This is not only very dangerous for people that ! * don't read the manual, it doesn't seem to be the behaviour one ! * would expect either.) -- petere 2000/01/14) ! */ ! pg_rel = heap_openr(DatabaseRelationName, AccessShareLock); ! pg_dsc = RelationGetDescr(pg_rel); ! ! ScanKeyInit(&scankey, ! Anum_pg_database_datdba, ! BTEqualStrategyNumber, F_INT4EQ, ! Int32GetDatum(usesysid)); ! ! scan = heap_beginscan(pg_rel, SnapshotNow, 1, &scankey); ! ! if ((tmp_tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) ! { ! char *dbname; ! ! dbname = NameStr(((Form_pg_database) GETSTRUCT(tmp_tuple))->datname); ereport(ERROR, (errcode(ERRCODE_OBJECT_IN_USE), errmsg("user \"%s\" cannot be dropped", user), ! errdetail("The user owns database \"%s\".", dbname))); ! } ! heap_endscan(scan); ! heap_close(pg_rel, AccessShareLock); ! ! /* ! * Somehow we'd have to check for tables, views, etc. owned by the ! * user as well, but those could be spread out over all sorts of ! * databases which we don't have access to (easily). ! */ /* * Remove the user from the pg_shadow table --- 1114,1128 ---- (errcode(ERRCODE_OBJECT_IN_USE), errmsg("session user cannot be dropped"))); ! /* Verify pg_shdepend entries mentioning this user. */ ! if (checkSharedDependencies(RelOid_pg_shadow, usesysid) > 0) ereport(ERROR, (errcode(ERRCODE_OBJECT_IN_USE), errmsg("user \"%s\" cannot be dropped", user), ! errdetail("Some objects depend on this user."))); ! /* Remove shared dependency references to this user */ ! deleteGlobalDependencyRecordsFor(RelOid_pg_shadow, usesysid); /* * Remove the user from the pg_shadow table Index: src/bin/initdb/initdb.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/bin/initdb/initdb.c,v retrieving revision 1.70 diff -c -r1.70 initdb.c *** src/bin/initdb/initdb.c 29 Nov 2004 03:05:03 -0000 1.70 --- src/bin/initdb/initdb.c 9 Dec 2004 07:53:10 -0000 *************** *** 173,178 **** --- 173,179 ---- static void get_set_pwd(void); static void unlimit_systables(void); static void setup_depend(void); + static void setup_shared_depend(void); static void setup_sysviews(void); static void setup_description(void); static void setup_conversion(void); *************** *** 1559,1564 **** --- 1560,1659 ---- check_ok(); } + static void + setup_shared_depend(void) + { + PG_CMD_DECL; + char **line; + static char *pg_shdepend_setup[] = { + /* + * Fill pg_shdepend with info about all existant objects. + * We need to take the ownership and tablespace into consideration. + * + * Note that we first clear the table to rid of dependencies recorded + * by normal operation, because it's incomplete. + */ + "DELETE FROM pg_shdepend\n", + + /* Entries for owners of database-local objects */ + "INSERT INTO pg_shdepend SELECT" + " (SELECT oid FROM pg_database WHERE datname=current_database())," + " 'pg_type'::regclass, oid, 'pg_shadow'::regclass, typowner" + " FROM pg_type t\n", + "INSERT INTO pg_shdepend SELECT" + " (SELECT oid FROM pg_database WHERE datname=current_database())," + " 'pg_proc'::regclass, oid, 'pg_shadow'::regclass, proowner" + " FROM pg_proc t\n", + "INSERT INTO pg_shdepend SELECT" + " (SELECT oid FROM pg_database WHERE datname=current_database())," + " 'pg_class'::regclass, oid, 'pg_shadow'::regclass, relowner" + " FROM pg_class t\n", + "INSERT INTO pg_shdepend SELECT" + " (SELECT oid FROM pg_database WHERE datname=current_database())," + " 'pg_operator'::regclass, oid, 'pg_shadow'::regclass, oprowner" + " FROM pg_operator t\n", + "INSERT INTO pg_shdepend SELECT" + " (SELECT oid FROM pg_database WHERE datname=current_database())," + " 'pg_opclass'::regclass, oid, 'pg_shadow'::regclass, opcowner" + " FROM pg_opclass t\n", + "INSERT INTO pg_shdepend SELECT" + " (SELECT oid FROM pg_database WHERE datname=current_database())," + " 'pg_am'::regclass, oid, 'pg_shadow'::regclass, amowner" + " FROM pg_am t\n", + "INSERT INTO pg_shdepend SELECT" + " (SELECT oid FROM pg_database WHERE datname=current_database())," + " 'pg_namespace'::regclass, oid, 'pg_shadow'::regclass, nspowner" + " FROM pg_namespace t\n", + "INSERT INTO pg_shdepend SELECT" + " (SELECT oid FROM pg_database WHERE datname=current_database())," + " 'pg_conversion'::regclass, oid, 'pg_shadow'::regclass, conowner" + " FROM pg_conversion t\n", + + /* Entries for tablespaces of database-local objects */ + "INSERT INTO pg_shdepend SELECT" + " (SELECT oid FROM pg_database WHERE datname=current_database())," + " 'pg_class'::regclass, oid, 'pg_tablespace'::regclass, reltablespace" + " FROM pg_class t" + " WHERE reltablespace <> 0\n", + + /* Entries for owners of shared objects */ + "INSERT INTO pg_shdepend SELECT" + " 0," + " 'pg_tablespace'::regclass, oid, 'pg_shadow'::regclass, spcowner" + " FROM pg_tablespace t\n", + "INSERT INTO pg_shdepend SELECT" + " 0," + " 'pg_database'::regclass, oid, 'pg_shadow'::regclass, datdba" + " FROM pg_database t\n", + /* Entries for tablespaces of shared objects */ + "INSERT INTO pg_shdepend SELECT" + " 0," + " 'pg_database'::regclass, oid, 'pg_tablespace'::regclass, dattablespace" + " FROM pg_database t" + " WHERE dattablespace <> 0\n", + NULL + }; + + + fputs(_("initializing pg_shdepend ... "), stdout); + fflush(stdout); + + snprintf(cmd, sizeof(cmd), + "\"%s\" %s template1 >%s", + backend_exec, backend_options, + DEVNULL); + + PG_CMD_OPEN; + + for (line = pg_shdepend_setup; *line != NULL; line++) + PG_CMD_PUTS(*line); + + PG_CMD_CLOSE; + + check_ok(); + } + + /* * set up system views */ *************** *** 2595,2600 **** --- 2690,2697 ---- setup_depend(); + setup_shared_depend(); + setup_sysviews(); setup_description(); Index: src/bin/psql/po/es.po =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/bin/psql/po/es.po,v retrieving revision 1.5 diff -c -r1.5 es.po *** src/bin/psql/po/es.po 11 Dec 2004 19:03:47 -0000 1.5 --- src/bin/psql/po/es.po 13 Dec 2004 20:17:40 -0000 *************** *** 809,815 **** #, c-format msgid " \\dp [PATTERN] list table, view, and sequence access privileges\n" msgstr "" ! "\\dp [PATRÓN] listar privilegios de acceso a tablas, vistas y secuencias\n" #: help.c:228 #, c-format --- 809,815 ---- #, c-format msgid " \\dp [PATTERN] list table, view, and sequence access privileges\n" msgstr "" ! " \\dp [PATRÓN] listar privilegios de acceso a tablas, vistas y secuencias\n" #: help.c:228 #, c-format *************** *** 835,841 **** msgstr "" " \\z [PATRÓN] listar privilegios de acceso a tablas, vistas y " "secuencias\n" ! " (lo mismo que \\dp)\n" #: help.c:234 #, c-format --- 835,841 ---- msgstr "" " \\z [PATRÓN] listar privilegios de acceso a tablas, vistas y " "secuencias\n" ! " (lo mismo que \\dp)\n" #: help.c:234 #, c-format Index: src/include/catalog/catname.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/catalog/catname.h,v retrieving revision 1.33 diff -c -r1.33 catname.h *** src/include/catalog/catname.h 29 Aug 2004 04:13:04 -0000 1.33 --- src/include/catalog/catname.h 29 Oct 2004 19:48:50 -0000 *************** *** 25,30 **** --- 25,31 ---- #define ConversionRelationName "pg_conversion" #define DatabaseRelationName "pg_database" #define DependRelationName "pg_depend" + #define SharedDependRelationName "pg_shdepend" #define DescriptionRelationName "pg_description" #define GroupRelationName "pg_group" #define IndexRelationName "pg_index" Index: src/include/catalog/dependency.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/catalog/dependency.h,v retrieving revision 1.13 diff -c -r1.13 dependency.h *** src/include/catalog/dependency.h 29 Aug 2004 04:13:04 -0000 1.13 --- src/include/catalog/dependency.h 10 Dec 2004 03:58:25 -0000 *************** *** 81,90 **** /* ! * This enum covers all system catalogs whose OIDs can appear in classId. */ typedef enum ObjectClass { OCLASS_CLASS, /* pg_class */ OCLASS_PROC, /* pg_proc */ OCLASS_TYPE, /* pg_type */ --- 81,92 ---- /* ! * This enum covers all system catalogs whose OIDs can appear in ! * pg_depend.classId or pg_shdepend.classId. */ typedef enum ObjectClass { + OCLASS_AM, /* pg_am */ OCLASS_CLASS, /* pg_class */ OCLASS_PROC, /* pg_proc */ OCLASS_TYPE, /* pg_type */ *************** *** 98,103 **** --- 100,109 ---- OCLASS_REWRITE, /* pg_rewrite */ OCLASS_TRIGGER, /* pg_trigger */ OCLASS_SCHEMA, /* pg_namespace */ + /* shared system catalogs: */ + OCLASS_TBLSPACE, /* pg_tablespace */ + OCLASS_DATABASE, /* pg_database */ + OCLASS_SHADOW, /* pg_shadow */ MAX_OCLASS /* MUST BE LAST */ } ObjectClass; *************** *** 136,139 **** --- 142,168 ---- extern long deleteDependencyRecordsFor(Oid classId, Oid objectId); + /* in pg_shdepend.c */ + + extern void recordSharedDependencyOn(const ObjectAddress *depender, + const ObjectAddress *referenced); + + extern void recordDependencyOnCurrentUser(const ObjectAddress *depender); + + extern void shdependChangeOwner(Oid classId, Oid objectId, + int newOwnerSysId); + + extern void shdependChangeTablespace(Oid classId, Oid objectId, + Oid newTblspc); + + extern int checkSharedDependencies(Oid classId, Oid objectId); + + extern void copyTemplateDependencies(Oid templateDbId, Oid newDbId); + + extern void dropDatabaseDependencies(Oid databaseId); + + extern void deleteSharedDependencyRecordsFor(Oid classId, Oid objectId); + + extern void deleteGlobalDependencyRecordsFor(Oid classId, Oid objectId); + #endif /* DEPENDENCY_H */ Index: src/include/catalog/indexing.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/catalog/indexing.h,v retrieving revision 1.83 diff -c -r1.83 indexing.h *** src/include/catalog/indexing.h 29 Aug 2004 04:13:05 -0000 1.83 --- src/include/catalog/indexing.h 29 Oct 2004 19:49:05 -0000 *************** *** 67,72 **** --- 67,74 ---- #define ProcedureOidIndex "pg_proc_oid_index" #define RewriteOidIndex "pg_rewrite_oid_index" #define RewriteRelRulenameIndex "pg_rewrite_rel_rulename_index" + #define SharedDependDependerIndex "pg_shdepend_depender_index" + #define SharedDependReferenceIndex "pg_shdepend_reference_index" #define ShadowNameIndex "pg_shadow_usename_index" #define ShadowSysidIndex "pg_shadow_usesysid_index" #define StatisticRelidAttnumIndex "pg_statistic_relid_att_index" *************** *** 165,170 **** --- 167,176 ---- /* This following index is not used for a cache and is not unique */ DECLARE_UNIQUE_INDEX(pg_rewrite_oid_index on pg_rewrite using btree(oid oid_ops)); DECLARE_UNIQUE_INDEX(pg_rewrite_rel_rulename_index on pg_rewrite using btree(ev_class oid_ops, rulename name_ops)); + /* This following index is not used for a cache and is not unique */ + DECLARE_INDEX(pg_shdepend_depender_index on pg_shdepend using btree(dbid oid_ops, classid oid_ops, objid oid_ops)); + /* This following index is not used for a cache and is not unique */ + DECLARE_INDEX(pg_shdepend_reference_index on pg_shdepend using btree(refclassid oid_ops, refobjid oid_ops)); DECLARE_UNIQUE_INDEX(pg_shadow_usename_index on pg_shadow using btree(usename name_ops)); DECLARE_UNIQUE_INDEX(pg_shadow_usesysid_index on pg_shadow using btree(usesysid int4_ops)); DECLARE_UNIQUE_INDEX(pg_statistic_relid_att_index on pg_statistic using btree(starelid oid_ops, staattnum int2_ops)); Index: src/include/catalog/pg_tablespace.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/catalog/pg_tablespace.h,v retrieving revision 1.4 diff -c -r1.4 pg_tablespace.h *** src/include/catalog/pg_tablespace.h 29 Aug 2004 05:06:55 -0000 1.4 --- src/include/catalog/pg_tablespace.h 10 Dec 2004 20:46:27 -0000 *************** *** 8,14 **** * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * ! * $PostgreSQL: pgsql/src/include/catalog/pg_tablespace.h,v 1.4 2004/08/29 05:06:55 momjian Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki --- 8,14 ---- * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * ! * $PostgreSQL: pgsql-server/src/include/catalog/pg_tablespace.h,v 1.3 2004/08/29 04:13:05 momjian Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki Index: src/test/regress/expected/sanity_check.out =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/test/regress/expected/sanity_check.out,v retrieving revision 1.24 diff -c -r1.24 sanity_check.out *** src/test/regress/expected/sanity_check.out 18 Jun 2004 06:14:27 -0000 1.24 --- src/test/regress/expected/sanity_check.out 10 Dec 2004 18:48:36 -0000 *************** *** 55,60 **** --- 55,61 ---- pg_proc | t pg_rewrite | t pg_shadow | t + pg_shdepend | t pg_statistic | t pg_tablespace | t pg_trigger | t *************** *** 63,69 **** shighway | t tenk1 | t tenk2 | t ! (53 rows) -- -- another sanity check: every system catalog that has OIDs should have --- 64,70 ---- shighway | t tenk1 | t tenk2 | t ! (54 rows) -- -- another sanity check: every system catalog that has OIDs should have
/*------------------------------------------------------------------------- * * pg_shdepend.c * routines to support manipulation of the pg_shdepend relation * * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $PostgreSQL$ * *------------------------------------------------------------------------- */ #include "postgres.h" #include "miscadmin.h" #include "access/genam.h" #include "access/heapam.h" #include "access/htup.h" #include "access/skey.h" #include "catalog/catname.h" #include "catalog/dependency.h" #include "catalog/indexing.h" #include "catalog/pg_shdepend.h" #include "storage/proc.h" #include "utils/lsyscache.h" #include "utils/rel.h" #include "utils/fmgroids.h" static Oid classIdGetDbId(Oid classId); /* * Record a dependency between 2 objects via their respective ObjectAddress. * The first argument is the dependent object, the second the one it * references. * * This simply creates an entry in pg_shdepend, without any other processing. */ void recordSharedDependencyOn(const ObjectAddress *depender, const ObjectAddress *referenced) { Relation shdepRel; HeapTuple tup; char nulls[Natts_pg_shdepend]; Datum values[Natts_pg_shdepend]; int i; /* * Objects in pg_shdepend can't have SubIds. * * XXX Is this restriction actually useful, besides saving space * in pg_shdepend? */ Assert(depender->objectSubId == 0); Assert(referenced->objectSubId == 0); /* * During bootstrap, do nothing since pg_shdepend may not exist yet. * initdb will fill in appropriate pg_shdepend entries after bootstrap. */ if (IsBootstrapProcessingMode()) return; shdepRel = heap_openr(SharedDependRelationName, RowExclusiveLock); MemSet(nulls, ' ', sizeof(nulls)); /* * Form the new tuple and record the dependency. */ i = 0; values[i++] = ObjectIdGetDatum(classIdGetDbId(depender->classId)); /* dbid */ values[i++] = ObjectIdGetDatum(depender->classId); /* classid */ values[i++] = ObjectIdGetDatum(depender->objectId); /* objid */ values[i++] = ObjectIdGetDatum(referenced->classId); /* refclassid */ values[i++] = ObjectIdGetDatum(referenced->objectId); /* refobjid */ tup = heap_formtuple(shdepRel->rd_att, values, nulls); simple_heap_insert(shdepRel, tup); /* keep indexes current */ CatalogUpdateIndexes(shdepRel, tup); /* clean up */ heap_freetuple(tup); heap_close(shdepRel, RowExclusiveLock); } /* * recordDependencyOnCurrentUser * * A convenient form of recordSharedDependencyOn(). */ void recordDependencyOnCurrentUser(const ObjectAddress *object) { ObjectAddress referenced; referenced.classId = get_system_catalog_relid(ShadowRelationName); referenced.objectId = GetUserId(); referenced.objectSubId = 0; recordSharedDependencyOn(object, &referenced); } /* * shdependChangeOwner * * Update the shared dependencies to account for the new owner. */ void shdependChangeOwner(Oid classId, Oid objectId, int newOwnerSysId) { Relation sdepRel; ScanKeyData key[3]; SysScanDesc scan; HeapTuple tup; sdepRel = heap_openr(SharedDependRelationName, RowExclusiveLock); 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, SharedDependDependerIndex, true, SnapshotNow, 3, key); while (HeapTupleIsValid(tup = systable_getnext(scan))) { Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tup); HeapTuple newtup; char repl_nulls[Natts_pg_shdepend]; char replace[Natts_pg_shdepend]; Datum *values; /* Look for tuples referring to pg_shadow */ if (sdepForm->refclassid != RelOid_pg_shadow) continue; /* * Skip a tuple we just inserted. * * XXX Another possibility would be to break out of the loop * as soon as we modify one tuple. Not sure what is best. */ if (DatumGetInt32(sdepForm->refobjid) == newOwnerSysId) continue; values = (Datum *) palloc0(sizeof(Datum) * Natts_pg_shdepend); MemSet(repl_nulls, ' ', Natts_pg_shdepend); MemSet(replace, ' ', Natts_pg_shdepend); replace[Anum_pg_shdepend_refobjid - 1] = 'r'; values[Anum_pg_shdepend_refobjid - 1] = Int32GetDatum(newOwnerSysId); newtup = heap_modifytuple(tup, sdepRel, values, repl_nulls, replace); simple_heap_update(sdepRel, &tup->t_self, newtup); /* Keep indexes current */ CatalogUpdateIndexes(sdepRel, newtup); pfree(values); heap_freetuple(newtup); } systable_endscan(scan); heap_close(sdepRel, RowExclusiveLock); } /* * shdependChangeTablespace * * Update the shared dependencies to account for the new tablespace. */ void shdependChangeTablespace(Oid classId, Oid objectId, Oid newTblspc) { Relation sdepRel; ScanKeyData key[3]; SysScanDesc scan; HeapTuple tup; int count = 0; sdepRel = heap_openr(SharedDependRelationName, RowExclusiveLock); 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, SharedDependDependerIndex, true, SnapshotNow, 3, key); while (HeapTupleIsValid(tup = systable_getnext(scan))) { Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tup); HeapTuple newtup; char repl_nulls[Natts_pg_shdepend]; char replace[Natts_pg_shdepend]; Datum *values; /* Look for tuples referring to pg_tablespace */ if (sdepForm->refclassid != RelOid_pg_tablespace) continue; /* * Skip a tuple we just inserted; otherwise we could loop here * forever. If we do skip it, count it anyway, so we don't insert * a new entry when the user changes a relation's tablespace to * the same as before. * * XXX Another possibility would be to break out of the loop * as soon as we modify one tuple. Not sure what is best. */ if (DatumGetObjectId(sdepForm->refobjid) == newTblspc) { count++; continue; } values = (Datum *) palloc0(sizeof(Datum) * Natts_pg_shdepend); MemSet(repl_nulls, ' ', Natts_pg_shdepend); MemSet(replace, ' ', Natts_pg_shdepend); replace[Anum_pg_shdepend_refobjid - 1] = 'r'; values[Anum_pg_shdepend_refobjid - 1] = ObjectIdGetDatum(newTblspc); newtup = heap_modifytuple(tup, sdepRel, values, repl_nulls, replace); simple_heap_update(sdepRel, &tup->t_self, newtup); /* Keep indexes current */ CatalogUpdateIndexes(sdepRel, newtup); count++; pfree(values); heap_freetuple(newtup); } systable_endscan(scan); /* * If we didn't update anything, then the object didn't have a * reference registered because it was using the default tablespace. * Register one now. */ if (count == 0) { ObjectAddress self, referenced; self.classId = classId; self.objectId = objectId; self.objectSubId = 0; referenced.classId = RelOid_pg_tablespace; referenced.objectId = newTblspc; referenced.objectSubId = 0; recordSharedDependencyOn(&self, &referenced); } heap_close(sdepRel, RowExclusiveLock); } /* * A struct to keep track of dependencies found in other databases. */ typedef struct { Oid dbOid; int count; } remoteDep; /* * checkSharedDependencies * * Check whether there are shared dependency entries for a given global * object. Returns the number found. * * XXX decide what to do with those found. Display them inconditionally? */ int checkSharedDependencies(Oid classId, Oid objectId) { Relation sdepRel; ScanKeyData key[2]; SysScanDesc scan; HeapTuple tup; int count = 0; List *remDeps = NIL; ListCell *cell; ObjectAddress object; sdepRel = heap_openr(SharedDependRelationName, RowExclusiveLock); 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, SharedDependReferenceIndex, true, SnapshotNow, 2, key); while (HeapTupleIsValid(tup = systable_getnext(scan))) { Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tup); object.classId = sdepForm->classid; object.objectId = sdepForm->objid; object.objectSubId = 0; /* * If it's a dependency local to this database, describe it. * If the dependent object is on a shared catalog, describe it. * If it's a remote dependency, keep track of it so we can * report later. * XXX this info is kept on a simple List. It might be better * to use a hash table, but I'm not sure it's worth the extra * complexity. */ if (sdepForm->dbid == MyProc->databaseId) elog(NOTICE, "in this database: %s", getObjectDescription(&object)); /* XXX optimize this. Keep a list of entries? */ else if (classIdGetDbId(sdepForm->classid) == InvalidOid) elog(NOTICE, getObjectDescription(&object)); else { remoteDep *dep; bool stored = false; 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); } } count++; } systable_endscan(scan); heap_close(sdepRel, RowExclusiveLock); foreach(cell, remDeps) { remoteDep *dep = lfirst(cell); object.classId = RelOid_pg_database; object.objectId = dep->dbOid; object.objectSubId = 0; elog(NOTICE, "in %s: %d objects", getObjectDescription(&object), dep->count); } list_free_deep(remDeps); return count; } /* * 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; ScanKeyData key[1]; SysScanDesc scan; HeapTuple tup; CatalogIndexState indstate; Datum *values; char repl_nulls[Natts_pg_shdepend]; char replace[Natts_pg_shdepend]; /* I think this lock is OK? */ sdepRel = heap_openr(SharedDependRelationName, RowShareLock); ScanKeyInit(&key[0], Anum_pg_shdepend_dbid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(templateDbId)); scan = systable_beginscan(sdepRel, SharedDependDependerIndex, true, SnapshotNow, 1, key); indstate = CatalogOpenIndexes(sdepRel); values = (Datum *) palloc0(sizeof(Datum) * Natts_pg_shdepend); MemSet(repl_nulls, ' ', Natts_pg_shdepend); MemSet(replace, ' ', Natts_pg_shdepend); replace[Anum_pg_shdepend_dbid - 1] = 'r'; values[Anum_pg_shdepend_dbid - 1] = ObjectIdGetDatum(newDbId); while (HeapTupleIsValid(tup = systable_getnext(scan))) { HeapTuple newtup; newtup = heap_modifytuple(tup, sdepRel, values, repl_nulls, replace); simple_heap_insert(sdepRel, newtup); /* Keep indexes current */ CatalogIndexInsert(indstate, newtup); heap_freetuple(newtup); } pfree(values); systable_endscan(scan); CatalogCloseIndexes(indstate); heap_close(sdepRel, RowShareLock); } /* * dropDatabaseDependencies * * Delete pg_shdepend entries corresponding to a database that's being * dropped. */ void dropDatabaseDependencies(Oid databaseId) { Relation sdepRel; ScanKeyData key[3]; SysScanDesc scan; HeapTuple tup; sdepRel = heap_openr(SharedDependRelationName, 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)); scan = systable_beginscan(sdepRel, SharedDependDependerIndex, true, SnapshotNow, 1, key); while (HeapTupleIsValid(tup = systable_getnext(scan))) { simple_heap_delete(sdepRel, &tup->t_self); } systable_endscan(scan); /* Now delete all entries corresponding to the database itself */ deleteSharedDependencyRecordsFor(RelOid_pg_database, databaseId); heap_close(sdepRel, RowExclusiveLock); } /* * deleteSharedDependencyRecordsFor * * Delete pg_shdepend entries corresponding to a database-local object that's * being dropped or modified. */ void deleteSharedDependencyRecordsFor(Oid classId, Oid objectId) { Relation sdepRel; ScanKeyData key[3]; SysScanDesc scan; HeapTuple tup; sdepRel = heap_openr(SharedDependRelationName, RowExclusiveLock); 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, SharedDependDependerIndex, true, SnapshotNow, 3, key); while (HeapTupleIsValid(tup = systable_getnext(scan))) simple_heap_delete(sdepRel, &tup->t_self); systable_endscan(scan); heap_close(sdepRel, RowExclusiveLock); } /* * deleteGlobalDependencyRecordsFor * * Delete pg_shdepend entries corresponding to a global object that's * being dropped or modified. * * XXX -- maybe this shouldn't exist at all, because when we drop a * global object we first check it isn't referenced. */ void deleteGlobalDependencyRecordsFor(Oid classId, Oid objectId) { Relation sdepRel; ScanKeyData key[2]; SysScanDesc scan; HeapTuple tup; sdepRel = heap_openr(SharedDependRelationName, RowExclusiveLock); 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, SharedDependReferenceIndex, true, SnapshotNow, 2, key); while (HeapTupleIsValid(tup = systable_getnext(scan))) { simple_heap_delete(sdepRel, &tup->t_self); } systable_endscan(scan); heap_close(sdepRel, RowExclusiveLock); } /* * classIdGetDbId * * Get the database Id that should be used in pg_shdepend. For shared * objects, it's 0 (InvalidOid); for all other objects, it's the * current database Id. */ static Oid classIdGetDbId(Oid classId) { Relation class; Oid dbId; class = heap_open(classId, AccessShareLock); if (class->rd_rel->relisshared) dbId = InvalidOid; else dbId = MyProc->databaseId; heap_close(class, AccessShareLock); return dbId; }
/*------------------------------------------------------------------------- * * pg_shdepend.h * definition of the system "shared dependency" relation (pg_shdepend) * along with the relation's initial contents. * * * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * $PostgreSQL$ * * NOTES * the genbki.sh script reads this file and generates .bki * information from the DATA() statements. * *------------------------------------------------------------------------- */ #ifndef PG_SHDEPEND_H #define PG_SHDEPEND_H /* ---------------- * postgres.h contains the system type definitions and the * CATALOG(), BOOTSTRAP and DATA() sugar words so this file * can be read by both genbki.sh and the C compiler. * ---------------- */ /* ---------------- * pg_shdepend definition. cpp turns this into * typedef struct FormData_pg_shdepend * ---------------- */ CATALOG(pg_shdepend) BKI_SHARED_RELATION BKI_WITHOUT_OIDS { /* * Identification of the dependent (referencing) object. * * These fields are all zeroes for a DEPENDENCY_PIN entry. */ Oid dbid; /* OID of database containing object */ Oid classid; /* OID of table containing object */ Oid objid; /* OID of object itself */ /* * Identification of the independent (referenced) object. */ Oid refclassid; /* OID of table containing object */ Oid refobjid; /* OID of object itself */ } 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 5 #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 /* * pg_shdepend has no preloaded contents; system-defined dependencies are * loaded into it during a late stage of the initdb process. */ #endif /* PG_SHDEPEND_H */
---------------------------(end of broadcast)--------------------------- TIP 5: Have you checked our extensive FAQ? http://www.postgresql.org/docs/faqs/FAQ.html