* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.58 2006/07/14 14:52:17 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.59 2006/08/20 21:56:16 alvherre Exp $
*
*-------------------------------------------------------------------------
*/
/* expansible list of ObjectAddresses */
-typedef struct
+struct ObjectAddresses
{
ObjectAddress *refs; /* => palloc'd array */
int numrefs; /* current number of references */
int maxrefs; /* current size of palloc'd array */
-} ObjectAddresses;
+};
+/* typedef ObjectAddresses appears in dependency.h */
/* for find_expr_references_walker */
typedef struct
{
- ObjectAddresses addrs; /* addresses being accumulated */
+ ObjectAddresses *addrs; /* addresses being accumulated */
List *rtables; /* list of rangetables to resolve Vars */
} find_expr_references_context;
};
+static void performDeletionWithList(const ObjectAddress *object,
+ ObjectAddresses *oktodelete,
+ DropBehavior behavior,
+ ObjectAddresses *alreadyDeleted);
static void findAutoDeletableObjects(const ObjectAddress *object,
ObjectAddresses *oktodelete,
- Relation depRel);
+ Relation depRel, bool addself);
static bool recursiveDeletion(const ObjectAddress *object,
DropBehavior behavior,
int msglevel,
const ObjectAddress *callingObject,
ObjectAddresses *oktodelete,
- Relation depRel);
+ Relation depRel,
+ ObjectAddresses *alreadyDeleted);
static bool deleteDependentObjects(const ObjectAddress *object,
const char *objDescription,
DropBehavior behavior,
find_expr_references_context *context);
static void eliminate_duplicate_dependencies(ObjectAddresses *addrs);
static int object_address_comparator(const void *a, const void *b);
-static void init_object_addresses(ObjectAddresses *addrs);
static void add_object_address(ObjectClass oclass, Oid objectId, int32 subId,
ObjectAddresses *addrs);
-static void add_exact_object_address(const ObjectAddress *object,
- ObjectAddresses *addrs);
-static bool object_address_present(const ObjectAddress *object,
- ObjectAddresses *addrs);
-static void term_object_addresses(ObjectAddresses *addrs);
static void getRelationDescription(StringInfo buffer, Oid relid);
{
char *objDescription;
Relation depRel;
- ObjectAddresses oktodelete;
+ ObjectAddresses *oktodelete;
/*
* Get object description for possible use in failure message. Must do
* even if the actual deletion pass first reaches one of them via a
* non-auto dependency.
*/
- init_object_addresses(&oktodelete);
+ oktodelete = new_object_addresses();
- findAutoDeletableObjects(object, &oktodelete, depRel);
+ findAutoDeletableObjects(object, oktodelete, depRel, true);
if (!recursiveDeletion(object, behavior, NOTICE,
- NULL, &oktodelete, depRel))
+ NULL, oktodelete, depRel, NULL))
ereport(ERROR,
(errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
errmsg("cannot drop %s because other objects depend on it",
objDescription),
errhint("Use DROP ... CASCADE to drop the dependent objects too.")));
- term_object_addresses(&oktodelete);
+ free_object_addresses(oktodelete);
heap_close(depRel, RowExclusiveLock);
}
+/*
+ * performDeletionWithList: As above, but the oktodelete list may have already
+ * filled with some objects. Also, the deleted objects are saved in the
+ * alreadyDeleted list.
+ *
+ * XXX performDeletion could be refactored to be a thin wrapper to this
+ * function.
+ */
+static void
+performDeletionWithList(const ObjectAddress *object,
+ ObjectAddresses *oktodelete,
+ DropBehavior behavior,
+ ObjectAddresses *alreadyDeleted)
+{
+ char *objDescription;
+ Relation depRel;
+
+ /*
+ * Get object description for possible use in failure message. Must do
+ * this before deleting it ...
+ */
+ objDescription = getObjectDescription(object);
+
+ /*
+ * We save some cycles by opening pg_depend just once and passing the
+ * Relation pointer down to all the recursive deletion steps.
+ */
+ depRel = heap_open(DependRelationId, RowExclusiveLock);
+
+ /*
+ * Construct a list of objects that are reachable by AUTO or INTERNAL
+ * dependencies from the target object. These should be deleted silently,
+ * even if the actual deletion pass first reaches one of them via a
+ * non-auto dependency.
+ */
+ findAutoDeletableObjects(object, oktodelete, depRel, true);
+
+ if (!recursiveDeletion(object, behavior, NOTICE,
+ NULL, oktodelete, depRel, alreadyDeleted))
+ ereport(ERROR,
+ (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
+ errmsg("cannot drop %s because other objects depend on it",
+ objDescription),
+ errhint("Use DROP ... CASCADE to drop the dependent objects too.")));
+
+ heap_close(depRel, RowExclusiveLock);
+
+ pfree(objDescription);
+}
+
+/*
+ * performMultipleDeletion: Similar to performDeletion, but act on multiple
+ * objects at once.
+ *
+ * The main difference from issuing multiple performDeletion calls is that the
+ * list of objects that would be implicitly dropped, for each object to be
+ * dropped, is the union of the implicit-object list for all objects. This
+ * makes each check be more relaxed.
+ */
+void
+performMultipleDeletions(const ObjectAddresses *objects,
+ DropBehavior behavior)
+{
+ ObjectAddresses *implicit;
+ ObjectAddresses *alreadyDeleted;
+ Relation depRel;
+ int i;
+
+ implicit = new_object_addresses();
+ alreadyDeleted = new_object_addresses();
+
+ depRel = heap_open(DependRelationId, RowExclusiveLock);
+
+ /*
+ * Get the list of all objects that would be deleted after deleting the
+ * whole "objects" list. We do this by creating a list of all implicit
+ * (INTERNAL and AUTO) dependencies for each object we collected above.
+ * Note that we must exclude the objects themselves from this list!
+ */
+ for (i = 0; i < objects->numrefs; i++)
+ {
+ ObjectAddress obj = objects->refs[i];
+
+ /*
+ * If it's in the implicit list, we don't need to delete it explicitly
+ * nor follow the dependencies, because that was already done in a
+ * previous iteration.
+ */
+ if (object_address_present(&obj, implicit))
+ continue;
+
+ /*
+ * Add the objects dependent on this one to the global list of implicit
+ * objects.
+ */
+ findAutoDeletableObjects(&obj, implicit, depRel, false);
+ }
+
+ /* Do the deletion. */
+ for (i = 0; i < objects->numrefs; i++)
+ {
+ ObjectAddress obj = objects->refs[i];
+
+ /*
+ * Skip this object if it was already deleted in a previous iteration.
+ */
+ if (object_address_present(&obj, alreadyDeleted))
+ continue;
+
+ /*
+ * Skip this object if it's also present in the list of implicit
+ * objects --- it will be deleted later.
+ */
+ if (object_address_present(&obj, implicit))
+ continue;
+
+ /* delete it */
+ performDeletionWithList(&obj, implicit, behavior, alreadyDeleted);
+ }
+
+ heap_close(depRel, RowExclusiveLock);
+
+ free_object_addresses(implicit);
+ free_object_addresses(alreadyDeleted);
+}
+
/*
* deleteWhatDependsOn: attempt to drop everything that depends on the
* specified object, though not the object itself. Behavior is always
{
char *objDescription;
Relation depRel;
- ObjectAddresses oktodelete;
+ ObjectAddresses *oktodelete;
/*
* Get object description for possible use in failure messages
* even if the actual deletion pass first reaches one of them via a
* non-auto dependency.
*/
- init_object_addresses(&oktodelete);
+ oktodelete = new_object_addresses();
- findAutoDeletableObjects(object, &oktodelete, depRel);
+ findAutoDeletableObjects(object, oktodelete, depRel, true);
/*
* Now invoke only step 2 of recursiveDeletion: just recurse to the stuff
if (!deleteDependentObjects(object, objDescription,
DROP_CASCADE,
showNotices ? NOTICE : DEBUG2,
- &oktodelete, depRel))
+ oktodelete, depRel))
ereport(ERROR,
(errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
errmsg("failed to drop all objects depending on %s",
* anything then each recursive call will have ended with one.
*/
- term_object_addresses(&oktodelete);
+ free_object_addresses(oktodelete);
heap_close(depRel, RowExclusiveLock);
/*
* findAutoDeletableObjects: find all objects that are reachable by AUTO or
* INTERNAL dependency paths from the given object. Add them all to the
- * oktodelete list. Note that the originally given object will also be
- * added to the list.
+ * oktodelete list. If addself is true, the originally given object will also
+ * be added to the list.
*
* depRel is the already-open pg_depend relation.
*/
static void
findAutoDeletableObjects(const ObjectAddress *object,
ObjectAddresses *oktodelete,
- Relation depRel)
+ Relation depRel, bool addself)
{
ScanKeyData key[3];
int nkeys;
*/
if (object_address_present(object, oktodelete))
return;
- add_exact_object_address(object, oktodelete);
+ if (addself)
+ add_exact_object_address(object, oktodelete);
/*
* Scan pg_depend records that link to this object, showing the things
otherObject.classId = foundDep->classid;
otherObject.objectId = foundDep->objid;
otherObject.objectSubId = foundDep->objsubid;
- findAutoDeletableObjects(&otherObject, oktodelete, depRel);
+ findAutoDeletableObjects(&otherObject, oktodelete, depRel, true);
break;
case DEPENDENCY_PIN:
int msglevel,
const ObjectAddress *callingObject,
ObjectAddresses *oktodelete,
- Relation depRel)
+ Relation depRel,
+ ObjectAddresses *alreadyDeleted)
{
bool ok = true;
char *objDescription;
getObjectDescription(&owningObject))));
if (!recursiveDeletion(&owningObject, behavior, msglevel,
- object, oktodelete, depRel))
+ object, oktodelete, depRel, alreadyDeleted))
ok = false;
pfree(objDescription);
*/
/*
- * Step 3: delete the object itself.
+ * Step 3: delete the object itself, and save it to the list of
+ * deleted objects if appropiate.
*/
doDeletion(object);
+ if (alreadyDeleted != NULL)
+ {
+ if (!object_address_present(object, alreadyDeleted))
+ add_exact_object_address(object, alreadyDeleted);
+ }
/*
* Delete any comments associated with this object. (This is a convenient
getObjectDescription(&otherObject))));
if (!recursiveDeletion(&otherObject, behavior, msglevel,
- object, oktodelete, depRel))
+ object, oktodelete, depRel, NULL))
ok = false;
break;
case DEPENDENCY_AUTO:
getObjectDescription(&otherObject))));
if (!recursiveDeletion(&otherObject, behavior, msglevel,
- object, oktodelete, depRel))
+ object, oktodelete, depRel, NULL))
ok = false;
break;
case DEPENDENCY_PIN:
{
find_expr_references_context context;
- init_object_addresses(&context.addrs);
+ context.addrs = new_object_addresses();
/* Set up interpretation for Vars at varlevelsup = 0 */
context.rtables = list_make1(rtable);
find_expr_references_walker(expr, &context);
/* Remove any duplicates */
- eliminate_duplicate_dependencies(&context.addrs);
+ eliminate_duplicate_dependencies(context.addrs);
/* And record 'em */
recordMultipleDependencies(depender,
- context.addrs.refs, context.addrs.numrefs,
+ context.addrs->refs, context.addrs->numrefs,
behavior);
- term_object_addresses(&context.addrs);
+ free_object_addresses(context.addrs);
}
/*
find_expr_references_context context;
RangeTblEntry rte;
- init_object_addresses(&context.addrs);
+ context.addrs = new_object_addresses();
/* We gin up a rather bogus rangetable list to handle Vars */
MemSet(&rte, 0, sizeof(rte));
find_expr_references_walker(expr, &context);
/* Remove any duplicates */
- eliminate_duplicate_dependencies(&context.addrs);
+ eliminate_duplicate_dependencies(context.addrs);
/* Separate self-dependencies if necessary */
- if (behavior != self_behavior && context.addrs.numrefs > 0)
+ if (behavior != self_behavior && context.addrs->numrefs > 0)
{
- ObjectAddresses self_addrs;
+ ObjectAddresses *self_addrs;
ObjectAddress *outobj;
int oldref,
outrefs;
- init_object_addresses(&self_addrs);
+ self_addrs = new_object_addresses();
- outobj = context.addrs.refs;
+ outobj = context.addrs->refs;
outrefs = 0;
- for (oldref = 0; oldref < context.addrs.numrefs; oldref++)
+ for (oldref = 0; oldref < context.addrs->numrefs; oldref++)
{
- ObjectAddress *thisobj = context.addrs.refs + oldref;
+ ObjectAddress *thisobj = context.addrs->refs + oldref;
if (thisobj->classId == RelationRelationId &&
thisobj->objectId == relId)
{
/* Move this ref into self_addrs */
add_object_address(OCLASS_CLASS, relId, thisobj->objectSubId,
- &self_addrs);
+ self_addrs);
}
else
{
outrefs++;
}
}
- context.addrs.numrefs = outrefs;
+ context.addrs->numrefs = outrefs;
/* Record the self-dependencies */
recordMultipleDependencies(depender,
- self_addrs.refs, self_addrs.numrefs,
+ self_addrs->refs, self_addrs->numrefs,
self_behavior);
- term_object_addresses(&self_addrs);
+ free_object_addresses(self_addrs);
}
/* Record the external dependencies */
recordMultipleDependencies(depender,
- context.addrs.refs, context.addrs.numrefs,
+ context.addrs->refs, context.addrs->numrefs,
behavior);
- term_object_addresses(&context.addrs);
+ free_object_addresses(context.addrs);
}
/*
{
/* If it's a plain relation, reference this column */
add_object_address(OCLASS_CLASS, rte->relid, var->varattno,
- &context->addrs);
+ context->addrs);
}
else if (rte->rtekind == RTE_JOIN)
{
/* A constant must depend on the constant's datatype */
add_object_address(OCLASS_TYPE, con->consttype, 0,
- &context->addrs);
+ context->addrs);
/*
* If it's a regclass or similar literal referring to an existing
ObjectIdGetDatum(objoid),
0, 0, 0))
add_object_address(OCLASS_PROC, objoid, 0,
- &context->addrs);
+ context->addrs);
break;
case REGOPEROID:
case REGOPERATOROID:
ObjectIdGetDatum(objoid),
0, 0, 0))
add_object_address(OCLASS_OPERATOR, objoid, 0,
- &context->addrs);
+ context->addrs);
break;
case REGCLASSOID:
objoid = DatumGetObjectId(con->constvalue);
ObjectIdGetDatum(objoid),
0, 0, 0))
add_object_address(OCLASS_CLASS, objoid, 0,
- &context->addrs);
+ context->addrs);
break;
case REGTYPEOID:
objoid = DatumGetObjectId(con->constvalue);
ObjectIdGetDatum(objoid),
0, 0, 0))
add_object_address(OCLASS_TYPE, objoid, 0,
- &context->addrs);
+ context->addrs);
break;
}
}
/* A parameter must depend on the parameter's datatype */
add_object_address(OCLASS_TYPE, param->paramtype, 0,
- &context->addrs);
+ context->addrs);
}
if (IsA(node, FuncExpr))
{
FuncExpr *funcexpr = (FuncExpr *) node;
add_object_address(OCLASS_PROC, funcexpr->funcid, 0,
- &context->addrs);
+ context->addrs);
/* fall through to examine arguments */
}
if (IsA(node, OpExpr))
OpExpr *opexpr = (OpExpr *) node;
add_object_address(OCLASS_OPERATOR, opexpr->opno, 0,
- &context->addrs);
+ context->addrs);
/* fall through to examine arguments */
}
if (IsA(node, DistinctExpr))
DistinctExpr *distinctexpr = (DistinctExpr *) node;
add_object_address(OCLASS_OPERATOR, distinctexpr->opno, 0,
- &context->addrs);
+ context->addrs);
/* fall through to examine arguments */
}
if (IsA(node, ScalarArrayOpExpr))
ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) node;
add_object_address(OCLASS_OPERATOR, opexpr->opno, 0,
- &context->addrs);
+ context->addrs);
/* fall through to examine arguments */
}
if (IsA(node, NullIfExpr))
NullIfExpr *nullifexpr = (NullIfExpr *) node;
add_object_address(OCLASS_OPERATOR, nullifexpr->opno, 0,
- &context->addrs);
+ context->addrs);
/* fall through to examine arguments */
}
if (IsA(node, Aggref))
Aggref *aggref = (Aggref *) node;
add_object_address(OCLASS_PROC, aggref->aggfnoid, 0,
- &context->addrs);
+ context->addrs);
/* fall through to examine arguments */
}
if (is_subplan(node))
/* since there is no function dependency, need to depend on type */
add_object_address(OCLASS_TYPE, relab->resulttype, 0,
- &context->addrs);
+ context->addrs);
}
if (IsA(node, ConvertRowtypeExpr))
{
/* since there is no function dependency, need to depend on type */
add_object_address(OCLASS_TYPE, cvt->resulttype, 0,
- &context->addrs);
+ context->addrs);
}
if (IsA(node, RowExpr))
{
RowExpr *rowexpr = (RowExpr *) node;
add_object_address(OCLASS_TYPE, rowexpr->row_typeid, 0,
- &context->addrs);
+ context->addrs);
}
if (IsA(node, RowCompareExpr))
{
foreach(l, rcexpr->opnos)
{
add_object_address(OCLASS_OPERATOR, lfirst_oid(l), 0,
- &context->addrs);
+ context->addrs);
}
foreach(l, rcexpr->opclasses)
{
add_object_address(OCLASS_OPCLASS, lfirst_oid(l), 0,
- &context->addrs);
+ context->addrs);
}
/* fall through to examine arguments */
}
CoerceToDomain *cd = (CoerceToDomain *) node;
add_object_address(OCLASS_TYPE, cd->resulttype, 0,
- &context->addrs);
+ context->addrs);
}
if (IsA(node, Query))
{
{
case RTE_RELATION:
add_object_address(OCLASS_CLASS, rte->relid, 0,
- &context->addrs);
+ context->addrs);
break;
case RTE_FUNCTION:
foreach(ct, rte->funccoltypes)
{
add_object_address(OCLASS_TYPE, lfirst_oid(ct), 0,
- &context->addrs);
+ context->addrs);
}
break;
default:
/*
* Routines for handling an expansible array of ObjectAddress items.
*
- * init_object_addresses: initialize an ObjectAddresses array.
+ * new_object_addresses: create a new ObjectAddresses array.
*/
-static void
-init_object_addresses(ObjectAddresses *addrs)
+ObjectAddresses *
+new_object_addresses(void)
{
- /* Initialize array to empty */
+ ObjectAddresses *addrs;
+
+ addrs = palloc(sizeof(ObjectAddresses));
+
addrs->numrefs = 0;
- addrs->maxrefs = 32; /* arbitrary initial array size */
+ addrs->maxrefs = 32;
addrs->refs = (ObjectAddress *)
palloc(addrs->maxrefs * sizeof(ObjectAddress));
+
+ return addrs;
}
/*
*
* As above, but specify entry exactly.
*/
-static void
+void
add_exact_object_address(const ObjectAddress *object,
ObjectAddresses *addrs)
{
*
* We return "true" if object is a subobject of something in the array, too.
*/
-static bool
+bool
object_address_present(const ObjectAddress *object,
ObjectAddresses *addrs)
{
/*
* Clean up when done with an ObjectAddresses array.
*/
-static void
-term_object_addresses(ObjectAddresses *addrs)
+void
+free_object_addresses(ObjectAddresses *addrs)
{
pfree(addrs->refs);
+ pfree(addrs);
}
/*