* pg_shdepend.c
* routines to support manipulation of the pg_shdepend relation
*
- * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2009, 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.14 2006/08/21 00:57:24 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/pg_shdepend.c,v 1.33 2009/06/04 18:33:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "catalog/pg_type.h"
#include "commands/conversioncmds.h"
#include "commands/defrem.h"
+#include "commands/proclang.h"
#include "commands/schemacmds.h"
#include "commands/tablecmds.h"
#include "commands/typecmds.h"
+#include "storage/lmgr.h"
#include "miscadmin.h"
#include "utils/acl.h"
#include "utils/fmgroids.h"
#include "utils/syscache.h"
+#include "utils/tqual.h"
typedef enum
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 shdepChangeDep(Relation sdepRel,
+ Oid classid, Oid objid, int32 objsubid,
+ Oid refclassid, Oid refobjid,
+ SharedDependencyType deptype);
+static void shdepAddDependency(Relation sdepRel,
+ Oid classId, Oid objectId, int32 objsubId,
+ Oid refclassId, Oid refobjId,
+ SharedDependencyType deptype);
+static void shdepDropDependency(Relation sdepRel,
+ Oid classId, Oid objectId, int32 objsubId,
+ bool drop_subobjects,
+ Oid refclassId, Oid refobjId,
+ SharedDependencyType deptype);
static void storeObjectDescription(StringInfo descs, objectType type,
ObjectAddress *object,
SharedDependencyType deptype,
sdepRel))
{
shdepAddDependency(sdepRel, depender->classId, depender->objectId,
+ depender->objectSubId,
referenced->classId, referenced->objectId,
deptype);
}
* locked.
*/
static void
-shdepChangeDep(Relation sdepRel, Oid classid, Oid objid,
+shdepChangeDep(Relation sdepRel,
+ Oid classid, Oid objid, int32 objsubid,
Oid refclassid, Oid refobjid,
SharedDependencyType deptype)
{
Oid dbid = classIdGetDbId(classid);
HeapTuple oldtup = NULL;
HeapTuple scantup;
- ScanKeyData key[3];
+ ScanKeyData key[4];
SysScanDesc scan;
/*
Anum_pg_shdepend_objid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(objid));
+ ScanKeyInit(&key[3],
+ Anum_pg_shdepend_objsubid,
+ BTEqualStrategyNumber, F_INT4EQ,
+ Int32GetDatum(objsubid));
scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
- SnapshotNow, 3, key);
+ SnapshotNow, 4, key);
while ((scantup = systable_getnext(scan)) != NULL)
{
/* Caller screwed up if multiple matches */
if (oldtup)
elog(ERROR,
- "multiple pg_shdepend entries for object %u/%u deptype %c",
- classid, objid, deptype);
+ "multiple pg_shdepend entries for object %u/%u/%d deptype %c",
+ classid, objid, objsubid, deptype);
oldtup = heap_copytuple(scantup);
}
Datum values[Natts_pg_shdepend];
bool nulls[Natts_pg_shdepend];
- memset(nulls, 0, sizeof(nulls));
+ memset(nulls, false, 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_objsubid - 1] = Int32GetDatum(objsubid);
values[Anum_pg_shdepend_refclassid - 1] = ObjectIdGetDatum(refclassid);
values[Anum_pg_shdepend_refobjid - 1] = ObjectIdGetDatum(refobjid);
* changeDependencyOnOwner
*
* Update the shared dependencies to account for the new owner.
+ *
+ * Note: we don't need an objsubid argument because only whole objects
+ * have owners.
*/
void
changeDependencyOnOwner(Oid classId, Oid objectId, Oid newOwnerId)
sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
/* Adjust the SHARED_DEPENDENCY_OWNER entry */
- shdepChangeDep(sdepRel, classId, objectId,
+ shdepChangeDep(sdepRel,
+ classId, objectId, 0,
AuthIdRelationId, newOwnerId,
SHARED_DEPENDENCY_OWNER);
* to make the various ALTER OWNER routines each know about it.
*----------
*/
- shdepDropDependency(sdepRel, classId, objectId,
+ shdepDropDependency(sdepRel, classId, objectId, 0, true,
AuthIdRelationId, newOwnerId,
SHARED_DEPENDENCY_ACL);
* updateAclDependencies
* Update the pg_shdepend info for an object's ACL during GRANT/REVOKE.
*
- * classId, objectId: identify the object whose ACL this is
+ * classId, objectId, objsubId: 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
* before return.
*/
void
-updateAclDependencies(Oid classId, Oid objectId, Oid ownerId, bool isGrant,
+updateAclDependencies(Oid classId, Oid objectId, int32 objsubId,
+ Oid ownerId, bool isGrant,
int noldmembers, Oid *oldmembers,
int nnewmembers, Oid *newmembers)
{
continue;
if (isGrant)
- shdepAddDependency(sdepRel, classId, objectId,
+ shdepAddDependency(sdepRel, classId, objectId, objsubId,
AuthIdRelationId, roleid,
SHARED_DEPENDENCY_ACL);
else
- shdepDropDependency(sdepRel, classId, objectId,
+ shdepDropDependency(sdepRel, classId, objectId, objsubId,
+ false, /* exact match on objsubId */
AuthIdRelationId, roleid,
SHARED_DEPENDENCY_ACL);
}
* checkSharedDependencies
*
* Check whether there are shared dependency entries for a given shared
- * object. Returns a string containing a newline-separated list of object
+ * object; return true if so.
+ *
+ * In addition, return a string containing a newline-separated list of object
* descriptions that depend on the shared object, or NULL if none is found.
+ * We actually return two such strings; the "detail" result is suitable for
+ * returning to the client as an errdetail() string, and is limited in size.
+ * The "detail_log" string is potentially much longer, and should be emitted
+ * to the server log only.
*
* We can find three different kinds of dependencies: dependencies on objects
* of the current database; dependencies on shared objects; and dependencies
*
* If we find a SHARED_DEPENDENCY_PIN entry, we can error out early.
*/
-char *
-checkSharedDependencies(Oid classId, Oid objectId)
+bool
+checkSharedDependencies(Oid classId, Oid objectId,
+ char **detail_msg, char **detail_log_msg)
{
Relation sdepRel;
ScanKeyData key[2];
SysScanDesc scan;
HeapTuple tup;
- int totalDeps = 0;
- int numLocalDeps = 0;
- int numSharedDeps = 0;
+ int numReportedDeps = 0;
+ int numNotReportedDeps = 0;
+ int numNotReportedDbs = 0;
List *remDeps = NIL;
ListCell *cell;
ObjectAddress object;
StringInfoData descs;
+ StringInfoData alldescs;
/*
- * We try to limit the number of reported dependencies to something sane,
- * both for the user's sake and to avoid blowing out memory.
+ * We limit the number of dependencies reported to the client to
+ * MAX_REPORTED_DEPS, since client software may not deal well with
+ * enormous error strings. The server log always gets a full report.
*/
#define MAX_REPORTED_DEPS 100
initStringInfo(&descs);
+ initStringInfo(&alldescs);
sdepRel = heap_open(SharedDependRelationId, AccessShareLock);
object.classId = sdepForm->classid;
object.objectId = sdepForm->objid;
- object.objectSubId = 0;
+ object.objectSubId = sdepForm->objsubid;
/*
* If it's a dependency local to this database or it's a shared
*/
if (sdepForm->dbid == MyDatabaseId)
{
- numLocalDeps++;
- if (++totalDeps <= MAX_REPORTED_DEPS)
+ if (numReportedDeps < MAX_REPORTED_DEPS)
+ {
+ numReportedDeps++;
storeObjectDescription(&descs, LOCAL_OBJECT, &object,
sdepForm->deptype, 0);
+ }
+ else
+ numNotReportedDeps++;
+ storeObjectDescription(&alldescs, LOCAL_OBJECT, &object,
+ sdepForm->deptype, 0);
}
else if (sdepForm->dbid == InvalidOid)
{
- numSharedDeps++;
- if (++totalDeps <= MAX_REPORTED_DEPS)
+ if (numReportedDeps < MAX_REPORTED_DEPS)
+ {
+ numReportedDeps++;
storeObjectDescription(&descs, SHARED_OBJECT, &object,
sdepForm->deptype, 0);
+ }
+ else
+ numNotReportedDeps++;
+ storeObjectDescription(&alldescs, SHARED_OBJECT, &object,
+ sdepForm->deptype, 0);
}
else
{
dep->dbOid = sdepForm->dbid;
dep->count = 1;
remDeps = lappend(remDeps, dep);
- totalDeps++;
}
}
}
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);
- }
-
+ /*
+ * Summarize dependencies in remote databases.
+ */
foreach(cell, remDeps)
{
remoteDep *dep = lfirst(cell);
object.objectId = dep->dbOid;
object.objectSubId = 0;
- storeObjectDescription(&descs, REMOTE_OBJECT, &object,
+ if (numReportedDeps < MAX_REPORTED_DEPS)
+ {
+ numReportedDeps++;
+ storeObjectDescription(&descs, REMOTE_OBJECT, &object,
+ SHARED_DEPENDENCY_INVALID, dep->count);
+ }
+ else
+ numNotReportedDbs++;
+ storeObjectDescription(&alldescs, REMOTE_OBJECT, &object,
SHARED_DEPENDENCY_INVALID, dep->count);
}
if (descs.len == 0)
{
pfree(descs.data);
- return NULL;
+ pfree(alldescs.data);
+ *detail_msg = *detail_log_msg = NULL;
+ return false;
}
- return descs.data;
+ if (numNotReportedDeps > 0)
+ appendStringInfo(&descs, ngettext("\nand %d other object "
+ "(see server log for list)",
+ "\nand %d other objects "
+ "(see server log for list)",
+ numNotReportedDeps),
+ numNotReportedDeps);
+ if (numNotReportedDbs > 0)
+ appendStringInfo(&descs, ngettext("\nand objects in %d other database "
+ "(see server log for list)",
+ "\nand objects in %d other databases "
+ "(see server log for list)",
+ numNotReportedDbs),
+ numNotReportedDbs);
+
+ *detail_msg = descs.data;
+ *detail_log_msg = alldescs.data;
+ return true;
}
/*
systable_endscan(scan);
/* Now delete all entries corresponding to the database itself */
- shdepDropDependency(sdepRel, DatabaseRelationId, databaseId,
+ shdepDropDependency(sdepRel, DatabaseRelationId, databaseId, 0, true,
InvalidOid, InvalidOid,
SHARED_DEPENDENCY_INVALID);
* Delete all pg_shdepend entries corresponding to an object that's being
* dropped or modified. The object is assumed to be either a shared object
* or local to the current database (the classId tells us which).
+ *
+ * If objectSubId is zero, we are deleting a whole object, so get rid of
+ * pg_shdepend entries for subobjects as well.
*/
void
-deleteSharedDependencyRecordsFor(Oid classId, Oid objectId)
+deleteSharedDependencyRecordsFor(Oid classId, Oid objectId, int32 objectSubId)
{
Relation sdepRel;
sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
- shdepDropDependency(sdepRel, classId, objectId,
+ shdepDropDependency(sdepRel, classId, objectId, objectSubId,
+ (objectSubId == 0),
InvalidOid, InvalidOid,
SHARED_DEPENDENCY_INVALID);
* locked.
*/
static void
-shdepAddDependency(Relation sdepRel, Oid classId, Oid objectId,
+shdepAddDependency(Relation sdepRel,
+ Oid classId, Oid objectId, int32 objsubId,
Oid refclassId, Oid refobjId,
SharedDependencyType deptype)
{
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_objsubid - 1] = Int32GetDatum(objsubId);
values[Anum_pg_shdepend_refclassid - 1] = ObjectIdGetDatum(refclassId);
values[Anum_pg_shdepend_refobjid - 1] = ObjectIdGetDatum(refobjId);
* Internal workhorse for deleting entries from pg_shdepend.
*
* We drop entries having the following properties:
- * dependent object is the one identified by classId/objectId
+ * dependent object is the one identified by classId/objectId/objsubId
* 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
*
+ * If drop_subobjects is true, we ignore objsubId and consider all entries
+ * matching classId/objectId.
+ *
* sdepRel must be the pg_shdepend relation, already opened and suitably
* locked.
*/
static void
-shdepDropDependency(Relation sdepRel, Oid classId, Oid objectId,
+shdepDropDependency(Relation sdepRel,
+ Oid classId, Oid objectId, int32 objsubId,
+ bool drop_subobjects,
Oid refclassId, Oid refobjId,
SharedDependencyType deptype)
{
- ScanKeyData key[3];
+ ScanKeyData key[4];
+ int nkeys;
SysScanDesc scan;
HeapTuple tup;
Anum_pg_shdepend_objid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(objectId));
+ if (drop_subobjects)
+ nkeys = 3;
+ else
+ {
+ ScanKeyInit(&key[3],
+ Anum_pg_shdepend_objsubid,
+ BTEqualStrategyNumber, F_INT4EQ,
+ Int32GetDatum(objsubId));
+ nkeys = 4;
+ }
scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
- SnapshotNow, 3, key);
+ SnapshotNow, nkeys, key);
while (HeapTupleIsValid(tup = systable_getnext(scan)))
{
case REMOTE_OBJECT:
/* translator: %s will always be "database %s" */
- appendStringInfo(descs, _("%d objects in %s"), count, objdesc);
+ appendStringInfo(descs, ngettext("%d object in %s",
+ "%d objects in %s",
+ count),
+ count, objdesc);
break;
default:
*
* Drop the objects owned by any one of the given RoleIds. If a role has
* access to an object, the grant will be removed as well (but the object
- * will not, of course.)
+ * will not, of course).
*
* We can revoke grants immediately while doing the scan, but drops are
* saved up and done all at once with performMultipleDeletions. This
deleteobjs = new_object_addresses();
- sdepRel = heap_open(SharedDependRelationId, AccessExclusiveLock);
+ /*
+ * We don't need this strong a lock here, but we'll call routines that
+ * acquire RowExclusiveLock. Better get that right now to avoid potential
+ * deadlock failures.
+ */
+ sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
/*
* For each role, find the dependent objects and drop them using the
while ((tuple = systable_getnext(scan)) != NULL)
{
- ObjectAddress obj;
- GrantObjectType objtype;
- InternalGrant istmt;
Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tuple);
+ InternalGrant istmt;
+ ObjectAddress obj;
/* We only operate on objects in the current database */
if (sdepForm->dbid != MyDatabaseId)
switch (sdepForm->deptype)
{
- /* Shouldn't happen */
+ /* Shouldn't happen */
case SHARED_DEPENDENCY_PIN:
case SHARED_DEPENDENCY_INVALID:
elog(ERROR, "unexpected dependency type");
default:
elog(ERROR, "unexpected object type %d",
sdepForm->classid);
- /* keep compiler quiet */
- objtype = (GrantObjectType) 0;
break;
}
istmt.is_grant = false;
istmt.objects = list_make1_oid(sdepForm->objid);
istmt.all_privs = true;
istmt.privileges = ACL_NO_RIGHTS;
+ istmt.col_privs = NIL;
istmt.grantees = list_make1_oid(roleid);
istmt.grant_option = false;
istmt.behavior = DROP_CASCADE;
/* Save it for deletion below */
obj.classId = sdepForm->classid;
obj.objectId = sdepForm->objid;
- obj.objectSubId = 0;
+ obj.objectSubId = sdepForm->objsubid;
add_exact_object_address(&obj, deleteobjs);
break;
}
/* the dependency mechanism does the actual work */
performMultipleDeletions(deleteobjs, behavior);
- heap_close(sdepRel, AccessExclusiveLock);
+ heap_close(sdepRel, RowExclusiveLock);
free_object_addresses(deleteobjs);
}
Relation sdepRel;
ListCell *cell;
- sdepRel = heap_open(SharedDependRelationId, AccessShareLock);
+ /*
+ * We don't need this strong a lock here, but we'll call routines that
+ * acquire RowExclusiveLock. Better get that right now to avoid potential
+ * deadlock problems.
+ */
+ sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
foreach(cell, roleids)
{
break;
case TypeRelationId:
- AlterTypeOwnerInternal(sdepForm->objid, newrole);
+ AlterTypeOwnerInternal(sdepForm->objid, newrole, true);
break;
case OperatorRelationId:
break;
case RelationRelationId:
+
/*
- * Pass recursing = true so that we don't fail on
- * indexes, owned sequences, etc when we happen
- * to visit them before their parent table.
+ * Pass recursing = true so that we don't fail on indexes,
+ * owned sequences, etc when we happen to visit them
+ * before their parent table.
*/
ATExecChangeOwner(sdepForm->objid, newrole, true);
break;
AlterFunctionOwner_oid(sdepForm->objid, newrole);
break;
+ case LanguageRelationId:
+ AlterLanguageOwner_oid(sdepForm->objid, newrole);
+ break;
+
default:
elog(ERROR, "unexpected classid %d", sdepForm->classid);
break;
systable_endscan(scan);
}
- heap_close(sdepRel, AccessShareLock);
+ heap_close(sdepRel, RowExclusiveLock);
}