2002-07-12 20:43:19 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* pg_depend.c
|
|
|
|
* routines to support manipulation of the pg_depend relation
|
|
|
|
*
|
2004-12-31 23:04:05 +01:00
|
|
|
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
2002-07-12 20:43:19 +02:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2005-11-21 13:49:33 +01:00
|
|
|
* $PostgreSQL: pgsql/src/backend/catalog/pg_depend.c,v 1.16 2005/11/21 12:49:30 alvherre Exp $
|
2002-07-12 20:43:19 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
|
|
|
|
#include "access/genam.h"
|
|
|
|
#include "access/heapam.h"
|
|
|
|
#include "catalog/indexing.h"
|
|
|
|
#include "catalog/dependency.h"
|
|
|
|
#include "catalog/pg_depend.h"
|
|
|
|
#include "miscadmin.h"
|
|
|
|
#include "utils/fmgroids.h"
|
|
|
|
|
|
|
|
|
|
|
|
static bool isObjectPinned(const ObjectAddress *object, Relation rel);
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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_depend, without any other processing.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
recordDependencyOn(const ObjectAddress *depender,
|
|
|
|
const ObjectAddress *referenced,
|
|
|
|
DependencyType behavior)
|
2002-07-16 07:53:34 +02:00
|
|
|
{
|
|
|
|
recordMultipleDependencies(depender, referenced, 1, behavior);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Record multiple dependencies (of the same kind) for a single dependent
|
2002-09-04 22:31:48 +02:00
|
|
|
* object. This has a little less overhead than recording each separately.
|
2002-07-16 07:53:34 +02:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
recordMultipleDependencies(const ObjectAddress *depender,
|
|
|
|
const ObjectAddress *referenced,
|
|
|
|
int nreferenced,
|
|
|
|
DependencyType behavior)
|
2002-07-12 20:43:19 +02:00
|
|
|
{
|
|
|
|
Relation dependDesc;
|
2002-08-05 05:29:17 +02:00
|
|
|
CatalogIndexState indstate;
|
2002-07-12 20:43:19 +02:00
|
|
|
HeapTuple tup;
|
|
|
|
int i;
|
|
|
|
char nulls[Natts_pg_depend];
|
|
|
|
Datum values[Natts_pg_depend];
|
2002-07-16 07:53:34 +02:00
|
|
|
|
|
|
|
if (nreferenced <= 0)
|
|
|
|
return; /* nothing to do */
|
2002-07-12 20:43:19 +02:00
|
|
|
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* During bootstrap, do nothing since pg_depend may not exist yet. initdb
|
|
|
|
* will fill in appropriate pg_depend entries after bootstrap.
|
2002-07-12 20:43:19 +02:00
|
|
|
*/
|
|
|
|
if (IsBootstrapProcessingMode())
|
|
|
|
return;
|
|
|
|
|
2005-04-14 22:03:27 +02:00
|
|
|
dependDesc = heap_open(DependRelationId, RowExclusiveLock);
|
2002-07-12 20:43:19 +02:00
|
|
|
|
2002-08-05 05:29:17 +02:00
|
|
|
/* Don't open indexes unless we need to make an update */
|
|
|
|
indstate = NULL;
|
|
|
|
|
2002-07-16 07:53:34 +02:00
|
|
|
memset(nulls, ' ', sizeof(nulls));
|
|
|
|
|
|
|
|
for (i = 0; i < nreferenced; i++, referenced++)
|
2002-07-12 20:43:19 +02:00
|
|
|
{
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* If the referenced object is pinned by the system, there's no real
|
|
|
|
* need to record dependencies on it. This saves lots of space in
|
|
|
|
* pg_depend, so it's worth the time taken to check.
|
2002-07-12 20:43:19 +02:00
|
|
|
*/
|
2002-07-16 07:53:34 +02:00
|
|
|
if (!isObjectPinned(referenced, dependDesc))
|
2002-07-12 20:43:19 +02:00
|
|
|
{
|
2002-07-16 07:53:34 +02:00
|
|
|
/*
|
|
|
|
* Record the Dependency. Note we don't bother to check for
|
|
|
|
* duplicate dependencies; there's no harm in them.
|
|
|
|
*/
|
2002-09-04 22:31:48 +02:00
|
|
|
values[Anum_pg_depend_classid - 1] = ObjectIdGetDatum(depender->classId);
|
|
|
|
values[Anum_pg_depend_objid - 1] = ObjectIdGetDatum(depender->objectId);
|
|
|
|
values[Anum_pg_depend_objsubid - 1] = Int32GetDatum(depender->objectSubId);
|
2002-07-16 07:53:34 +02:00
|
|
|
|
2002-09-04 22:31:48 +02:00
|
|
|
values[Anum_pg_depend_refclassid - 1] = ObjectIdGetDatum(referenced->classId);
|
|
|
|
values[Anum_pg_depend_refobjid - 1] = ObjectIdGetDatum(referenced->objectId);
|
|
|
|
values[Anum_pg_depend_refobjsubid - 1] = Int32GetDatum(referenced->objectSubId);
|
2002-07-16 07:53:34 +02:00
|
|
|
|
2002-09-04 22:31:48 +02:00
|
|
|
values[Anum_pg_depend_deptype - 1] = CharGetDatum((char) behavior);
|
2002-07-16 07:53:34 +02:00
|
|
|
|
|
|
|
tup = heap_formtuple(dependDesc->rd_att, values, nulls);
|
|
|
|
|
|
|
|
simple_heap_insert(dependDesc, tup);
|
|
|
|
|
2002-08-05 05:29:17 +02:00
|
|
|
/* keep indexes current */
|
|
|
|
if (indstate == NULL)
|
|
|
|
indstate = CatalogOpenIndexes(dependDesc);
|
|
|
|
|
|
|
|
CatalogIndexInsert(indstate, tup);
|
2002-07-16 07:53:34 +02:00
|
|
|
|
|
|
|
heap_freetuple(tup);
|
2002-07-12 20:43:19 +02:00
|
|
|
}
|
2002-07-16 07:53:34 +02:00
|
|
|
}
|
2002-07-12 20:43:19 +02:00
|
|
|
|
2002-08-05 05:29:17 +02:00
|
|
|
if (indstate != NULL)
|
|
|
|
CatalogCloseIndexes(indstate);
|
2002-07-12 20:43:19 +02:00
|
|
|
|
|
|
|
heap_close(dependDesc, RowExclusiveLock);
|
|
|
|
}
|
|
|
|
|
2002-07-17 00:12:20 +02:00
|
|
|
/*
|
|
|
|
* deleteDependencyRecordsFor -- delete all records with given depender
|
2002-08-11 23:17:35 +02:00
|
|
|
* classId/objectId. Returns the number of records deleted.
|
2002-07-17 00:12:20 +02:00
|
|
|
*
|
|
|
|
* This is used when redefining an existing object. Links leading to the
|
|
|
|
* object do not change, and links leading from it will be recreated
|
|
|
|
* (possibly with some differences from before).
|
|
|
|
*/
|
2002-08-11 23:17:35 +02:00
|
|
|
long
|
2002-07-17 00:12:20 +02:00
|
|
|
deleteDependencyRecordsFor(Oid classId, Oid objectId)
|
|
|
|
{
|
2002-09-04 22:31:48 +02:00
|
|
|
long count = 0;
|
|
|
|
Relation depRel;
|
|
|
|
ScanKeyData key[2];
|
|
|
|
SysScanDesc scan;
|
|
|
|
HeapTuple tup;
|
2002-07-17 00:12:20 +02:00
|
|
|
|
2005-04-14 22:03:27 +02:00
|
|
|
depRel = heap_open(DependRelationId, RowExclusiveLock);
|
2002-07-17 00:12:20 +02:00
|
|
|
|
2003-11-12 22:15:59 +01:00
|
|
|
ScanKeyInit(&key[0],
|
|
|
|
Anum_pg_depend_classid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(classId));
|
|
|
|
ScanKeyInit(&key[1],
|
|
|
|
Anum_pg_depend_objid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(objectId));
|
2002-07-17 00:12:20 +02:00
|
|
|
|
2005-04-14 22:03:27 +02:00
|
|
|
scan = systable_beginscan(depRel, DependDependerIndexId, true,
|
2002-07-17 00:12:20 +02:00
|
|
|
SnapshotNow, 2, key);
|
|
|
|
|
|
|
|
while (HeapTupleIsValid(tup = systable_getnext(scan)))
|
|
|
|
{
|
|
|
|
simple_heap_delete(depRel, &tup->t_self);
|
2002-08-11 23:17:35 +02:00
|
|
|
count++;
|
2002-07-17 00:12:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
systable_endscan(scan);
|
|
|
|
|
|
|
|
heap_close(depRel, RowExclusiveLock);
|
2002-08-11 23:17:35 +02:00
|
|
|
|
|
|
|
return count;
|
2002-07-17 00:12:20 +02:00
|
|
|
}
|
2002-07-12 20:43:19 +02:00
|
|
|
|
2005-11-21 13:49:33 +01:00
|
|
|
/*
|
|
|
|
* objectIsInternalDependency -- return whether the specified object
|
|
|
|
* is listed as an internal dependency for some other object.
|
|
|
|
*
|
|
|
|
* This is used to implement DROP/REASSIGN OWNED. We cannot invoke
|
|
|
|
* performDeletion blindly, because it may try to drop or modify an internal-
|
|
|
|
* dependent object before the "main" object, so we need to skip the first
|
|
|
|
* object and expect it to be automatically dropped when the main object is
|
|
|
|
* dropped.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
objectIsInternalDependency(Oid classId, Oid objectId)
|
|
|
|
{
|
|
|
|
Relation depRel;
|
|
|
|
ScanKeyData key[2];
|
|
|
|
SysScanDesc scan;
|
|
|
|
HeapTuple tup;
|
|
|
|
bool isdep = false;
|
|
|
|
|
|
|
|
depRel = heap_open(DependRelationId, AccessShareLock);
|
|
|
|
|
|
|
|
ScanKeyInit(&key[0],
|
|
|
|
Anum_pg_depend_classid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(classId));
|
|
|
|
ScanKeyInit(&key[1],
|
|
|
|
Anum_pg_depend_objid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(objectId));
|
|
|
|
|
|
|
|
scan = systable_beginscan(depRel, DependDependerIndexId, true,
|
|
|
|
SnapshotNow, 2, key);
|
|
|
|
|
|
|
|
while (HeapTupleIsValid(tup = systable_getnext(scan)))
|
|
|
|
{
|
|
|
|
Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
|
|
|
|
|
|
|
|
if (depForm->deptype == DEPENDENCY_INTERNAL)
|
|
|
|
{
|
|
|
|
/* No need to keep scanning */
|
|
|
|
isdep = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
systable_endscan(scan);
|
|
|
|
|
|
|
|
heap_close(depRel, AccessShareLock);
|
|
|
|
|
|
|
|
return isdep;
|
|
|
|
}
|
|
|
|
|
2005-08-01 06:03:59 +02:00
|
|
|
/*
|
|
|
|
* Adjust dependency record(s) to point to a different object of the same type
|
|
|
|
*
|
|
|
|
* classId/objectId specify the referencing object.
|
|
|
|
* refClassId/oldRefObjectId specify the old referenced object.
|
|
|
|
* newRefObjectId is the new referenced object (must be of class refClassId).
|
|
|
|
*
|
|
|
|
* Note the lack of objsubid parameters. If there are subobject references
|
|
|
|
* they will all be readjusted.
|
|
|
|
*
|
|
|
|
* Returns the number of records updated.
|
|
|
|
*/
|
|
|
|
long
|
|
|
|
changeDependencyFor(Oid classId, Oid objectId,
|
|
|
|
Oid refClassId, Oid oldRefObjectId,
|
|
|
|
Oid newRefObjectId)
|
|
|
|
{
|
|
|
|
long count = 0;
|
|
|
|
Relation depRel;
|
|
|
|
ScanKeyData key[2];
|
|
|
|
SysScanDesc scan;
|
|
|
|
HeapTuple tup;
|
|
|
|
ObjectAddress objAddr;
|
|
|
|
bool newIsPinned;
|
|
|
|
|
|
|
|
depRel = heap_open(DependRelationId, RowExclusiveLock);
|
|
|
|
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* If oldRefObjectId is pinned, there won't be any dependency entries on
|
|
|
|
* it --- we can't cope in that case. (This isn't really worth expending
|
|
|
|
* code to fix, in current usage; it just means you can't rename stuff out
|
|
|
|
* of pg_catalog, which would likely be a bad move anyway.)
|
2005-08-01 06:03:59 +02:00
|
|
|
*/
|
|
|
|
objAddr.classId = refClassId;
|
|
|
|
objAddr.objectId = oldRefObjectId;
|
|
|
|
objAddr.objectSubId = 0;
|
|
|
|
|
|
|
|
if (isObjectPinned(&objAddr, depRel))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
2005-10-15 04:49:52 +02:00
|
|
|
errmsg("cannot remove dependency on %s because it is a system object",
|
|
|
|
getObjectDescription(&objAddr))));
|
2005-08-01 06:03:59 +02:00
|
|
|
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* We can handle adding a dependency on something pinned, though, since
|
|
|
|
* that just means deleting the dependency entry.
|
2005-08-01 06:03:59 +02:00
|
|
|
*/
|
|
|
|
objAddr.objectId = newRefObjectId;
|
|
|
|
|
|
|
|
newIsPinned = isObjectPinned(&objAddr, depRel);
|
|
|
|
|
|
|
|
/* Now search for dependency records */
|
|
|
|
ScanKeyInit(&key[0],
|
|
|
|
Anum_pg_depend_classid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(classId));
|
|
|
|
ScanKeyInit(&key[1],
|
|
|
|
Anum_pg_depend_objid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(objectId));
|
|
|
|
|
|
|
|
scan = systable_beginscan(depRel, DependDependerIndexId, true,
|
|
|
|
SnapshotNow, 2, key);
|
|
|
|
|
|
|
|
while (HeapTupleIsValid((tup = systable_getnext(scan))))
|
|
|
|
{
|
|
|
|
Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
|
|
|
|
|
|
|
|
if (depform->refclassid == refClassId &&
|
|
|
|
depform->refobjid == oldRefObjectId)
|
|
|
|
{
|
|
|
|
if (newIsPinned)
|
|
|
|
simple_heap_delete(depRel, &tup->t_self);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* make a modifiable copy */
|
|
|
|
tup = heap_copytuple(tup);
|
|
|
|
depform = (Form_pg_depend) GETSTRUCT(tup);
|
|
|
|
|
|
|
|
depform->refobjid = newRefObjectId;
|
|
|
|
|
|
|
|
simple_heap_update(depRel, &tup->t_self, tup);
|
|
|
|
CatalogUpdateIndexes(depRel, tup);
|
|
|
|
|
|
|
|
heap_freetuple(tup);
|
|
|
|
}
|
|
|
|
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
systable_endscan(scan);
|
|
|
|
|
|
|
|
heap_close(depRel, RowExclusiveLock);
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2002-07-12 20:43:19 +02:00
|
|
|
/*
|
|
|
|
* isObjectPinned()
|
|
|
|
*
|
|
|
|
* Test if an object is required for basic database functionality.
|
|
|
|
* Caller must already have opened pg_depend.
|
|
|
|
*
|
|
|
|
* The passed subId, if any, is ignored; we assume that only whole objects
|
|
|
|
* are pinned (and that this implies pinning their components).
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
isObjectPinned(const ObjectAddress *object, Relation rel)
|
|
|
|
{
|
|
|
|
bool ret = false;
|
2002-09-04 22:31:48 +02:00
|
|
|
SysScanDesc scan;
|
2002-07-12 20:43:19 +02:00
|
|
|
HeapTuple tup;
|
|
|
|
ScanKeyData key[2];
|
|
|
|
|
2003-11-12 22:15:59 +01:00
|
|
|
ScanKeyInit(&key[0],
|
|
|
|
Anum_pg_depend_refclassid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(object->classId));
|
2002-07-12 20:43:19 +02:00
|
|
|
|
2003-11-12 22:15:59 +01:00
|
|
|
ScanKeyInit(&key[1],
|
|
|
|
Anum_pg_depend_refobjid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(object->objectId));
|
2002-07-12 20:43:19 +02:00
|
|
|
|
2005-04-14 22:03:27 +02:00
|
|
|
scan = systable_beginscan(rel, DependReferenceIndexId, true,
|
2002-07-12 20:43:19 +02:00
|
|
|
SnapshotNow, 2, key);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Since we won't generate additional pg_depend entries for pinned
|
2005-10-15 04:49:52 +02:00
|
|
|
* 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.
|
2002-07-12 20:43:19 +02:00
|
|
|
*/
|
|
|
|
tup = systable_getnext(scan);
|
|
|
|
if (HeapTupleIsValid(tup))
|
|
|
|
{
|
2002-09-04 22:31:48 +02:00
|
|
|
Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(tup);
|
2002-07-12 20:43:19 +02:00
|
|
|
|
|
|
|
if (foundDep->deptype == DEPENDENCY_PIN)
|
|
|
|
ret = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
systable_endscan(scan);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|