aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/catalog/dependency.c649
-rw-r--r--src/backend/catalog/heap.c18
-rw-r--r--src/backend/catalog/index.c5
-rw-r--r--src/backend/catalog/pg_constraint.c21
-rw-r--r--src/backend/catalog/pg_depend.c88
-rw-r--r--src/backend/commands/tablecmds.c3
-rw-r--r--src/backend/rewrite/rewriteDefine.c32
-rw-r--r--src/include/catalog/dependency.h11
-rw-r--r--src/include/catalog/pg_constraint.h3
-rw-r--r--src/test/regress/expected/privileges.out14
-rw-r--r--src/test/regress/expected/rangefuncs.out26
-rw-r--r--src/test/regress/sql/privileges.sql12
-rw-r--r--src/test/regress/sql/rangefuncs.sql26
13 files changed, 768 insertions, 140 deletions
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 7622d035f7c..527cca3a85b 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/catalog/dependency.c,v 1.2 2002/07/15 16:33:31 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/catalog/dependency.c,v 1.3 2002/07/16 05:53:33 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -34,6 +34,8 @@
#include "commands/trigger.h"
#include "lib/stringinfo.h"
#include "miscadmin.h"
+#include "optimizer/clauses.h"
+#include "parser/parsetree.h"
#include "rewrite/rewriteRemove.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
@@ -51,14 +53,56 @@ typedef enum ObjectClasses
OCLASS_LANGUAGE, /* pg_language */
OCLASS_OPERATOR, /* pg_operator */
OCLASS_REWRITE, /* pg_rewrite */
- OCLASS_TRIGGER /* pg_trigger */
+ OCLASS_TRIGGER, /* pg_trigger */
+ MAX_OCLASS /* MUST BE LAST */
} ObjectClasses;
+/* expansible list of ObjectAddresses */
+typedef struct ObjectAddresses
+{
+ ObjectAddress *refs; /* => palloc'd array */
+ int numrefs; /* current number of references */
+ int maxrefs; /* current size of palloc'd array */
+ struct ObjectAddresses *link; /* list link for use in recursion */
+} ObjectAddresses;
+
+/* for find_expr_references_walker */
+typedef struct
+{
+ ObjectAddresses addrs; /* addresses being accumulated */
+ List *rtables; /* list of rangetables to resolve Vars */
+} find_expr_references_context;
+
+
+/*
+ * Because not all system catalogs have predetermined OIDs, we build a table
+ * mapping between ObjectClasses and OIDs. This is done at most once per
+ * backend run, to minimize lookup overhead.
+ */
+static bool object_classes_initialized = false;
+static Oid object_classes[MAX_OCLASS];
+
+
static bool recursiveDeletion(const ObjectAddress *object,
DropBehavior behavior,
- int recursionLevel,
+ const ObjectAddress *callingObject,
+ ObjectAddresses *pending,
Relation depRel);
static void doDeletion(const ObjectAddress *object);
+static bool find_expr_references_walker(Node *node,
+ 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(ObjectClasses oclass, Oid objectId, int32 subId,
+ ObjectAddresses *addrs);
+static void add_exact_object_address(const ObjectAddress *object,
+ ObjectAddresses *addrs);
+static void del_object_address(const ObjectAddress *object,
+ ObjectAddresses *addrs);
+static void del_object_address_by_index(int index, ObjectAddresses *addrs);
+static void term_object_addresses(ObjectAddresses *addrs);
+static void init_object_classes(void);
static ObjectClasses getObjectClass(const ObjectAddress *object);
static char *getObjectDescription(const ObjectAddress *object);
static void getRelationDescription(StringInfo buffer, Oid relid);
@@ -93,7 +137,7 @@ performDeletion(const ObjectAddress *object,
*/
depRel = heap_openr(DependRelationName, RowExclusiveLock);
- if (!recursiveDeletion(object, behavior, 0, depRel))
+ if (!recursiveDeletion(object, behavior, NULL, NULL, depRel))
elog(ERROR, "Cannot drop %s because other objects depend on it"
"\n\tUse DROP ... CASCADE to drop the dependent objects too",
objDescription);
@@ -107,28 +151,58 @@ performDeletion(const ObjectAddress *object,
/*
* recursiveDeletion: delete a single object for performDeletion.
*
- * Returns TRUE if successful, FALSE if not. recursionLevel is 0 at the
- * outer level, >0 when deleting a dependent object.
+ * Returns TRUE if successful, FALSE if not.
+ *
+ * callingObject is NULL at the outer level, else identifies the object that
+ * we recursed from (the reference object that someone else needs to delete).
+ * pending is a linked list of objects that outer recursion levels want to
+ * delete. We remove the target object from any outer-level list it may
+ * appear in.
+ * depRel is the already-open pg_depend relation.
*
* In RESTRICT mode, we perform all the deletions anyway, but elog a NOTICE
* and return FALSE if we find a restriction violation. performDeletion
* will then abort the transaction to nullify the deletions. We have to
* do it this way to (a) report all the direct and indirect dependencies
* while (b) not going into infinite recursion if there's a cycle.
+ *
+ * This is even more complex than one could wish, because it is possible for
+ * the same pair of objects to be related by both NORMAL and AUTO (or IMPLICIT)
+ * dependencies. (Since one or both paths might be indirect, it's very hard
+ * to prevent this; we must cope instead.) If there is an AUTO/IMPLICIT
+ * deletion path then we should perform the deletion, and not fail because
+ * of the NORMAL dependency. So, when we hit a NORMAL dependency we don't
+ * immediately decide we've failed; instead we stick the NORMAL dependent
+ * object into a list of pending deletions. If we find a legal path to delete
+ * that object later on, the recursive call will remove it from our pending
+ * list. After we've exhausted all such possibilities, we remove the
+ * remaining pending objects anyway, but emit a notice and prepare to return
+ * FALSE. (We have to do it this way because the dependent objects *must* be
+ * removed before we can remove the object they depend on.)
+ *
+ * Note: in the case where the AUTO path is traversed first, we will never
+ * see the NORMAL dependency path because of the pg_depend removals done in
+ * recursive executions of step 1. The pending list is necessary essentially
+ * just to make the behavior independent of the order in which pg_depend
+ * entries are visited.
*/
static bool
recursiveDeletion(const ObjectAddress *object,
DropBehavior behavior,
- int recursionLevel,
+ const ObjectAddress *callingObject,
+ ObjectAddresses *pending,
Relation depRel)
{
bool ok = true;
char *objDescription;
+ ObjectAddresses mypending;
ScanKeyData key[3];
int nkeys;
SysScanDesc scan;
HeapTuple tup;
ObjectAddress otherObject;
+ ObjectAddress owningObject;
+ bool amOwned = false;
/*
* Get object description for possible use in messages. Must do this
@@ -137,10 +211,16 @@ recursiveDeletion(const ObjectAddress *object,
objDescription = getObjectDescription(object);
/*
+ * Initialize list of restricted objects, and set up chain link.
+ */
+ init_object_addresses(&mypending);
+ mypending.link = pending;
+
+ /*
* Step 1: find and remove pg_depend records that link from this
* object to others. We have to do this anyway, and doing it first
* ensures that we avoid infinite recursion in the case of cycles.
- * Also, some dependency types require an error here.
+ * Also, some dependency types require extra processing here.
*
* When dropping a whole object (subId = 0), remove all pg_depend
* records for its sub-objects too.
@@ -180,17 +260,48 @@ recursiveDeletion(const ObjectAddress *object,
break;
case DEPENDENCY_INTERNAL:
/*
- * Disallow direct DROP of an object that is part of the
- * implementation of another object. (We just elog here,
- * rather than issuing a notice and continuing, since
- * no other dependencies are likely to be interesting.)
+ * This object is part of the internal implementation
+ * of another object. We have three cases:
+ *
+ * 1. At the outermost recursion level, disallow the DROP.
+ * (We just elog here, rather than considering this drop
+ * to be pending, since no other dependencies are likely
+ * to be interesting.)
*/
- if (recursionLevel == 0)
+ if (callingObject == NULL)
+ {
+ char *otherObjDesc = getObjectDescription(&otherObject);
+
elog(ERROR, "Cannot drop %s because %s requires it"
- "\n\tYou may DROP the other object instead",
- objDescription,
- getObjectDescription(&otherObject));
- break;
+ "\n\tYou may drop %s instead",
+ objDescription, otherObjDesc, otherObjDesc);
+ }
+ /*
+ * 2. When recursing from the other end of this dependency,
+ * it's okay to continue with the deletion. This holds when
+ * recursing from a whole object that includes the nominal
+ * other end as a component, too.
+ */
+ if (callingObject->classId == otherObject.classId &&
+ callingObject->objectId == otherObject.objectId &&
+ (callingObject->objectSubId == otherObject.objectSubId ||
+ callingObject->objectSubId == 0))
+ break;
+ /*
+ * 3. When recursing from anyplace else, transform this
+ * deletion request into a delete of the other object.
+ * (This will be an error condition iff RESTRICT mode.)
+ * In this case we finish deleting my dependencies except
+ * for the INTERNAL link, which will be needed to cause
+ * the owning object to recurse back to me.
+ */
+ if (amOwned) /* shouldn't happen */
+ elog(ERROR, "recursiveDeletion: multiple INTERNAL dependencies for %s",
+ objDescription);
+ owningObject = otherObject;
+ amOwned = true;
+ /* "continue" bypasses the simple_heap_delete call below */
+ continue;
case DEPENDENCY_PIN:
/*
* Should not happen; PIN dependencies should have zeroes
@@ -219,6 +330,34 @@ recursiveDeletion(const ObjectAddress *object,
CommandCounterIncrement();
/*
+ * If we found we are owned by another object, ask it to delete itself
+ * instead of proceeding.
+ */
+ if (amOwned)
+ {
+ if (behavior == DROP_RESTRICT)
+ {
+ elog(NOTICE, "%s depends on %s",
+ getObjectDescription(&owningObject),
+ objDescription);
+ ok = false;
+ }
+ else
+ elog(NOTICE, "Drop cascades to %s",
+ getObjectDescription(&owningObject));
+
+ if (!recursiveDeletion(&owningObject, behavior,
+ object,
+ pending, depRel))
+ ok = false;
+
+ pfree(objDescription);
+ term_object_addresses(&mypending);
+
+ return ok;
+ }
+
+ /*
* Step 2: scan pg_depend records that link to this object, showing
* the things that depend on it. Recursively delete those things.
* (We don't delete the pg_depend records here, as the recursive call
@@ -268,18 +407,24 @@ recursiveDeletion(const ObjectAddress *object,
case DEPENDENCY_NORMAL:
if (behavior == DROP_RESTRICT)
{
- elog(NOTICE, "%s depends on %s",
- getObjectDescription(&otherObject),
- objDescription);
- ok = false;
+ /*
+ * We've found a restricted object (or at least one
+ * that's not deletable along this path). Log for later
+ * processing. (Note it's okay if the same object gets
+ * into mypending multiple times.)
+ */
+ add_exact_object_address(&otherObject, &mypending);
}
else
+ {
elog(NOTICE, "Drop cascades to %s",
getObjectDescription(&otherObject));
- if (!recursiveDeletion(&otherObject, behavior,
- recursionLevel + 1, depRel))
- ok = false;
+ if (!recursiveDeletion(&otherObject, behavior,
+ object,
+ &mypending, depRel))
+ ok = false;
+ }
break;
case DEPENDENCY_AUTO:
case DEPENDENCY_INTERNAL:
@@ -292,7 +437,8 @@ recursiveDeletion(const ObjectAddress *object,
getObjectDescription(&otherObject));
if (!recursiveDeletion(&otherObject, behavior,
- recursionLevel + 1, depRel))
+ object,
+ &mypending, depRel))
ok = false;
break;
case DEPENDENCY_PIN:
@@ -313,6 +459,36 @@ recursiveDeletion(const ObjectAddress *object,
systable_endscan(scan);
/*
+ * If we found no restricted objects, or got rid of them all via other
+ * paths, we're in good shape. Otherwise continue step 2 by processing
+ * the remaining restricted objects.
+ */
+ if (mypending.numrefs > 0)
+ {
+ /*
+ * Successively extract and delete each remaining object.
+ * Note that the right things will happen if some of these objects
+ * depend on others: we'll report/delete each one exactly once.
+ */
+ while (mypending.numrefs > 0)
+ {
+ ObjectAddress otherObject = mypending.refs[0];
+
+ del_object_address_by_index(0, &mypending);
+
+ elog(NOTICE, "%s depends on %s",
+ getObjectDescription(&otherObject),
+ objDescription);
+ if (!recursiveDeletion(&otherObject, behavior,
+ object,
+ &mypending, depRel))
+ ok = false;
+ }
+
+ ok = false;
+ }
+
+ /*
* We do not need CommandCounterIncrement here, since if step 2 did
* anything then each recursive call will have ended with one.
*/
@@ -329,6 +505,11 @@ recursiveDeletion(const ObjectAddress *object,
DeleteComments(object->objectId, object->classId, object->objectSubId);
/*
+ * If this object is mentioned in any caller's pending list, remove it.
+ */
+ del_object_address(object, pending);
+
+ /*
* CommandCounterIncrement here to ensure that preceding changes
* are all visible.
*/
@@ -338,6 +519,7 @@ recursiveDeletion(const ObjectAddress *object,
* And we're done!
*/
pfree(objDescription);
+ term_object_addresses(&mypending);
return ok;
}
@@ -422,6 +604,387 @@ doDeletion(const ObjectAddress *object)
}
/*
+ * recordDependencyOnExpr - find expression dependencies
+ *
+ * This is used to find the dependencies of rules, constraint expressions,
+ * etc.
+ *
+ * Given an expression or query in node-tree form, find all the objects
+ * it refers to (tables, columns, operators, functions, etc). Record
+ * a dependency of the specified type from the given depender object
+ * to each object mentioned in the expression.
+ *
+ * rtable is the rangetable to be used to interpret Vars with varlevelsup=0.
+ * It can be NIL if no such variables are expected.
+ *
+ * XXX is it important to create dependencies on the datatypes mentioned in
+ * the expression? In most cases this would be redundant (eg, a ref to an
+ * operator indirectly references its input and output datatypes), but I'm
+ * not quite convinced there are no cases where we need it.
+ */
+void
+recordDependencyOnExpr(const ObjectAddress *depender,
+ Node *expr, List *rtable,
+ DependencyType behavior)
+{
+ find_expr_references_context context;
+
+ init_object_addresses(&context.addrs);
+
+ /* Set up interpretation for Vars at varlevelsup = 0 */
+ context.rtables = makeList1(rtable);
+
+ /* Scan the expression tree for referenceable objects */
+ find_expr_references_walker(expr, &context);
+
+ /* Remove any duplicates */
+ eliminate_duplicate_dependencies(&context.addrs);
+
+ /* And record 'em */
+ recordMultipleDependencies(depender,
+ context.addrs.refs, context.addrs.numrefs,
+ behavior);
+
+ term_object_addresses(&context.addrs);
+}
+
+/*
+ * Recursively search an expression tree for object references.
+ */
+static bool
+find_expr_references_walker(Node *node,
+ find_expr_references_context *context)
+{
+ if (node == NULL)
+ return false;
+ if (IsA(node, Var))
+ {
+ Var *var = (Var *) node;
+ int levelsup;
+ List *rtable,
+ *rtables;
+ RangeTblEntry *rte;
+
+ /* Find matching rtable entry, or complain if not found */
+ levelsup = var->varlevelsup;
+ rtables = context->rtables;
+ while (levelsup--)
+ {
+ if (rtables == NIL)
+ break;
+ rtables = lnext(rtables);
+ }
+ if (rtables == NIL)
+ elog(ERROR, "find_expr_references_walker: bogus varlevelsup %d",
+ var->varlevelsup);
+ rtable = lfirst(rtables);
+ if (var->varno <= 0 || var->varno > length(rtable))
+ elog(ERROR, "find_expr_references_walker: bogus varno %d",
+ var->varno);
+ rte = rt_fetch(var->varno, rtable);
+ /* If it's a plain relation, reference this column */
+ if (rte->rtekind == RTE_RELATION)
+ add_object_address(OCLASS_CLASS, rte->relid, var->varattno,
+ &context->addrs);
+ return false;
+ }
+ if (IsA(node, Expr))
+ {
+ Expr *expr = (Expr *) node;
+
+ if (expr->opType == OP_EXPR)
+ {
+ Oper *oper = (Oper *) expr->oper;
+
+ add_object_address(OCLASS_OPERATOR, oper->opno, 0,
+ &context->addrs);
+ }
+ else if (expr->opType == FUNC_EXPR)
+ {
+ Func *func = (Func *) expr->oper;
+
+ add_object_address(OCLASS_PROC, func->funcid, 0,
+ &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);
+ /* fall through to examine arguments */
+ }
+ if (is_subplan(node))
+ {
+ /* Extra work needed here if we ever need this case */
+ elog(ERROR, "find_expr_references_walker: already-planned subqueries not supported");
+ }
+ if (IsA(node, Query))
+ {
+ /* Recurse into RTE subquery or not-yet-planned sublink subquery */
+ Query *query = (Query *) node;
+ List *rtable;
+ bool result;
+
+ /*
+ * Add whole-relation refs for each plain relation mentioned in the
+ * subquery's rtable. (Note: query_tree_walker takes care of
+ * recursing into RTE_FUNCTION and RTE_SUBQUERY RTEs, so no need
+ * to do that here.)
+ */
+ foreach(rtable, query->rtable)
+ {
+ RangeTblEntry *rte = (RangeTblEntry *) lfirst(rtable);
+
+ if (rte->rtekind == RTE_RELATION)
+ add_object_address(OCLASS_CLASS, rte->relid, 0,
+ &context->addrs);
+ }
+
+ /* Examine substructure of query */
+ context->rtables = lcons(query->rtable, context->rtables);
+ result = query_tree_walker(query,
+ find_expr_references_walker,
+ (void *) context, true);
+ context->rtables = lnext(context->rtables);
+ return result;
+ }
+ return expression_tree_walker(node, find_expr_references_walker,
+ (void *) context);
+}
+
+/*
+ * Given an array of dependency references, eliminate any duplicates.
+ */
+static void
+eliminate_duplicate_dependencies(ObjectAddresses *addrs)
+{
+ ObjectAddress *priorobj;
+ int oldref,
+ newrefs;
+
+ if (addrs->numrefs <= 1)
+ return; /* nothing to do */
+
+ /* Sort the refs so that duplicates are adjacent */
+ qsort((void *) addrs->refs, addrs->numrefs, sizeof(ObjectAddress),
+ object_address_comparator);
+
+ /* Remove dups */
+ priorobj = addrs->refs;
+ newrefs = 1;
+ for (oldref = 1; oldref < addrs->numrefs; oldref++)
+ {
+ ObjectAddress *thisobj = addrs->refs + oldref;
+
+ if (priorobj->classId == thisobj->classId &&
+ priorobj->objectId == thisobj->objectId)
+ {
+ if (priorobj->objectSubId == thisobj->objectSubId)
+ continue; /* identical, so drop thisobj */
+ /*
+ * If we have a whole-object reference and a reference to a
+ * part of the same object, we don't need the whole-object
+ * reference (for example, we don't need to reference both
+ * table foo and column foo.bar). The whole-object reference
+ * will always appear first in the sorted list.
+ */
+ if (priorobj->objectSubId == 0)
+ {
+ /* replace whole ref with partial */
+ priorobj->objectSubId = thisobj->objectSubId;
+ continue;
+ }
+ }
+ /* Not identical, so add thisobj to output set */
+ priorobj++;
+ priorobj->classId = thisobj->classId;
+ priorobj->objectId = thisobj->objectId;
+ priorobj->objectSubId = thisobj->objectSubId;
+ newrefs++;
+ }
+
+ addrs->numrefs = newrefs;
+}
+
+/*
+ * qsort comparator for ObjectAddress items
+ */
+static int
+object_address_comparator(const void *a, const void *b)
+{
+ const ObjectAddress *obja = (const ObjectAddress *) a;
+ const ObjectAddress *objb = (const ObjectAddress *) b;
+
+ if (obja->classId < objb->classId)
+ return -1;
+ if (obja->classId > objb->classId)
+ return 1;
+ if (obja->objectId < objb->objectId)
+ return -1;
+ if (obja->objectId > objb->objectId)
+ return 1;
+ /*
+ * We sort the subId as an unsigned int so that 0 will come first.
+ * See logic in eliminate_duplicate_dependencies.
+ */
+ if ((unsigned int) obja->objectSubId < (unsigned int) objb->objectSubId)
+ return -1;
+ if ((unsigned int) obja->objectSubId > (unsigned int) objb->objectSubId)
+ return 1;
+ return 0;
+}
+
+/*
+ * Routines for handling an expansible array of ObjectAddress items.
+ *
+ * init_object_addresses: initialize an ObjectAddresses array.
+ */
+static void
+init_object_addresses(ObjectAddresses *addrs)
+{
+ /* Initialize array to empty */
+ addrs->numrefs = 0;
+ addrs->maxrefs = 32; /* arbitrary initial array size */
+ addrs->refs = (ObjectAddress *)
+ palloc(addrs->maxrefs * sizeof(ObjectAddress));
+ addrs->link = NULL;
+
+ /* Initialize object_classes[] if not done yet */
+ /* This will be needed by add_object_address() */
+ if (!object_classes_initialized)
+ init_object_classes();
+}
+
+/*
+ * Add an entry to an ObjectAddresses array.
+ *
+ * It is convenient to specify the class by ObjectClass rather than directly
+ * by catalog OID.
+ */
+static void
+add_object_address(ObjectClasses oclass, Oid objectId, int32 subId,
+ ObjectAddresses *addrs)
+{
+ ObjectAddress *item;
+
+ /* enlarge array if needed */
+ if (addrs->numrefs >= addrs->maxrefs)
+ {
+ addrs->maxrefs *= 2;
+ addrs->refs = (ObjectAddress *)
+ repalloc(addrs->refs, addrs->maxrefs * sizeof(ObjectAddress));
+ }
+ /* record this item */
+ item = addrs->refs + addrs->numrefs;
+ item->classId = object_classes[oclass];
+ item->objectId = objectId;
+ item->objectSubId = subId;
+ addrs->numrefs++;
+}
+
+/*
+ * Add an entry to an ObjectAddresses array.
+ *
+ * As above, but specify entry exactly.
+ */
+static void
+add_exact_object_address(const ObjectAddress *object,
+ ObjectAddresses *addrs)
+{
+ ObjectAddress *item;
+
+ /* enlarge array if needed */
+ if (addrs->numrefs >= addrs->maxrefs)
+ {
+ addrs->maxrefs *= 2;
+ addrs->refs = (ObjectAddress *)
+ repalloc(addrs->refs, addrs->maxrefs * sizeof(ObjectAddress));
+ }
+ /* record this item */
+ item = addrs->refs + addrs->numrefs;
+ *item = *object;
+ addrs->numrefs++;
+}
+
+/*
+ * If an ObjectAddresses array contains any matches for the given object,
+ * remove it/them. Also, do the same in any linked ObjectAddresses arrays.
+ */
+static void
+del_object_address(const ObjectAddress *object,
+ ObjectAddresses *addrs)
+{
+ for (; addrs != NULL; addrs = addrs->link)
+ {
+ int i;
+
+ /* Scan backwards to simplify deletion logic. */
+ for (i = addrs->numrefs-1; i >= 0; i--)
+ {
+ ObjectAddress *thisobj = addrs->refs + i;
+
+ if (object->classId == thisobj->classId &&
+ object->objectId == thisobj->objectId)
+ {
+ /*
+ * Delete if exact match, or if thisobj is a subobject of
+ * the passed-in object.
+ */
+ if (object->objectSubId == thisobj->objectSubId ||
+ object->objectSubId == 0)
+ del_object_address_by_index(i, addrs);
+ }
+ }
+ }
+}
+
+/*
+ * Remove an entry (specified by array index) from an ObjectAddresses array.
+ * The end item in the list is moved down to fill the hole.
+ */
+static void
+del_object_address_by_index(int index, ObjectAddresses *addrs)
+{
+ Assert(index >= 0 && index < addrs->numrefs);
+ addrs->refs[index] = addrs->refs[addrs->numrefs - 1];
+ addrs->numrefs--;
+}
+
+/*
+ * Clean up when done with an ObjectAddresses array.
+ */
+static void
+term_object_addresses(ObjectAddresses *addrs)
+{
+ pfree(addrs->refs);
+}
+
+/*
+ * Initialize the object_classes[] table.
+ *
+ * Although some of these OIDs aren't compile-time constants, they surely
+ * shouldn't change during a backend's run. So, we look them up the
+ * first time through and then cache them.
+ */
+static void
+init_object_classes(void)
+{
+ object_classes[OCLASS_CLASS] = RelOid_pg_class;
+ object_classes[OCLASS_PROC] = RelOid_pg_proc;
+ object_classes[OCLASS_TYPE] = RelOid_pg_type;
+ object_classes[OCLASS_CONSTRAINT] = get_system_catalog_relid(ConstraintRelationName);
+ object_classes[OCLASS_DEFAULT] = get_system_catalog_relid(AttrDefaultRelationName);
+ object_classes[OCLASS_LANGUAGE] = get_system_catalog_relid(LanguageRelationName);
+ object_classes[OCLASS_OPERATOR] = get_system_catalog_relid(OperatorRelationName);
+ object_classes[OCLASS_REWRITE] = get_system_catalog_relid(RewriteRelationName);
+ object_classes[OCLASS_TRIGGER] = get_system_catalog_relid(TriggerRelationName);
+ object_classes_initialized = true;
+}
+
+/*
* Determine the class of a given object identified by objectAddress.
*
* This function is needed just because some of the system catalogs do
@@ -430,14 +993,6 @@ doDeletion(const ObjectAddress *object)
static ObjectClasses
getObjectClass(const ObjectAddress *object)
{
- static bool reloids_initialized = false;
- static Oid reloid_pg_constraint;
- static Oid reloid_pg_attrdef;
- static Oid reloid_pg_language;
- static Oid reloid_pg_operator;
- static Oid reloid_pg_rewrite;
- static Oid reloid_pg_trigger;
-
/* Easy for the bootstrapped catalogs... */
switch (object->classId)
{
@@ -456,48 +1011,36 @@ getObjectClass(const ObjectAddress *object)
/*
* Handle cases where catalog's OID is not hardwired.
- *
- * Although these OIDs aren't compile-time constants, they surely
- * shouldn't change during a backend's run. So, look them up the
- * first time through and then cache them.
*/
- if (!reloids_initialized)
- {
- reloid_pg_constraint = get_system_catalog_relid(ConstraintRelationName);
- reloid_pg_attrdef = get_system_catalog_relid(AttrDefaultRelationName);
- reloid_pg_language = get_system_catalog_relid(LanguageRelationName);
- reloid_pg_operator = get_system_catalog_relid(OperatorRelationName);
- reloid_pg_rewrite = get_system_catalog_relid(RewriteRelationName);
- reloid_pg_trigger = get_system_catalog_relid(TriggerRelationName);
- reloids_initialized = true;
- }
+ if (!object_classes_initialized)
+ init_object_classes();
- if (object->classId == reloid_pg_constraint)
+ if (object->classId == object_classes[OCLASS_CONSTRAINT])
{
Assert(object->objectSubId == 0);
return OCLASS_CONSTRAINT;
}
- if (object->classId == reloid_pg_attrdef)
+ if (object->classId == object_classes[OCLASS_DEFAULT])
{
Assert(object->objectSubId == 0);
return OCLASS_DEFAULT;
}
- if (object->classId == reloid_pg_language)
+ if (object->classId == object_classes[OCLASS_LANGUAGE])
{
Assert(object->objectSubId == 0);
return OCLASS_LANGUAGE;
}
- if (object->classId == reloid_pg_operator)
+ if (object->classId == object_classes[OCLASS_OPERATOR])
{
Assert(object->objectSubId == 0);
return OCLASS_OPERATOR;
}
- if (object->classId == reloid_pg_rewrite)
+ if (object->classId == object_classes[OCLASS_REWRITE])
{
Assert(object->objectSubId == 0);
return OCLASS_REWRITE;
}
- if (object->classId == reloid_pg_trigger)
+ if (object->classId == object_classes[OCLASS_TRIGGER])
{
Assert(object->objectSubId == 0);
return OCLASS_TRIGGER;
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 5c9499c86b5..a8700c5efd9 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.207 2002/07/15 16:33:31 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.208 2002/07/16 05:53:33 tgl Exp $
*
*
* INTERFACE ROUTINES
@@ -1157,10 +1157,15 @@ StoreAttrDefault(Relation rel, AttrNumber attnum, char *adbin)
colobject.objectSubId = attnum;
recordDependencyOn(&defobject, &colobject, DEPENDENCY_AUTO);
+
+ /*
+ * Record dependencies on objects used in the expression, too.
+ */
+ recordDependencyOnExpr(&defobject, expr, NIL, DEPENDENCY_NORMAL);
}
/*
- * Store a constraint expression for the given relation.
+ * Store a check-constraint expression for the given relation.
* The expression must be presented as a nodeToString() string.
*
* Caller is responsible for updating the count of constraints
@@ -1191,6 +1196,10 @@ StoreRelCheck(Relation rel, char *ccname, char *ccbin)
/*
* Find columns of rel that are used in ccbin
+ *
+ * NB: pull_var_clause is okay here only because we don't allow
+ * subselects in check constraints; it would fail to examine the
+ * contents of subselects.
*/
varList = pull_var_clause(expr, false);
keycount = length(varList);
@@ -1226,8 +1235,8 @@ StoreRelCheck(Relation rel, char *ccname, char *ccbin)
false, /* Is Deferrable */
false, /* Is Deferred */
RelationGetRelid(rel), /* relation */
- attNos, /* List of attributes in the constraint */
- keycount, /* # attributes in the constraint */
+ attNos, /* attrs in the constraint */
+ keycount, /* # attrs in the constraint */
InvalidOid, /* not a domain constraint */
InvalidOid, /* Foreign key fields */
NULL,
@@ -1235,6 +1244,7 @@ StoreRelCheck(Relation rel, char *ccname, char *ccbin)
' ',
' ',
' ',
+ expr, /* Tree form check constraint */
ccbin, /* Binary form check constraint */
ccsrc); /* Source form check constraint */
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 996fb80a7cb..c827edfb3e5 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.183 2002/07/14 21:08:08 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.184 2002/07/16 05:53:33 tgl Exp $
*
*
* INTERFACE ROUTINES
@@ -712,7 +712,8 @@ index_create(Oid heapRelationId,
' ',
' ',
' ',
- NULL, /* Constraint Bin & Src */
+ NULL, /* no check constraint */
+ NULL,
NULL);
referenced.classId = get_system_catalog_relid(ConstraintRelationName);
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index 47712dde1d6..41580f2c53f 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/catalog/pg_constraint.c,v 1.1 2002/07/12 18:43:15 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/catalog/pg_constraint.c,v 1.2 2002/07/16 05:53:33 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -52,6 +52,7 @@ CreateConstraintEntry(const char *constraintName,
char foreignUpdateType,
char foreignDeleteType,
char foreignMatchType,
+ Node *conExpr,
const char *conBin,
const char *conSrc)
{
@@ -227,6 +228,24 @@ CreateConstraintEntry(const char *constraintName,
}
}
+ if (conExpr != NULL)
+ {
+ /*
+ * Register dependencies from constraint to objects mentioned
+ * in CHECK expression. We gin up a rather bogus rangetable
+ * list to handle any Vars in the constraint.
+ */
+ RangeTblEntry rte;
+
+ MemSet(&rte, 0, sizeof(rte));
+ rte.type = T_RangeTblEntry;
+ rte.rtekind = RTE_RELATION;
+ rte.relid = relId;
+
+ recordDependencyOnExpr(&conobject, conExpr, makeList1(&rte),
+ DEPENDENCY_NORMAL);
+ }
+
return conOid;
}
diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c
index 4057374069a..75a1ba01c4e 100644
--- a/src/backend/catalog/pg_depend.c
+++ b/src/backend/catalog/pg_depend.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/catalog/pg_depend.c,v 1.1 2002/07/12 18:43:15 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/catalog/pg_depend.c,v 1.2 2002/07/16 05:53:33 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -39,12 +39,29 @@ recordDependencyOn(const ObjectAddress *depender,
const ObjectAddress *referenced,
DependencyType behavior)
{
+ recordMultipleDependencies(depender, referenced, 1, behavior);
+}
+
+/*
+ * Record multiple dependencies (of the same kind) for a single dependent
+ * object. This has a little less overhead than recording each separately.
+ */
+void
+recordMultipleDependencies(const ObjectAddress *depender,
+ const ObjectAddress *referenced,
+ int nreferenced,
+ DependencyType behavior)
+{
Relation dependDesc;
HeapTuple tup;
int i;
char nulls[Natts_pg_depend];
Datum values[Natts_pg_depend];
Relation idescs[Num_pg_depend_indices];
+ bool indices_opened = false;
+
+ if (nreferenced <= 0)
+ return; /* nothing to do */
/*
* During bootstrap, do nothing since pg_depend may not exist yet.
@@ -55,44 +72,51 @@ recordDependencyOn(const ObjectAddress *depender,
dependDesc = heap_openr(DependRelationName, RowExclusiveLock);
- /*
- * 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.
- */
- if (!isObjectPinned(referenced, dependDesc))
+ memset(nulls, ' ', sizeof(nulls));
+
+ for (i = 0; i < nreferenced; i++, referenced++)
{
/*
- * Record the Dependency. Note we don't bother to check for
- * duplicate dependencies; there's no harm in them.
+ * 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.
*/
- for (i = 0; i < Natts_pg_depend; ++i)
+ if (!isObjectPinned(referenced, dependDesc))
{
- nulls[i] = ' ';
- values[i] = (Datum) 0;
+ /*
+ * Record the Dependency. Note we don't bother to check for
+ * duplicate dependencies; there's no harm in them.
+ */
+ 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);
+
+ 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);
+
+ values[Anum_pg_depend_deptype -1] = CharGetDatum((char) behavior);
+
+ tup = heap_formtuple(dependDesc->rd_att, values, nulls);
+
+ simple_heap_insert(dependDesc, tup);
+
+ /*
+ * Keep indices current
+ */
+ if (!indices_opened)
+ {
+ CatalogOpenIndices(Num_pg_depend_indices, Name_pg_depend_indices, idescs);
+ indices_opened = true;
+ }
+ CatalogIndexInsert(idescs, Num_pg_depend_indices, dependDesc, tup);
+
+ heap_freetuple(tup);
}
+ }
- 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);
-
- 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);
-
- values[Anum_pg_depend_deptype -1] = CharGetDatum((char) behavior);
-
- tup = heap_formtuple(dependDesc->rd_att, values, nulls);
-
- simple_heap_insert(dependDesc, tup);
-
- /*
- * Keep indices current
- */
- CatalogOpenIndices(Num_pg_depend_indices, Name_pg_depend_indices, idescs);
- CatalogIndexInsert(idescs, Num_pg_depend_indices, dependDesc, tup);
+ if (indices_opened)
CatalogCloseIndices(Num_pg_depend_indices, idescs);
- }
heap_close(dependDesc, RowExclusiveLock);
}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 3722a071082..099c6351b7f 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.21 2002/07/15 16:33:31 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.22 2002/07/16 05:53:33 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -2708,6 +2708,7 @@ createForeignKeyConstraint(Relation rel, Relation pkrel,
fkconstraint->fk_upd_action,
fkconstraint->fk_del_action,
fkconstraint->fk_matchtype,
+ NULL, /* no check constraint */
NULL,
NULL);
}
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index 5922cb4ab5b..59d0744dcee 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.74 2002/07/12 18:43:17 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.75 2002/07/16 05:53:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -47,9 +47,11 @@ InsertRule(char *rulname,
Oid eventrel_oid,
AttrNumber evslot_index,
bool evinstead,
- char *evqual,
- char *actiontree)
+ Node *event_qual,
+ List *action)
{
+ char *evqual = nodeToString(event_qual);
+ char *actiontree = nodeToString((Node *) action);
int i;
Datum values[Natts_pg_rewrite];
char nulls[Natts_pg_rewrite];
@@ -123,6 +125,21 @@ InsertRule(char *rulname,
recordDependencyOn(&myself, &referenced,
(evtype == CMD_SELECT) ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);
+ /*
+ * Also install dependencies on objects referenced in action and qual.
+ */
+ recordDependencyOnExpr(&myself, (Node *) action, NIL,
+ DEPENDENCY_NORMAL);
+ if (event_qual != NULL)
+ {
+ /* Find query containing OLD/NEW rtable entries */
+ Query *qry = (Query *) lfirst(action);
+
+ qry = getInsertSelectQuery(qry, NULL);
+ recordDependencyOnExpr(&myself, event_qual, qry->rtable,
+ DEPENDENCY_NORMAL);
+ }
+
heap_close(pg_rewrite_desc, RowExclusiveLock);
return rewriteObjectId;
@@ -141,8 +158,6 @@ DefineQueryRewrite(RuleStmt *stmt)
Oid ruleId;
int event_attno;
Oid event_attype;
- char *actionP,
- *event_qualP;
List *l;
Query *query;
AclResult aclresult;
@@ -342,16 +357,13 @@ DefineQueryRewrite(RuleStmt *stmt)
/* discard rule if it's null action and not INSTEAD; it's a no-op */
if (action != NIL || is_instead)
{
- event_qualP = nodeToString(event_qual);
- actionP = nodeToString(action);
-
ruleId = InsertRule(stmt->rulename,
event_type,
ev_relid,
event_attno,
is_instead,
- event_qualP,
- actionP);
+ event_qual,
+ action);
/*
* Set pg_class 'relhasrules' field TRUE for event relation. If
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index c890acb687a..bcadaee732f 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: dependency.h,v 1.1 2002/07/12 18:43:19 tgl Exp $
+ * $Id: dependency.h,v 1.2 2002/07/16 05:53:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -83,10 +83,19 @@ typedef struct ObjectAddress
extern void performDeletion(const ObjectAddress *object,
DropBehavior behavior);
+extern void recordDependencyOnExpr(const ObjectAddress *depender,
+ Node *expr, List *rtable,
+ DependencyType behavior);
+
/* in pg_depend.c */
extern void recordDependencyOn(const ObjectAddress *depender,
const ObjectAddress *referenced,
DependencyType behavior);
+extern void recordMultipleDependencies(const ObjectAddress *depender,
+ const ObjectAddress *referenced,
+ int nreferenced,
+ DependencyType behavior);
+
#endif /* DEPENDENCY_H */
diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h
index ec493146524..e4a7dc86a2d 100644
--- a/src/include/catalog/pg_constraint.h
+++ b/src/include/catalog/pg_constraint.h
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: pg_constraint.h,v 1.1 2002/07/12 18:43:19 tgl Exp $
+ * $Id: pg_constraint.h,v 1.2 2002/07/16 05:53:34 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
@@ -158,6 +158,7 @@ extern Oid CreateConstraintEntry(const char *constraintName,
char foreignUpdateType,
char foreignDeleteType,
char foreignMatchType,
+ Node *conExpr,
const char *conBin,
const char *conSrc);
diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out
index e1fe2f60246..5eb078e0c45 100644
--- a/src/test/regress/expected/privileges.out
+++ b/src/test/regress/expected/privileges.out
@@ -544,13 +544,19 @@ from (select oid from pg_class where relname = 'atest1') as t1;
\c regression
DROP FUNCTION testfunc2(int);
DROP FUNCTION testfunc4(boolean);
-DROP TABLE atest1;
-DROP TABLE atest2;
-DROP TABLE atest3;
DROP VIEW atestv1;
DROP VIEW atestv2;
-DROP VIEW atestv3;
+-- this should cascade to drop atestv4
+DROP VIEW atestv3 CASCADE;
+NOTICE: Drop cascades to rule _RETURN on view atestv3
+NOTICE: Drop cascades to rule _RETURN on view atestv4
+NOTICE: Drop cascades to view atestv4
+-- this should complain "does not exist"
DROP VIEW atestv4;
+ERROR: view "atestv4" does not exist
+DROP TABLE atest1;
+DROP TABLE atest2;
+DROP TABLE atest3;
DROP GROUP regressgroup1;
DROP GROUP regressgroup2;
DROP USER regressuser1;
diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out
index 590382d0fa6..f930c857aa6 100644
--- a/src/test/regress/expected/rangefuncs.out
+++ b/src/test/regress/expected/rangefuncs.out
@@ -61,6 +61,7 @@ SELECT * FROM vw_getfoo;
(1 row)
-- sql, proretset = t, prorettype = b
+DROP VIEW vw_getfoo;
DROP FUNCTION getfoo(int);
CREATE FUNCTION getfoo(int) RETURNS setof int AS 'SELECT fooid FROM foo WHERE fooid = $1;' LANGUAGE SQL;
SELECT * FROM getfoo(1) AS t1;
@@ -70,7 +71,6 @@ SELECT * FROM getfoo(1) AS t1;
1
(2 rows)
-DROP VIEW vw_getfoo;
CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1);
SELECT * FROM vw_getfoo;
getfoo
@@ -80,6 +80,7 @@ SELECT * FROM vw_getfoo;
(2 rows)
-- sql, proretset = t, prorettype = b
+DROP VIEW vw_getfoo;
DROP FUNCTION getfoo(int);
CREATE FUNCTION getfoo(int) RETURNS setof text AS 'SELECT fooname FROM foo WHERE fooid = $1;' LANGUAGE SQL;
SELECT * FROM getfoo(1) AS t1;
@@ -89,7 +90,6 @@ SELECT * FROM getfoo(1) AS t1;
Ed
(2 rows)
-DROP VIEW vw_getfoo;
CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1);
SELECT * FROM vw_getfoo;
getfoo
@@ -99,6 +99,7 @@ SELECT * FROM vw_getfoo;
(2 rows)
-- sql, proretset = f, prorettype = c
+DROP VIEW vw_getfoo;
DROP FUNCTION getfoo(int);
CREATE FUNCTION getfoo(int) RETURNS foo AS 'SELECT * FROM foo WHERE fooid = $1;' LANGUAGE SQL;
SELECT * FROM getfoo(1) AS t1;
@@ -107,7 +108,6 @@ SELECT * FROM getfoo(1) AS t1;
1 | 1 | Joe
(1 row)
-DROP VIEW vw_getfoo;
CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1);
SELECT * FROM vw_getfoo;
fooid | foosubid | fooname
@@ -116,6 +116,7 @@ SELECT * FROM vw_getfoo;
(1 row)
-- sql, proretset = t, prorettype = c
+DROP VIEW vw_getfoo;
DROP FUNCTION getfoo(int);
CREATE FUNCTION getfoo(int) RETURNS setof foo AS 'SELECT * FROM foo WHERE fooid = $1;' LANGUAGE SQL;
SELECT * FROM getfoo(1) AS t1;
@@ -125,7 +126,6 @@ SELECT * FROM getfoo(1) AS t1;
1 | 2 | Ed
(2 rows)
-DROP VIEW vw_getfoo;
CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1);
SELECT * FROM vw_getfoo;
fooid | foosubid | fooname
@@ -135,6 +135,7 @@ SELECT * FROM vw_getfoo;
(2 rows)
-- plpgsql, proretset = f, prorettype = b
+DROP VIEW vw_getfoo;
DROP FUNCTION getfoo(int);
CREATE FUNCTION getfoo(int) RETURNS int AS 'DECLARE fooint int; BEGIN SELECT fooid into fooint FROM foo WHERE fooid = $1; RETURN fooint; END;' LANGUAGE 'plpgsql';
SELECT * FROM getfoo(1) AS t1;
@@ -143,7 +144,6 @@ SELECT * FROM getfoo(1) AS t1;
1
(1 row)
-DROP VIEW vw_getfoo;
CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1);
SELECT * FROM vw_getfoo;
getfoo
@@ -152,6 +152,7 @@ SELECT * FROM vw_getfoo;
(1 row)
-- plpgsql, proretset = f, prorettype = c
+DROP VIEW vw_getfoo;
DROP FUNCTION getfoo(int);
CREATE FUNCTION getfoo(int) RETURNS foo AS 'DECLARE footup foo%ROWTYPE; BEGIN SELECT * into footup FROM foo WHERE fooid = $1; RETURN footup; END;' LANGUAGE 'plpgsql';
SELECT * FROM getfoo(1) AS t1;
@@ -160,7 +161,6 @@ SELECT * FROM getfoo(1) AS t1;
1 | 1 | Joe
(1 row)
-DROP VIEW vw_getfoo;
CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1);
SELECT * FROM vw_getfoo;
fooid | foosubid | fooname
@@ -168,11 +168,11 @@ SELECT * FROM vw_getfoo;
1 | 1 | Joe
(1 row)
-DROP TABLE foo2;
+DROP VIEW vw_getfoo;
+DROP FUNCTION getfoo(int);
DROP FUNCTION foot(int);
+DROP TABLE foo2;
DROP TABLE foo;
-DROP FUNCTION getfoo(int);
-DROP VIEW vw_getfoo;
-- Rescan tests --
CREATE TABLE foorescan (fooid int, foosubid int, fooname text, primary key(fooid,foosubid));
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index 'foorescan_pkey' for table 'foorescan'
@@ -339,10 +339,10 @@ SELECT * FROM fooview2 AS fv WHERE fv.maxsubid = 5;
5008 | 5
(6 rows)
-DROP TABLE foorescan;
-DROP FUNCTION foorescan(int,int);
DROP VIEW vw_foorescan;
-DROP TABLE barrescan;
-DROP FUNCTION foorescan(int);
DROP VIEW fooview1;
DROP VIEW fooview2;
+DROP FUNCTION foorescan(int,int);
+DROP FUNCTION foorescan(int);
+DROP TABLE foorescan;
+DROP TABLE barrescan;
diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql
index e53b600d753..26b1be671ce 100644
--- a/src/test/regress/sql/privileges.sql
+++ b/src/test/regress/sql/privileges.sql
@@ -292,15 +292,17 @@ from (select oid from pg_class where relname = 'atest1') as t1;
DROP FUNCTION testfunc2(int);
DROP FUNCTION testfunc4(boolean);
-DROP TABLE atest1;
-DROP TABLE atest2;
-DROP TABLE atest3;
-
DROP VIEW atestv1;
DROP VIEW atestv2;
-DROP VIEW atestv3;
+-- this should cascade to drop atestv4
+DROP VIEW atestv3 CASCADE;
+-- this should complain "does not exist"
DROP VIEW atestv4;
+DROP TABLE atest1;
+DROP TABLE atest2;
+DROP TABLE atest3;
+
DROP GROUP regressgroup1;
DROP GROUP regressgroup2;
diff --git a/src/test/regress/sql/rangefuncs.sql b/src/test/regress/sql/rangefuncs.sql
index 75899df1f7d..0ace80e5d4d 100644
--- a/src/test/regress/sql/rangefuncs.sql
+++ b/src/test/regress/sql/rangefuncs.sql
@@ -32,58 +32,58 @@ CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1);
SELECT * FROM vw_getfoo;
-- sql, proretset = t, prorettype = b
+DROP VIEW vw_getfoo;
DROP FUNCTION getfoo(int);
CREATE FUNCTION getfoo(int) RETURNS setof int AS 'SELECT fooid FROM foo WHERE fooid = $1;' LANGUAGE SQL;
SELECT * FROM getfoo(1) AS t1;
-DROP VIEW vw_getfoo;
CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1);
SELECT * FROM vw_getfoo;
-- sql, proretset = t, prorettype = b
+DROP VIEW vw_getfoo;
DROP FUNCTION getfoo(int);
CREATE FUNCTION getfoo(int) RETURNS setof text AS 'SELECT fooname FROM foo WHERE fooid = $1;' LANGUAGE SQL;
SELECT * FROM getfoo(1) AS t1;
-DROP VIEW vw_getfoo;
CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1);
SELECT * FROM vw_getfoo;
-- sql, proretset = f, prorettype = c
+DROP VIEW vw_getfoo;
DROP FUNCTION getfoo(int);
CREATE FUNCTION getfoo(int) RETURNS foo AS 'SELECT * FROM foo WHERE fooid = $1;' LANGUAGE SQL;
SELECT * FROM getfoo(1) AS t1;
-DROP VIEW vw_getfoo;
CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1);
SELECT * FROM vw_getfoo;
-- sql, proretset = t, prorettype = c
+DROP VIEW vw_getfoo;
DROP FUNCTION getfoo(int);
CREATE FUNCTION getfoo(int) RETURNS setof foo AS 'SELECT * FROM foo WHERE fooid = $1;' LANGUAGE SQL;
SELECT * FROM getfoo(1) AS t1;
-DROP VIEW vw_getfoo;
CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1);
SELECT * FROM vw_getfoo;
-- plpgsql, proretset = f, prorettype = b
+DROP VIEW vw_getfoo;
DROP FUNCTION getfoo(int);
CREATE FUNCTION getfoo(int) RETURNS int AS 'DECLARE fooint int; BEGIN SELECT fooid into fooint FROM foo WHERE fooid = $1; RETURN fooint; END;' LANGUAGE 'plpgsql';
SELECT * FROM getfoo(1) AS t1;
-DROP VIEW vw_getfoo;
CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1);
SELECT * FROM vw_getfoo;
-- plpgsql, proretset = f, prorettype = c
+DROP VIEW vw_getfoo;
DROP FUNCTION getfoo(int);
CREATE FUNCTION getfoo(int) RETURNS foo AS 'DECLARE footup foo%ROWTYPE; BEGIN SELECT * into footup FROM foo WHERE fooid = $1; RETURN footup; END;' LANGUAGE 'plpgsql';
SELECT * FROM getfoo(1) AS t1;
-DROP VIEW vw_getfoo;
CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1);
SELECT * FROM vw_getfoo;
-DROP TABLE foo2;
+DROP VIEW vw_getfoo;
+DROP FUNCTION getfoo(int);
DROP FUNCTION foot(int);
+DROP TABLE foo2;
DROP TABLE foo;
-DROP FUNCTION getfoo(int);
-DROP VIEW vw_getfoo;
-- Rescan tests --
CREATE TABLE foorescan (fooid int, foosubid int, fooname text, primary key(fooid,foosubid));
@@ -172,10 +172,10 @@ SELECT * FROM fooview1 AS fv WHERE fv.fooid = 5004;
CREATE VIEW fooview2 AS SELECT b.fooid, max(f.foosubid) AS maxsubid FROM barrescan b, foorescan f WHERE f.fooid = b.fooid AND b.fooid IN (SELECT fooid FROM foorescan(b.fooid)) GROUP BY b.fooid ORDER BY 1,2;
SELECT * FROM fooview2 AS fv WHERE fv.maxsubid = 5;
-DROP TABLE foorescan;
-DROP FUNCTION foorescan(int,int);
DROP VIEW vw_foorescan;
-DROP TABLE barrescan;
-DROP FUNCTION foorescan(int);
DROP VIEW fooview1;
DROP VIEW fooview2;
+DROP FUNCTION foorescan(int,int);
+DROP FUNCTION foorescan(int);
+DROP TABLE foorescan;
+DROP TABLE barrescan;