aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2002-07-12 18:43:19 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2002-07-12 18:43:19 +0000
commit7c6df91dda27accab3097390ef0d21d93028c7a1 (patch)
tree5705b975e8de4edf82252e6df28e0bd57c83cb95 /src
parent791a40f943e2a9353c5823fb4f2bd446ec623d38 (diff)
downloadpostgresql-7c6df91dda27accab3097390ef0d21d93028c7a1.tar.gz
postgresql-7c6df91dda27accab3097390ef0d21d93028c7a1.zip
Second phase of committing Rod Taylor's pg_depend/pg_constraint patch.
pg_relcheck is gone; CHECK, UNIQUE, PRIMARY KEY, and FOREIGN KEY constraints all have real live entries in pg_constraint. pg_depend exists, and RESTRICT/CASCADE options work on most kinds of DROP; however, pg_depend is not yet very well populated with dependencies. (Most of the ones that are present at this point just replace formerly hardwired associations, such as the implicit drop of a relation's pg_type entry when the relation is dropped.) Need to add more logic to create dependency entries, improve pg_dump to dump constraints in place of indexes and triggers, and add some regression tests.
Diffstat (limited to 'src')
-rw-r--r--src/backend/bootstrap/bootparse.y8
-rw-r--r--src/backend/catalog/Makefile12
-rw-r--r--src/backend/catalog/dependency.c731
-rw-r--r--src/backend/catalog/heap.c610
-rw-r--r--src/backend/catalog/index.c108
-rw-r--r--src/backend/catalog/indexing.c8
-rw-r--r--src/backend/catalog/namespace.c229
-rw-r--r--src/backend/catalog/pg_constraint.c453
-rw-r--r--src/backend/catalog/pg_depend.c147
-rw-r--r--src/backend/catalog/pg_type.c76
-rw-r--r--src/backend/commands/aggregatecmds.c51
-rw-r--r--src/backend/commands/cluster.c14
-rw-r--r--src/backend/commands/comment.c41
-rw-r--r--src/backend/commands/dbcommands.c11
-rw-r--r--src/backend/commands/functioncmds.c82
-rw-r--r--src/backend/commands/indexcmds.c13
-rw-r--r--src/backend/commands/operatorcmds.c48
-rw-r--r--src/backend/commands/proclang.c46
-rw-r--r--src/backend/commands/tablecmds.c703
-rw-r--r--src/backend/commands/trigger.c328
-rw-r--r--src/backend/commands/typecmds.c117
-rw-r--r--src/backend/commands/view.c18
-rw-r--r--src/backend/nodes/copyfuncs.c10
-rw-r--r--src/backend/nodes/equalfuncs.c12
-rw-r--r--src/backend/nodes/outfuncs.c18
-rw-r--r--src/backend/parser/analyze.c232
-rw-r--r--src/backend/parser/gram.y70
-rw-r--r--src/backend/rewrite/rewriteDefine.c22
-rw-r--r--src/backend/rewrite/rewriteRemove.c148
-rw-r--r--src/backend/rewrite/rewriteSupport.c37
-rw-r--r--src/backend/tcop/utility.c23
-rw-r--r--src/backend/utils/cache/lsyscache.c25
-rw-r--r--src/backend/utils/cache/relcache.c68
-rw-r--r--src/bin/initdb/initdb.sh4
-rw-r--r--src/bin/pg_dump/pg_dump.c39
-rw-r--r--src/bin/psql/describe.c21
-rw-r--r--src/include/access/tupdesc.h4
-rw-r--r--src/include/catalog/catname.h5
-rw-r--r--src/include/catalog/catversion.h4
-rw-r--r--src/include/catalog/dependency.h92
-rw-r--r--src/include/catalog/heap.h7
-rw-r--r--src/include/catalog/index.h3
-rw-r--r--src/include/catalog/indexing.h24
-rw-r--r--src/include/catalog/pg_constraint.h172
-rw-r--r--src/include/catalog/pg_depend.h93
-rw-r--r--src/include/catalog/pg_relcheck.h55
-rw-r--r--src/include/commands/comment.h2
-rw-r--r--src/include/commands/defrem.h10
-rw-r--r--src/include/commands/proclang.h1
-rw-r--r--src/include/commands/trigger.h10
-rw-r--r--src/include/nodes/parsenodes.h41
-rw-r--r--src/include/rewrite/rewriteRemove.h10
-rw-r--r--src/include/utils/lsyscache.h3
-rw-r--r--src/test/regress/expected/alter_table.out16
-rw-r--r--src/test/regress/expected/domain.out6
-rw-r--r--src/test/regress/expected/foreign_key.out63
-rw-r--r--src/test/regress/expected/sanity_check.out5
-rw-r--r--src/test/regress/output/constraints.source4
-rw-r--r--src/test/regress/sql/alter_table.sql8
-rw-r--r--src/test/regress/sql/domain.sql5
-rw-r--r--src/test/regress/sql/foreign_key.sql8
61 files changed, 3505 insertions, 1729 deletions
diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y
index 3039f49725c..705a2b1fe80 100644
--- a/src/backend/bootstrap/bootparse.y
+++ b/src/backend/bootstrap/bootparse.y
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/bootstrap/bootparse.y,v 1.48 2002/06/20 20:29:25 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/bootstrap/bootparse.y,v 1.49 2002/07/12 18:43:13 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -240,7 +240,8 @@ Boot_DeclareIndexStmt:
DefineIndex(makeRangeVar(NULL, LexIDStr($5)),
LexIDStr($3),
LexIDStr($7),
- $9, false, false, NULL, NIL);
+ $9,
+ false, false, false, NULL, NIL);
do_end();
}
;
@@ -253,7 +254,8 @@ Boot_DeclareUniqueIndexStmt:
DefineIndex(makeRangeVar(NULL, LexIDStr($6)),
LexIDStr($4),
LexIDStr($8),
- $10, true, false, NULL, NIL);
+ $10,
+ true, false, false, NULL, NIL);
do_end();
}
;
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 0d021964b7a..22e033524ee 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -2,7 +2,7 @@
#
# Makefile for backend/catalog
#
-# $Header: /cvsroot/pgsql/src/backend/catalog/Makefile,v 1.40 2002/07/11 07:39:27 ishii Exp $
+# $Header: /cvsroot/pgsql/src/backend/catalog/Makefile,v 1.41 2002/07/12 18:43:13 tgl Exp $
#
#-------------------------------------------------------------------------
@@ -10,9 +10,9 @@ subdir = src/backend/catalog
top_builddir = ../../..
include $(top_builddir)/src/Makefile.global
-OBJS = catalog.o heap.o index.o indexing.o namespace.o aclchk.o \
- pg_aggregate.o pg_largeobject.o pg_namespace.o \
- pg_operator.o pg_proc.o pg_type.o pg_conversion.o
+OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \
+ pg_aggregate.o pg_constraint.o pg_conversion.o pg_depend.o \
+ pg_largeobject.o pg_namespace.o pg_operator.o pg_proc.o pg_type.o
BKIFILES = postgres.bki postgres.description
@@ -27,12 +27,12 @@ SUBSYS.o: $(OBJS)
POSTGRES_BKI_SRCS := $(addprefix $(top_srcdir)/src/include/catalog/,\
pg_proc.h pg_type.h pg_attribute.h pg_class.h \
- pg_attrdef.h pg_relcheck.h pg_inherits.h pg_index.h \
+ pg_attrdef.h pg_constraint.h pg_inherits.h pg_index.h \
pg_operator.h pg_opclass.h pg_am.h pg_amop.h pg_amproc.h \
pg_language.h pg_largeobject.h pg_aggregate.h pg_statistic.h \
pg_rewrite.h pg_trigger.h pg_listener.h pg_description.h \
pg_namespace.h pg_conversion.h pg_database.h pg_shadow.h pg_group.h \
- indexing.h \
+ pg_depend.h indexing.h \
)
pg_includes := $(sort -I$(top_srcdir)/src/include -I$(top_builddir)/src/include)
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
new file mode 100644
index 00000000000..b7c431d3727
--- /dev/null
+++ b/src/backend/catalog/dependency.c
@@ -0,0 +1,731 @@
+/*-------------------------------------------------------------------------
+ *
+ * dependency.c
+ * Routines to support inter-object dependencies.
+ *
+ *
+ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/catalog/dependency.c,v 1.1 2002/07/12 18:43:13 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "catalog/catname.h"
+#include "catalog/dependency.h"
+#include "catalog/heap.h"
+#include "catalog/index.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_constraint.h"
+#include "catalog/pg_depend.h"
+#include "catalog/pg_language.h"
+#include "catalog/pg_rewrite.h"
+#include "catalog/pg_trigger.h"
+#include "catalog/pg_type.h"
+#include "commands/comment.h"
+#include "commands/defrem.h"
+#include "commands/proclang.h"
+#include "commands/trigger.h"
+#include "lib/stringinfo.h"
+#include "miscadmin.h"
+#include "rewrite/rewriteRemove.h"
+#include "utils/fmgroids.h"
+#include "utils/lsyscache.h"
+#include "utils/syscache.h"
+
+
+/* This enum covers all system catalogs whose OIDs can appear in classid. */
+typedef enum ObjectClasses
+{
+ OCLASS_CLASS, /* pg_class */
+ OCLASS_PROC, /* pg_proc */
+ OCLASS_TYPE, /* pg_type */
+ OCLASS_CONSTRAINT, /* pg_constraint */
+ OCLASS_LANGUAGE, /* pg_language */
+ OCLASS_OPERATOR, /* pg_operator */
+ OCLASS_REWRITE, /* pg_rewrite */
+ OCLASS_TRIGGER /* pg_trigger */
+} ObjectClasses;
+
+static bool recursiveDeletion(const ObjectAddress *object,
+ DropBehavior behavior,
+ int recursionLevel,
+ Relation depRel);
+static void doDeletion(const ObjectAddress *object);
+static ObjectClasses getObjectClass(const ObjectAddress *object);
+static char *getObjectDescription(const ObjectAddress *object);
+
+
+/*
+ * performDeletion: attempt to drop the specified object. If CASCADE
+ * behavior is specified, also drop any dependent objects (recursively).
+ * If RESTRICT behavior is specified, error out if there are any dependent
+ * objects, except for those that should be implicitly dropped anyway
+ * according to the dependency type.
+ *
+ * This is the outer control routine for all forms of DROP that drop objects
+ * that can participate in dependencies.
+ */
+void
+performDeletion(const ObjectAddress *object,
+ DropBehavior behavior)
+{
+ 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_openr(DependRelationName, RowExclusiveLock);
+
+ if (!recursiveDeletion(object, behavior, 0, depRel))
+ elog(ERROR, "Cannot drop %s because other objects depend on it"
+ "\n\tUse DROP ... CASCADE to drop the dependent objects too",
+ objDescription);
+
+ heap_close(depRel, RowExclusiveLock);
+
+ pfree(objDescription);
+}
+
+
+/*
+ * 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.
+ *
+ * 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.
+ */
+static bool
+recursiveDeletion(const ObjectAddress *object,
+ DropBehavior behavior,
+ int recursionLevel,
+ Relation depRel)
+{
+ bool ok = true;
+ char *objDescription;
+ ScanKeyData key[3];
+ int nkeys;
+ SysScanDesc scan;
+ HeapTuple tup;
+ ObjectAddress otherObject;
+
+ /*
+ * Get object description for possible use in messages. Must do this
+ * before deleting it ...
+ */
+ objDescription = getObjectDescription(object);
+
+ /*
+ * 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.
+ *
+ * When dropping a whole object (subId = 0), remove all pg_depend
+ * records for its sub-objects too.
+ */
+ ScanKeyEntryInitialize(&key[0], 0x0,
+ Anum_pg_depend_classid, F_OIDEQ,
+ ObjectIdGetDatum(object->classId));
+ ScanKeyEntryInitialize(&key[1], 0x0,
+ Anum_pg_depend_objid, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+ if (object->objectSubId != 0)
+ {
+ ScanKeyEntryInitialize(&key[2], 0x0,
+ Anum_pg_depend_objsubid, F_INT4EQ,
+ Int32GetDatum(object->objectSubId));
+ nkeys = 3;
+ }
+ else
+ nkeys = 2;
+
+ scan = systable_beginscan(depRel, DependDependerIndex, true,
+ SnapshotNow, nkeys, key);
+
+ while (HeapTupleIsValid(tup = systable_getnext(scan)))
+ {
+ Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(tup);
+
+ otherObject.classId = foundDep->refclassid;
+ otherObject.objectId = foundDep->refobjid;
+ otherObject.objectSubId = foundDep->refobjsubid;
+
+ switch (foundDep->deptype)
+ {
+ case DEPENDENCY_NORMAL:
+ case DEPENDENCY_AUTO:
+ /* no problem */
+ 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.)
+ */
+ if (recursionLevel == 0)
+ elog(ERROR, "Cannot drop %s because %s requires it"
+ "\n\tYou may DROP the other object instead",
+ objDescription,
+ getObjectDescription(&otherObject));
+ break;
+ case DEPENDENCY_PIN:
+ /*
+ * Should not happen; PIN dependencies should have zeroes
+ * in the depender fields...
+ */
+ elog(ERROR, "recursiveDeletion: incorrect use of PIN dependency with %s",
+ objDescription);
+ break;
+ default:
+ elog(ERROR, "recursiveDeletion: unknown dependency type '%c' for %s",
+ foundDep->deptype, objDescription);
+ break;
+ }
+
+ simple_heap_delete(depRel, &tup->t_self);
+ }
+
+ systable_endscan(scan);
+
+ /*
+ * CommandCounterIncrement here to ensure that preceding changes
+ * are all visible; in particular, that the above deletions of pg_depend
+ * entries are visible. That prevents infinite recursion in case of
+ * a dependency loop (which is perfectly legal).
+ */
+ CommandCounterIncrement();
+
+ /*
+ * 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
+ * will do that.) Note it's important to delete the dependent objects
+ * before the referenced one, since the deletion routines might do
+ * things like try to update the pg_class record when deleting a
+ * check constraint.
+ *
+ * Again, when dropping a whole object (subId = 0), find pg_depend
+ * records for its sub-objects too.
+ *
+ * NOTE: because we are using SnapshotNow, if a recursive call deletes
+ * any pg_depend tuples that our scan hasn't yet visited, we will not see
+ * them as good when we do visit them. This is essential for correct
+ * behavior if there are multiple dependency paths between two objects
+ * --- else we might try to delete an already-deleted object.
+ */
+ ScanKeyEntryInitialize(&key[0], 0x0,
+ Anum_pg_depend_refclassid, F_OIDEQ,
+ ObjectIdGetDatum(object->classId));
+ ScanKeyEntryInitialize(&key[1], 0x0,
+ Anum_pg_depend_refobjid, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+ if (object->objectSubId != 0)
+ {
+ ScanKeyEntryInitialize(&key[2], 0x0,
+ Anum_pg_depend_refobjsubid, F_INT4EQ,
+ Int32GetDatum(object->objectSubId));
+ nkeys = 3;
+ }
+ else
+ nkeys = 2;
+
+ scan = systable_beginscan(depRel, DependReferenceIndex, true,
+ SnapshotNow, nkeys, key);
+
+ while (HeapTupleIsValid(tup = systable_getnext(scan)))
+ {
+ Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(tup);
+
+ otherObject.classId = foundDep->classid;
+ otherObject.objectId = foundDep->objid;
+ otherObject.objectSubId = foundDep->objsubid;
+
+ switch (foundDep->deptype)
+ {
+ case DEPENDENCY_NORMAL:
+ if (behavior == DROP_RESTRICT)
+ {
+ elog(NOTICE, "%s depends on %s",
+ getObjectDescription(&otherObject),
+ objDescription);
+ ok = false;
+ }
+ else
+ elog(NOTICE, "Drop cascades to %s",
+ getObjectDescription(&otherObject));
+
+ if (!recursiveDeletion(&otherObject, behavior,
+ recursionLevel + 1, depRel))
+ ok = false;
+ break;
+ case DEPENDENCY_AUTO:
+ case DEPENDENCY_INTERNAL:
+ /*
+ * We propagate the DROP without complaint even in the
+ * RESTRICT case. (However, normal dependencies on the
+ * component object could still cause failure.)
+ */
+ elog(DEBUG1, "Drop internally cascades to %s",
+ getObjectDescription(&otherObject));
+
+ if (!recursiveDeletion(&otherObject, behavior,
+ recursionLevel + 1, depRel))
+ ok = false;
+ break;
+ case DEPENDENCY_PIN:
+ /*
+ * For a PIN dependency we just elog immediately; there
+ * won't be any others to report.
+ */
+ elog(ERROR, "Cannot drop %s because it is required by the database system",
+ objDescription);
+ break;
+ default:
+ elog(ERROR, "recursiveDeletion: unknown dependency type '%c' for %s",
+ foundDep->deptype, objDescription);
+ break;
+ }
+ }
+
+ systable_endscan(scan);
+
+ /*
+ * We do not need CommandCounterIncrement here, since if step 2 did
+ * anything then each recursive call will have ended with one.
+ */
+
+ /*
+ * Step 3: delete the object itself.
+ */
+ doDeletion(object);
+
+ /*
+ * Delete any comments associated with this object. (This is a convenient
+ * place to do it instead of having every object type know to do it.)
+ */
+ DeleteComments(object->objectId, object->classId, object->objectSubId);
+
+ /*
+ * CommandCounterIncrement here to ensure that preceding changes
+ * are all visible.
+ */
+ CommandCounterIncrement();
+
+ /*
+ * And we're done!
+ */
+ pfree(objDescription);
+
+ return ok;
+}
+
+
+/*
+ * doDeletion: actually delete a single object
+ */
+static void
+doDeletion(const ObjectAddress *object)
+{
+ switch (getObjectClass(object))
+ {
+ case OCLASS_CLASS:
+ {
+ HeapTuple relTup;
+ char relKind;
+
+ /*
+ * Need the relkind to figure out how to drop.
+ */
+ relTup = SearchSysCache(RELOID,
+ ObjectIdGetDatum(object->objectId),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(relTup))
+ elog(ERROR, "doDeletion: Relation %u does not exist",
+ object->objectId);
+ relKind = ((Form_pg_class) GETSTRUCT(relTup))->relkind;
+ ReleaseSysCache(relTup);
+
+ if (relKind == RELKIND_INDEX)
+ {
+ Assert(object->objectSubId == 0);
+ index_drop(object->objectId);
+ }
+ else
+ {
+ if (object->objectSubId != 0)
+ elog(ERROR, "DROP COLUMN not implemented yet");
+ else
+ heap_drop_with_catalog(object->objectId);
+ }
+ break;
+ }
+
+ case OCLASS_PROC:
+ RemoveFunctionById(object->objectId);
+ break;
+
+ case OCLASS_TYPE:
+ RemoveTypeById(object->objectId);
+ break;
+
+ case OCLASS_CONSTRAINT:
+ RemoveConstraintById(object->objectId);
+ break;
+
+ case OCLASS_LANGUAGE:
+ DropProceduralLanguageById(object->objectId);
+ break;
+
+ case OCLASS_OPERATOR:
+ RemoveOperatorById(object->objectId);
+ break;
+
+ case OCLASS_REWRITE:
+ RemoveRewriteRuleById(object->objectId);
+ break;
+
+ case OCLASS_TRIGGER:
+ RemoveTriggerById(object->objectId);
+ break;
+
+ default:
+ elog(ERROR, "doDeletion: Unsupported object class %u",
+ object->classId);
+ }
+}
+
+/*
+ * Determine the class of a given object identified by objectAddress.
+ *
+ * This function is needed just because some of the system catalogs do
+ * not have hardwired-at-compile-time OIDs.
+ */
+static ObjectClasses
+getObjectClass(const ObjectAddress *object)
+{
+ static bool reloids_initialized = false;
+ static Oid reloid_pg_constraint;
+ 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)
+ {
+ case RelOid_pg_class:
+ /* caller must check objectSubId */
+ return OCLASS_CLASS;
+
+ case RelOid_pg_proc:
+ Assert(object->objectSubId == 0);
+ return OCLASS_PROC;
+
+ case RelOid_pg_type:
+ Assert(object->objectSubId == 0);
+ return OCLASS_TYPE;
+ }
+
+ /*
+ * 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_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->classId == reloid_pg_constraint)
+ {
+ Assert(object->objectSubId == 0);
+ return OCLASS_CONSTRAINT;
+ }
+ if (object->classId == reloid_pg_language)
+ {
+ Assert(object->objectSubId == 0);
+ return OCLASS_LANGUAGE;
+ }
+ if (object->classId == reloid_pg_operator)
+ {
+ Assert(object->objectSubId == 0);
+ return OCLASS_OPERATOR;
+ }
+ if (object->classId == reloid_pg_rewrite)
+ {
+ Assert(object->objectSubId == 0);
+ return OCLASS_REWRITE;
+ }
+ if (object->classId == reloid_pg_trigger)
+ {
+ Assert(object->objectSubId == 0);
+ return OCLASS_TRIGGER;
+ }
+
+ elog(ERROR, "getObjectClass: Unknown object class %u",
+ object->classId);
+ return OCLASS_CLASS; /* keep compiler quiet */
+}
+
+/*
+ * getObjectDescription: build an object description for messages
+ *
+ * The result is a palloc'd string.
+ */
+static char *
+getObjectDescription(const ObjectAddress *object)
+{
+ StringInfoData buffer;
+
+ initStringInfo(&buffer);
+
+ switch (getObjectClass(object))
+ {
+ case OCLASS_CLASS:
+ {
+ HeapTuple relTup;
+ Form_pg_class relForm;
+
+ relTup = SearchSysCache(RELOID,
+ ObjectIdGetDatum(object->objectId),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(relTup))
+ elog(ERROR, "getObjectDescription: Relation %u does not exist",
+ object->objectId);
+ relForm = (Form_pg_class) GETSTRUCT(relTup);
+
+ switch (relForm->relkind)
+ {
+ case RELKIND_RELATION:
+ appendStringInfo(&buffer, "table %s",
+ NameStr(relForm->relname));
+ break;
+ case RELKIND_INDEX:
+ appendStringInfo(&buffer, "index %s",
+ NameStr(relForm->relname));
+ break;
+ case RELKIND_SPECIAL:
+ appendStringInfo(&buffer, "special system relation %s",
+ NameStr(relForm->relname));
+ break;
+ case RELKIND_SEQUENCE:
+ appendStringInfo(&buffer, "sequence %s",
+ NameStr(relForm->relname));
+ break;
+ case RELKIND_UNCATALOGED:
+ appendStringInfo(&buffer, "uncataloged table %s",
+ NameStr(relForm->relname));
+ break;
+ case RELKIND_TOASTVALUE:
+ appendStringInfo(&buffer, "toast table %s",
+ NameStr(relForm->relname));
+ break;
+ case RELKIND_VIEW:
+ appendStringInfo(&buffer, "view %s",
+ NameStr(relForm->relname));
+ break;
+ default:
+ /* shouldn't get here */
+ appendStringInfo(&buffer, "relation %s",
+ NameStr(relForm->relname));
+ break;
+ }
+
+ if (object->objectSubId != 0)
+ appendStringInfo(&buffer, " column %s",
+ get_attname(object->objectId,
+ object->objectSubId));
+
+ ReleaseSysCache(relTup);
+ break;
+ }
+
+ case OCLASS_PROC:
+ /* XXX could improve on this */
+ appendStringInfo(&buffer, "function %s",
+ get_func_name(object->objectId));
+ break;
+
+ case OCLASS_TYPE:
+ {
+ HeapTuple typeTup;
+
+ typeTup = SearchSysCache(TYPEOID,
+ ObjectIdGetDatum(object->objectId),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(typeTup))
+ elog(ERROR, "getObjectDescription: Type %u does not exist",
+ object->objectId);
+ appendStringInfo(&buffer, "type %s",
+ NameStr(((Form_pg_type) GETSTRUCT(typeTup))->typname));
+ ReleaseSysCache(typeTup);
+ break;
+ }
+
+ case OCLASS_CONSTRAINT:
+ {
+ Relation conDesc;
+ ScanKeyData skey[1];
+ SysScanDesc rcscan;
+ HeapTuple tup;
+ Form_pg_constraint con;
+
+ conDesc = heap_openr(ConstraintRelationName, AccessShareLock);
+
+ ScanKeyEntryInitialize(&skey[0], 0x0,
+ ObjectIdAttributeNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ rcscan = systable_beginscan(conDesc, ConstraintOidIndex, true,
+ SnapshotNow, 1, skey);
+
+ tup = systable_getnext(rcscan);
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "getObjectDescription: Constraint %u does not exist",
+ object->objectId);
+
+ con = (Form_pg_constraint) GETSTRUCT(tup);
+
+ appendStringInfo(&buffer, "constraint %s",
+ NameStr(con->conname));
+ if (OidIsValid(con->conrelid))
+ appendStringInfo(&buffer, " on table %s",
+ get_rel_name(con->conrelid));
+
+ systable_endscan(rcscan);
+ heap_close(conDesc, AccessShareLock);
+ break;
+ }
+
+ case OCLASS_LANGUAGE:
+ {
+ HeapTuple langTup;
+
+ langTup = SearchSysCache(LANGOID,
+ ObjectIdGetDatum(object->objectId),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(langTup))
+ elog(ERROR, "getObjectDescription: Language %u does not exist",
+ object->objectId);
+ appendStringInfo(&buffer, "language %s",
+ NameStr(((Form_pg_language) GETSTRUCT(langTup))->lanname));
+ ReleaseSysCache(langTup);
+ break;
+ }
+
+ case OCLASS_OPERATOR:
+ /* XXX could improve on this */
+ appendStringInfo(&buffer, "operator %s",
+ get_opname(object->objectId));
+ break;
+
+ case OCLASS_REWRITE:
+ {
+ Relation ruleDesc;
+ ScanKeyData skey[1];
+ SysScanDesc rcscan;
+ HeapTuple tup;
+ Form_pg_rewrite rule;
+
+ ruleDesc = heap_openr(RewriteRelationName, AccessShareLock);
+
+ ScanKeyEntryInitialize(&skey[0], 0x0,
+ ObjectIdAttributeNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ rcscan = systable_beginscan(ruleDesc, RewriteOidIndex, true,
+ SnapshotNow, 1, skey);
+
+ tup = systable_getnext(rcscan);
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "getObjectDescription: Rule %u does not exist",
+ object->objectId);
+
+ rule = (Form_pg_rewrite) GETSTRUCT(tup);
+
+ appendStringInfo(&buffer, "rule %s",
+ NameStr(rule->rulename));
+ if (OidIsValid(rule->ev_class))
+ appendStringInfo(&buffer, " on table %s",
+ get_rel_name(rule->ev_class));
+
+ systable_endscan(rcscan);
+ heap_close(ruleDesc, AccessShareLock);
+ break;
+ }
+
+ case OCLASS_TRIGGER:
+ {
+ Relation trigDesc;
+ ScanKeyData skey[1];
+ SysScanDesc tgscan;
+ HeapTuple tup;
+ Form_pg_trigger trig;
+
+ trigDesc = heap_openr(TriggerRelationName, AccessShareLock);
+
+ ScanKeyEntryInitialize(&skey[0], 0x0,
+ ObjectIdAttributeNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ tgscan = systable_beginscan(trigDesc, TriggerOidIndex, true,
+ SnapshotNow, 1, skey);
+
+ tup = systable_getnext(tgscan);
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "getObjectDescription: Trigger %u does not exist",
+ object->objectId);
+
+ trig = (Form_pg_trigger) GETSTRUCT(tup);
+
+ appendStringInfo(&buffer, "trigger %s",
+ NameStr(trig->tgname));
+ if (OidIsValid(trig->tgrelid))
+ appendStringInfo(&buffer, " on table %s",
+ get_rel_name(trig->tgrelid));
+
+ systable_endscan(tgscan);
+ heap_close(trigDesc, AccessShareLock);
+ break;
+ }
+
+ default:
+ appendStringInfo(&buffer, "unknown object %u %u %d",
+ object->classId,
+ object->objectId,
+ object->objectSubId);
+ break;
+ }
+
+ return buffer.data;
+}
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 81fbee1fa58..207090ae70a 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.204 2002/06/20 20:29:26 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.205 2002/07/12 18:43:13 tgl Exp $
*
*
* INTERFACE ROUTINES
@@ -33,21 +33,20 @@
#include "access/genam.h"
#include "catalog/catalog.h"
#include "catalog/catname.h"
+#include "catalog/dependency.h"
#include "catalog/heap.h"
#include "catalog/index.h"
#include "catalog/indexing.h"
#include "catalog/pg_attrdef.h"
+#include "catalog/pg_constraint.h"
#include "catalog/pg_inherits.h"
-#include "catalog/pg_relcheck.h"
#include "catalog/pg_statistic.h"
#include "catalog/pg_type.h"
-#include "commands/comment.h"
#include "commands/trigger.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/planmain.h"
-#include "optimizer/prep.h"
#include "optimizer/var.h"
#include "parser/parse_coerce.h"
#include "parser/parse_expr.h"
@@ -69,8 +68,6 @@ static void AddNewRelationTuple(Relation pg_class_desc,
char relkind, bool relhasoids);
static void DeleteAttributeTuples(Relation rel);
static void DeleteRelationTuple(Relation rel);
-static void DeleteTypeTuple(Relation rel);
-static void RelationRemoveIndexes(Relation relation);
static void RelationRemoveInheritance(Relation relation);
static void AddNewRelationType(const char *typeName,
Oid typeNamespace,
@@ -80,7 +77,7 @@ static void StoreAttrDefault(Relation rel, AttrNumber attnum, char *adbin);
static void StoreRelCheck(Relation rel, char *ccname, char *ccbin);
static void StoreConstraints(Relation rel, TupleDesc tupdesc);
static void SetRelationNumChecks(Relation rel, int numchecks);
-static void RemoveConstraints(Relation rel);
+static void RemoveDefaults(Relation rel);
static void RemoveStatistics(Relation rel);
@@ -760,106 +757,42 @@ heap_create_with_catalog(const char *relname,
}
-/* --------------------------------
+/*
* RelationRemoveInheritance
*
- * Note: for now, we cause an exception if relation is a
- * superclass. Someday, we may want to allow this and merge
- * the type info into subclass procedures.... this seems like
- * lots of work.
- * --------------------------------
+ * Formerly, this routine checked for child relations and aborted the
+ * deletion if any were found. Now we rely on the dependency mechanism
+ * to check for or delete child relations. By the time we get here,
+ * there are no children and we need only remove the pg_inherits rows.
*/
static void
RelationRemoveInheritance(Relation relation)
{
Relation catalogRelation;
HeapTuple tuple;
- HeapScanDesc scan;
+ SysScanDesc scan;
ScanKeyData entry;
- bool found = false;
- /*
- * open pg_inherits
- */
catalogRelation = heap_openr(InheritsRelationName, RowExclusiveLock);
- /*
- * form a scan key for the subclasses of this class and begin scanning
- */
- ScanKeyEntryInitialize(&entry, 0x0, Anum_pg_inherits_inhparent,
- F_OIDEQ,
+ ScanKeyEntryInitialize(&entry, 0x0,
+ Anum_pg_inherits_inhrelid, F_OIDEQ,
ObjectIdGetDatum(RelationGetRelid(relation)));
- scan = heap_beginscan(catalogRelation,
- SnapshotNow,
- 1,
- &entry);
-
- /*
- * if any subclasses exist, then we disallow the deletion.
- */
- if ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
- {
- Oid subclass = ((Form_pg_inherits) GETSTRUCT(tuple))->inhrelid;
- char *subclassname;
-
- subclassname = get_rel_name(subclass);
- /* Just in case get_rel_name fails... */
- if (subclassname)
- elog(ERROR, "Relation \"%s\" inherits from \"%s\"",
- subclassname, RelationGetRelationName(relation));
- else
- elog(ERROR, "Relation %u inherits from \"%s\"",
- subclass, RelationGetRelationName(relation));
- }
- heap_endscan(scan);
-
- /*
- * If we get here, it means the relation has no subclasses so we can
- * trash it. First we remove dead INHERITS tuples.
- */
- entry.sk_attno = Anum_pg_inherits_inhrelid;
-
- scan = heap_beginscan(catalogRelation,
- SnapshotNow,
- 1,
- &entry);
+ scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndex, true,
+ SnapshotNow, 1, &entry);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ while (HeapTupleIsValid(tuple = systable_getnext(scan)))
{
simple_heap_delete(catalogRelation, &tuple->t_self);
- found = true;
}
- heap_endscan(scan);
+ systable_endscan(scan);
heap_close(catalogRelation, RowExclusiveLock);
}
/*
- * RelationRemoveIndexes
- */
-static void
-RelationRemoveIndexes(Relation relation)
-{
- List *indexoidlist,
- *indexoidscan;
-
- indexoidlist = RelationGetIndexList(relation);
-
- foreach(indexoidscan, indexoidlist)
- {
- Oid indexoid = lfirsti(indexoidscan);
-
- index_drop(indexoid);
- }
-
- freeList(indexoidlist);
-}
-
-/* --------------------------------
* DeleteRelationTuple
- *
- * --------------------------------
*/
static void
DeleteRelationTuple(Relation rel)
@@ -1049,163 +982,34 @@ DeleteAttributeTuples(Relation rel)
heap_close(pg_attribute_desc, RowExclusiveLock);
}
-/* --------------------------------
- * DeleteTypeTuple
- *
- * If the user attempts to destroy a relation and there
- * exists attributes in other relations of type
- * "relation we are deleting", then we have to do something
- * special. presently we disallow the destroy.
- * --------------------------------
- */
-static void
-DeleteTypeTuple(Relation rel)
-{
- Relation pg_type_desc;
- HeapScanDesc pg_type_scan;
- Relation pg_attribute_desc;
- HeapScanDesc pg_attribute_scan;
- ScanKeyData key;
- ScanKeyData attkey;
- HeapTuple tup;
- HeapTuple atttup;
- Oid typoid;
-
- /*
- * open pg_type
- */
- pg_type_desc = heap_openr(TypeRelationName, RowExclusiveLock);
-
- /*
- * create a scan key to locate the type tuple corresponding to this
- * relation.
- */
- ScanKeyEntryInitialize(&key, 0,
- Anum_pg_type_typrelid,
- F_OIDEQ,
- ObjectIdGetDatum(RelationGetRelid(rel)));
-
- pg_type_scan = heap_beginscan(pg_type_desc,
- SnapshotNow,
- 1,
- &key);
-
- /*
- * use heap_getnext() to fetch the pg_type tuple. If this tuple is
- * not valid then something's wrong.
- */
- tup = heap_getnext(pg_type_scan, ForwardScanDirection);
-
- if (!HeapTupleIsValid(tup))
- {
- heap_endscan(pg_type_scan);
- heap_close(pg_type_desc, RowExclusiveLock);
- elog(ERROR, "DeleteTypeTuple: type \"%s\" does not exist",
- RelationGetRelationName(rel));
- }
-
- /*
- * now scan pg_attribute. if any other relations have attributes of
- * the type of the relation we are deleteing then we have to disallow
- * the deletion. should talk to stonebraker about this. -cim 6/19/90
- */
- typoid = tup->t_data->t_oid;
-
- pg_attribute_desc = heap_openr(AttributeRelationName, RowExclusiveLock);
-
- ScanKeyEntryInitialize(&attkey,
- 0,
- Anum_pg_attribute_atttypid,
- F_OIDEQ,
- ObjectIdGetDatum(typoid));
-
- pg_attribute_scan = heap_beginscan(pg_attribute_desc,
- SnapshotNow,
- 1,
- &attkey);
-
- /*
- * try and get a pg_attribute tuple. if we succeed it means we can't
- * delete the relation because something depends on the schema.
- */
- atttup = heap_getnext(pg_attribute_scan, ForwardScanDirection);
-
- if (HeapTupleIsValid(atttup))
- {
- Oid relid = ((Form_pg_attribute) GETSTRUCT(atttup))->attrelid;
-
- heap_endscan(pg_attribute_scan);
- heap_close(pg_attribute_desc, RowExclusiveLock);
- heap_endscan(pg_type_scan);
- heap_close(pg_type_desc, RowExclusiveLock);
-
- elog(ERROR, "DeleteTypeTuple: column of type %s exists in relation %u",
- RelationGetRelationName(rel), relid);
- }
- heap_endscan(pg_attribute_scan);
- heap_close(pg_attribute_desc, RowExclusiveLock);
-
- /*
- * Ok, it's safe so we delete the relation tuple from pg_type and
- * finish up.
- */
- simple_heap_delete(pg_type_desc, &tup->t_self);
-
- heap_endscan(pg_type_scan);
- heap_close(pg_type_desc, RowExclusiveLock);
-}
-
/* ----------------------------------------------------------------
- * heap_drop_with_catalog - removes all record of named relation from catalogs
+ * heap_drop_with_catalog - removes specified relation from catalogs
*
- * 1) open relation, check for existence, etc.
- * 2) remove inheritance information
- * 3) remove indexes
- * 4) remove pg_class tuple
- * 5) remove pg_attribute tuples and related descriptions
- * 6) remove pg_description tuples
- * 7) remove pg_type tuples
- * 8) RemoveConstraints ()
- * 9) unlink relation
+ * 1) open relation, acquire exclusive lock.
+ * 2) flush relation buffers from bufmgr
+ * 3) remove inheritance information
+ * 4) remove pg_statistic tuples
+ * 5) remove pg_attribute tuples and related items
+ * 6) remove pg_class tuple
+ * 7) unlink relation file
*
- * old comments
- * Except for vital relations, removes relation from
- * relation catalog, and related attributes from
- * attribute catalog (needed?). (Anything else?)
- *
- * get proper relation from relation catalog (if not arg)
- * scan attribute catalog deleting attributes of reldesc
- * (necessary?)
- * delete relation from relation catalog
- * (How are the tuples of the relation discarded?)
- *
- * XXX Must fix to work with indexes.
- * There may be a better order for doing things.
- * Problems with destroying a deleted database--cannot create
- * a struct reldesc without having an open file descriptor.
+ * Note that this routine is not responsible for dropping objects that are
+ * linked to the pg_class entry via dependencies (for example, indexes and
+ * constraints). Those are deleted by the dependency-tracing logic in
+ * dependency.c before control gets here. In general, therefore, this routine
+ * should never be called directly; go through performDeletion() instead.
* ----------------------------------------------------------------
*/
void
-heap_drop_with_catalog(Oid rid,
- bool allow_system_table_mods)
+heap_drop_with_catalog(Oid rid)
{
Relation rel;
- Oid toasttableOid;
int i;
/*
* Open and lock the relation.
*/
rel = heap_open(rid, AccessExclusiveLock);
- toasttableOid = rel->rd_rel->reltoastrelid;
-
- /*
- * prevent deletion of system relations
- */
- if (!allow_system_table_mods &&
- IsSystemRelation(rel))
- elog(ERROR, "System relation \"%s\" may not be dropped",
- RelationGetRelationName(rel));
/*
* Release all buffers that belong to this relation, after writing any
@@ -1217,42 +1021,21 @@ heap_drop_with_catalog(Oid rid,
i);
/*
- * remove rules if necessary
- */
- if (rel->rd_rules != NULL)
- RelationRemoveRules(rid);
-
- /* triggers */
- RelationRemoveTriggers(rel);
-
- /*
* remove inheritance information
*/
RelationRemoveInheritance(rel);
/*
- * remove indexes if necessary
+ * delete statistics
*/
- RelationRemoveIndexes(rel);
+ RemoveStatistics(rel);
/*
- * delete attribute tuples
+ * delete attribute tuples and associated defaults
*/
DeleteAttributeTuples(rel);
- /*
- * delete comments, statistics, and constraints
- */
- DeleteComments(rid, RelOid_pg_class);
-
- RemoveStatistics(rel);
-
- RemoveConstraints(rel);
-
- /*
- * delete type tuple
- */
- DeleteTypeTuple(rel);
+ RemoveDefaults(rel);
/*
* delete relation tuple
@@ -1276,10 +1059,6 @@ heap_drop_with_catalog(Oid rid,
* flush the relation from the relcache
*/
RelationForgetRelation(rid);
-
- /* If it has a toast table, recurse to get rid of that too */
- if (OidIsValid(toasttableOid))
- heap_drop_with_catalog(toasttableOid, true);
}
@@ -1374,11 +1153,9 @@ StoreRelCheck(Relation rel, char *ccname, char *ccbin)
{
Node *expr;
char *ccsrc;
- Relation rcrel;
- Relation idescs[Num_pg_relcheck_indices];
- HeapTuple tuple;
- Datum values[4];
- static char nulls[4] = {' ', ' ', ' ', ' '};
+ List *varList;
+ int keycount;
+ int16 *attNos;
/*
* Convert condition to a normal boolean expression tree.
@@ -1394,26 +1171,55 @@ StoreRelCheck(Relation rel, char *ccname, char *ccbin)
RelationGetRelid(rel)),
false);
- values[Anum_pg_relcheck_rcrelid - 1] = RelationGetRelid(rel);
- values[Anum_pg_relcheck_rcname - 1] = DirectFunctionCall1(namein,
- CStringGetDatum(ccname));
- values[Anum_pg_relcheck_rcbin - 1] = DirectFunctionCall1(textin,
- CStringGetDatum(ccbin));
- values[Anum_pg_relcheck_rcsrc - 1] = DirectFunctionCall1(textin,
- CStringGetDatum(ccsrc));
- rcrel = heap_openr(RelCheckRelationName, RowExclusiveLock);
- tuple = heap_formtuple(rcrel->rd_att, values, nulls);
- simple_heap_insert(rcrel, tuple);
- CatalogOpenIndices(Num_pg_relcheck_indices, Name_pg_relcheck_indices,
- idescs);
- CatalogIndexInsert(idescs, Num_pg_relcheck_indices, rcrel, tuple);
- CatalogCloseIndices(Num_pg_relcheck_indices, idescs);
- heap_close(rcrel, RowExclusiveLock);
+ /*
+ * Find columns of rel that are used in ccbin
+ */
+ varList = pull_var_clause(expr, false);
+ keycount = length(varList);
+
+ if (keycount > 0)
+ {
+ List *vl;
+ int i = 0;
+
+ attNos = (int16 *) palloc(keycount * sizeof(int16));
+ foreach(vl, varList)
+ {
+ Var *var = (Var *) lfirst(vl);
+ int j;
+
+ for (j = 0; j < i; j++)
+ if (attNos[j] == var->varattno)
+ break;
+ if (j == i)
+ attNos[i++] = var->varattno;
+ }
+ keycount = i;
+ }
+ else
+ attNos = NULL;
+
+ /*
+ * Create the Check Constraint
+ */
+ CreateConstraintEntry(ccname, /* Constraint Name */
+ RelationGetNamespace(rel), /* namespace */
+ CONSTRAINT_CHECK, /* Constraint Type */
+ false, /* Is Deferrable */
+ false, /* Is Deferred */
+ RelationGetRelid(rel), /* relation */
+ attNos, /* List of attributes in the constraint */
+ keycount, /* # attributes in the constraint */
+ InvalidOid, /* not a domain constraint */
+ InvalidOid, /* Foreign key fields */
+ NULL,
+ 0,
+ ' ',
+ ' ',
+ ' ',
+ ccbin, /* Binary form check constraint */
+ ccsrc); /* Source form check constraint */
- pfree(DatumGetPointer(values[Anum_pg_relcheck_rcname - 1]));
- pfree(DatumGetPointer(values[Anum_pg_relcheck_rcbin - 1]));
- pfree(DatumGetPointer(values[Anum_pg_relcheck_rcsrc - 1]));
- heap_freetuple(tuple);
pfree(ccsrc);
}
@@ -1488,6 +1294,7 @@ AddRelationRawConstraints(Relation rel,
ParseState *pstate;
RangeTblEntry *rte;
int numchecks;
+ int constr_name_ctr = 0;
List *listptr;
Node *expr;
@@ -1549,18 +1356,17 @@ AddRelationRawConstraints(Relation rel,
/* Check name uniqueness, or generate a new name */
if (cdef->name != NULL)
{
- int i;
List *listptr2;
ccname = cdef->name;
- /* Check against old constraints */
- for (i = 0; i < numoldchecks; i++)
- {
- if (strcmp(oldchecks[i].ccname, ccname) == 0)
- elog(ERROR, "Duplicate CHECK constraint name: '%s'",
- ccname);
- }
+ /* Check against pre-existing constraints */
+ if (ConstraintNameIsUsed(RelationGetRelid(rel),
+ RelationGetNamespace(rel),
+ ccname))
+ elog(ERROR, "constraint \"%s\" already exists for relation \"%s\"",
+ ccname, RelationGetRelationName(rel));
/* Check against other new constraints */
+ /* Needed because we don't do CommandCounterIncrement in loop */
foreach(listptr2, rawConstraints)
{
Constraint *cdef2 = (Constraint *) lfirst(listptr2);
@@ -1577,55 +1383,40 @@ AddRelationRawConstraints(Relation rel,
}
else
{
- int i;
- int j;
bool success;
- List *listptr2;
- ccname = (char *) palloc(NAMEDATALEN);
-
- /* Loop until we find a non-conflicting constraint name */
- /* What happens if this loops forever? */
- j = numchecks + 1;
do
{
- success = true;
- snprintf(ccname, NAMEDATALEN, "$%d", j);
-
- /* Check against old constraints */
- for (i = 0; i < numoldchecks; i++)
- {
- if (strcmp(oldchecks[i].ccname, ccname) == 0)
- {
- success = false;
- break;
- }
- }
+ List *listptr2;
/*
- * Check against other new constraints, if the check
- * hasn't already failed
+ * Generate a name that does not conflict with pre-existing
+ * constraints, nor with any auto-generated names so far.
+ */
+ ccname = GenerateConstraintName(RelationGetRelid(rel),
+ RelationGetNamespace(rel),
+ &constr_name_ctr);
+ /*
+ * Check against other new constraints, in case the user
+ * has specified a name that looks like an auto-generated
+ * name.
*/
- if (success)
+ success = true;
+ foreach(listptr2, rawConstraints)
{
- foreach(listptr2, rawConstraints)
+ Constraint *cdef2 = (Constraint *) lfirst(listptr2);
+
+ if (cdef2 == cdef ||
+ cdef2->contype != CONSTR_CHECK ||
+ cdef2->raw_expr == NULL ||
+ cdef2->name == NULL)
+ continue;
+ if (strcmp(cdef2->name, ccname) == 0)
{
- Constraint *cdef2 = (Constraint *) lfirst(listptr2);
-
- if (cdef2 == cdef ||
- cdef2->contype != CONSTR_CHECK ||
- cdef2->raw_expr == NULL ||
- cdef2->name == NULL)
- continue;
- if (strcmp(cdef2->name, ccname) == 0)
- {
- success = false;
- break;
- }
+ success = false;
+ break;
}
}
-
- ++j;
} while (!success);
}
@@ -1852,157 +1643,74 @@ RemoveAttrDefaults(Relation rel)
heap_close(adrel, RowExclusiveLock);
}
-static void
-RemoveRelChecks(Relation rel)
-{
- Relation rcrel;
- HeapScanDesc rcscan;
- ScanKeyData key;
- HeapTuple tup;
-
- rcrel = heap_openr(RelCheckRelationName, RowExclusiveLock);
-
- ScanKeyEntryInitialize(&key, 0, Anum_pg_relcheck_rcrelid,
- F_OIDEQ,
- ObjectIdGetDatum(RelationGetRelid(rel)));
-
- rcscan = heap_beginscan(rcrel, SnapshotNow, 1, &key);
-
- while ((tup = heap_getnext(rcscan, ForwardScanDirection)) != NULL)
- simple_heap_delete(rcrel, &tup->t_self);
-
- heap_endscan(rcscan);
- heap_close(rcrel, RowExclusiveLock);
-
-}
-
/*
- * Removes all CHECK constraints on a relation that match the given name.
- * It is the responsibility of the calling function to acquire a lock on
- * the relation.
- * Returns: The number of CHECK constraints removed.
+ * Removes all constraints on a relation that match the given name.
+ *
+ * It is the responsibility of the calling function to acquire a suitable
+ * lock on the relation.
+ *
+ * Returns: The number of constraints removed.
*/
int
-RemoveCheckConstraint(Relation rel, const char *constrName, bool inh)
+RemoveRelConstraints(Relation rel, const char *constrName,
+ DropBehavior behavior)
{
- Oid relid;
- Relation rcrel;
- TupleDesc tupleDesc;
- TupleConstr *oldconstr;
- int numoldchecks;
- int numchecks;
- HeapScanDesc rcscan;
- ScanKeyData key[2];
- HeapTuple rctup;
- int rel_deleted = 0;
- int all_deleted = 0;
+ int ndeleted = 0;
+ Relation conrel;
+ SysScanDesc conscan;
+ ScanKeyData key[1];
+ HeapTuple contup;
+
+ /* Grab an appropriate lock on the pg_constraint relation */
+ conrel = heap_openr(ConstraintRelationName, RowExclusiveLock);
+
+ /* Use the index to scan only constraints of the target relation */
+ ScanKeyEntryInitialize(&key[0], 0x0,
+ Anum_pg_constraint_conrelid, F_OIDEQ,
+ ObjectIdGetDatum(RelationGetRelid(rel)));
- /* Find id of the relation */
- relid = RelationGetRelid(rel);
+ conscan = systable_beginscan(conrel, ConstraintRelidIndex, true,
+ SnapshotNow, 1, key);
/*
- * Process child tables and remove constraints of the same name.
+ * Scan over the result set, removing any matching entries.
*/
- if (inh)
+ while ((contup = systable_getnext(conscan)) != NULL)
{
- List *child,
- *children;
-
- /* This routine is actually in the planner */
- children = find_all_inheritors(relid);
+ Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(contup);
- /*
- * find_all_inheritors does the recursive search of the
- * inheritance hierarchy, so all we have to do is process all of
- * the relids in the list that it returns.
- */
- foreach(child, children)
+ if (strcmp(NameStr(con->conname), constrName) == 0)
{
- Oid childrelid = lfirsti(child);
- Relation inhrel;
-
- if (childrelid == relid)
- continue;
- inhrel = heap_open(childrelid, AccessExclusiveLock);
- all_deleted += RemoveCheckConstraint(inhrel, constrName, false);
- heap_close(inhrel, NoLock);
- }
- }
-
- /*
- * Get number of existing constraints.
- */
- tupleDesc = RelationGetDescr(rel);
- oldconstr = tupleDesc->constr;
- if (oldconstr)
- numoldchecks = oldconstr->num_check;
- else
- numoldchecks = 0;
+ ObjectAddress conobj;
- /* Grab an appropriate lock on the pg_relcheck relation */
- rcrel = heap_openr(RelCheckRelationName, RowExclusiveLock);
+ conobj.classId = RelationGetRelid(conrel);
+ conobj.objectId = contup->t_data->t_oid;
+ conobj.objectSubId = 0;
- /*
- * Create two scan keys. We need to match on the oid of the table the
- * CHECK is in and also we need to match the name of the CHECK
- * constraint.
- */
- ScanKeyEntryInitialize(&key[0], 0, Anum_pg_relcheck_rcrelid,
- F_OIDEQ,
- ObjectIdGetDatum(RelationGetRelid(rel)));
+ performDeletion(&conobj, behavior);
- ScanKeyEntryInitialize(&key[1], 0, Anum_pg_relcheck_rcname,
- F_NAMEEQ,
- PointerGetDatum(constrName));
-
- /* Begin scanning the heap */
- rcscan = heap_beginscan(rcrel, SnapshotNow, 2, key);
-
- /*
- * Scan over the result set, removing any matching entries. Note that
- * this has the side-effect of removing ALL CHECK constraints that
- * share the specified constraint name.
- */
- while ((rctup = heap_getnext(rcscan, ForwardScanDirection)) != NULL)
- {
- simple_heap_delete(rcrel, &rctup->t_self);
- ++rel_deleted;
- ++all_deleted;
+ ndeleted++;
+ }
}
/* Clean up after the scan */
- heap_endscan(rcscan);
- heap_close(rcrel, RowExclusiveLock);
+ systable_endscan(conscan);
+ heap_close(conrel, RowExclusiveLock);
- if (rel_deleted)
- {
- /*
- * Update the count of constraints in the relation's pg_class tuple.
- */
- numchecks = numoldchecks - rel_deleted;
- if (numchecks < 0)
- elog(ERROR, "check count became negative");
-
- SetRelationNumChecks(rel, numchecks);
- }
-
- /* Return the number of tuples deleted, including all children */
- return all_deleted;
+ return ndeleted;
}
static void
-RemoveConstraints(Relation rel)
+RemoveDefaults(Relation rel)
{
TupleConstr *constr = rel->rd_att->constr;
- if (!constr)
- return;
-
- if (constr->num_defval > 0)
+ /*
+ * We can skip looking at pg_attrdef if there are no defaults recorded
+ * in the Relation.
+ */
+ if (constr && constr->num_defval > 0)
RemoveAttrDefaults(rel);
-
- if (constr->num_check > 0)
- RemoveRelChecks(rel);
}
static void
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 2b6f77594c6..30cef89feff 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.181 2002/06/20 20:29:26 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.182 2002/07/12 18:43:13 tgl Exp $
*
*
* INTERFACE ROUTINES
@@ -29,14 +29,15 @@
#include "bootstrap/bootstrap.h"
#include "catalog/catalog.h"
#include "catalog/catname.h"
+#include "catalog/dependency.h"
#include "catalog/heap.h"
#include "catalog/index.h"
#include "catalog/indexing.h"
+#include "catalog/pg_constraint.h"
#include "catalog/pg_index.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
-#include "commands/comment.h"
#include "executor/executor.h"
#include "miscadmin.h"
#include "optimizer/clauses.h"
@@ -535,6 +536,7 @@ index_create(Oid heapRelationId,
Oid accessMethodObjectId,
Oid *classObjectId,
bool primary,
+ bool isconstraint,
bool allow_system_table_mods)
{
Relation heapRelation;
@@ -543,6 +545,7 @@ index_create(Oid heapRelationId,
bool shared_relation;
Oid namespaceId;
Oid indexoid;
+ int i;
SetReindexProcessing(false);
@@ -660,7 +663,89 @@ index_create(Oid heapRelationId,
classObjectId, primary);
/*
- * fill in the index strategy structure with information from the
+ * Register constraint and dependencies for the index.
+ *
+ * If the index is from a CONSTRAINT clause, construct a pg_constraint
+ * entry. The index is then linked to the constraint, which in turn is
+ * linked to the table. If it's not a CONSTRAINT, make the dependency
+ * directly on the table.
+ *
+ * During bootstrap we can't register any dependencies, and we don't
+ * try to make a constraint either.
+ */
+ if (!IsBootstrapProcessingMode())
+ {
+ ObjectAddress myself,
+ referenced;
+
+ myself.classId = RelOid_pg_class;
+ myself.objectId = indexoid;
+ myself.objectSubId = 0;
+
+ if (isconstraint)
+ {
+ char constraintType;
+ Oid conOid;
+
+ if (primary)
+ constraintType = CONSTRAINT_PRIMARY;
+ else if (indexInfo->ii_Unique)
+ constraintType = CONSTRAINT_UNIQUE;
+ else
+ {
+ elog(ERROR, "index_create: constraint must be PRIMARY or UNIQUE");
+ constraintType = 0; /* keep compiler quiet */
+ }
+
+ conOid = CreateConstraintEntry(indexRelationName,
+ namespaceId,
+ constraintType,
+ false, /* isDeferrable */
+ false, /* isDeferred */
+ heapRelationId,
+ indexInfo->ii_KeyAttrNumbers,
+ indexInfo->ii_NumIndexAttrs,
+ InvalidOid, /* no domain */
+ InvalidOid, /* no foreign key */
+ NULL,
+ 0,
+ ' ',
+ ' ',
+ ' ',
+ NULL, /* Constraint Bin & Src */
+ NULL);
+
+ referenced.classId = get_system_catalog_relid(ConstraintRelationName);
+ referenced.objectId = conOid;
+ referenced.objectSubId = 0;
+
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ }
+ else
+ {
+ for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
+ {
+ referenced.classId = RelOid_pg_class;
+ referenced.objectId = heapRelationId;
+ referenced.objectSubId = indexInfo->ii_KeyAttrNumbers[i];
+
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
+ }
+ }
+
+ /* Store the dependency on the function (if appropriate) */
+ if (OidIsValid(indexInfo->ii_FuncOid))
+ {
+ referenced.classId = RelOid_pg_proc;
+ referenced.objectId = indexInfo->ii_FuncOid;
+ referenced.objectSubId = 0;
+
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
+ }
+
+ /*
+ * Fill in the index strategy structure with information from the
* catalogs. First we must advance the command counter so that we
* will see the newly-entered index catalog tuples.
*/
@@ -691,11 +776,11 @@ index_create(Oid heapRelationId,
return indexoid;
}
-/* ----------------------------------------------------------------
- *
+/*
* index_drop
*
- * ----------------------------------------------------------------
+ * NOTE: this routine should now only be called through performDeletion(),
+ * else associated dependencies won't be cleaned up.
*/
void
index_drop(Oid indexId)
@@ -731,17 +816,6 @@ index_drop(Oid indexId)
LockRelation(userIndexRelation, AccessExclusiveLock);
/*
- * Note: unlike heap_drop_with_catalog, we do not need to prevent
- * deletion of system indexes here; that's checked for upstream. If we
- * did check it here, deletion of TOAST tables would fail...
- */
-
- /*
- * fix DESCRIPTION relation
- */
- DeleteComments(indexId, RelOid_pg_class);
-
- /*
* fix RELATION relation
*/
relationRelation = heap_openr(RelationRelationName, RowExclusiveLock);
diff --git a/src/backend/catalog/indexing.c b/src/backend/catalog/indexing.c
index 5611edda853..9925c39af1e 100644
--- a/src/backend/catalog/indexing.c
+++ b/src/backend/catalog/indexing.c
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/catalog/indexing.c,v 1.95 2002/07/11 07:39:27 ishii Exp $
+ * $Header: /cvsroot/pgsql/src/backend/catalog/indexing.c,v 1.96 2002/07/12 18:43:14 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -45,10 +45,14 @@ char *Name_pg_attrdef_indices[Num_pg_attrdef_indices] =
{AttrDefaultIndex};
char *Name_pg_class_indices[Num_pg_class_indices] =
{ClassNameNspIndex, ClassOidIndex};
+char *Name_pg_constraint_indices[Num_pg_constraint_indices] =
+{ConstraintNameNspIndex, ConstraintOidIndex, ConstraintRelidIndex};
char *Name_pg_conversion_indices[Num_pg_conversion_indices] =
{ConversionNameNspIndex, ConversionDefaultIndex};
char *Name_pg_database_indices[Num_pg_database_indices] =
{DatabaseNameIndex, DatabaseOidIndex};
+char *Name_pg_depend_indices[Num_pg_depend_indices] =
+{DependDependerIndex, DependReferenceIndex};
char *Name_pg_group_indices[Num_pg_group_indices] =
{GroupNameIndex, GroupSysidIndex};
char *Name_pg_index_indices[Num_pg_index_indices] =
@@ -67,8 +71,6 @@ char *Name_pg_operator_indices[Num_pg_operator_indices] =
{OperatorOidIndex, OperatorNameNspIndex};
char *Name_pg_proc_indices[Num_pg_proc_indices] =
{ProcedureOidIndex, ProcedureNameNspIndex};
-char *Name_pg_relcheck_indices[Num_pg_relcheck_indices] =
-{RelCheckIndex};
char *Name_pg_rewrite_indices[Num_pg_rewrite_indices] =
{RewriteOidIndex, RewriteRelRulenameIndex};
char *Name_pg_shadow_indices[Num_pg_shadow_indices] =
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index 2af7e1d00f0..ad5a0c0d6a3 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -13,7 +13,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/catalog/namespace.c,v 1.23 2002/06/20 20:29:26 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/catalog/namespace.c,v 1.24 2002/07/12 18:43:15 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -23,6 +23,7 @@
#include "access/xact.h"
#include "catalog/catalog.h"
#include "catalog/catname.h"
+#include "catalog/dependency.h"
#include "catalog/heap.h"
#include "catalog/namespace.h"
#include "catalog/pg_inherits.h"
@@ -128,25 +129,10 @@ static Oid mySpecialNamespace = InvalidOid;
char *namespace_search_path = NULL;
-/*
- * Deletion ordering constraint item.
- */
-typedef struct DelConstraint
-{
- Oid referencer; /* table to delete first */
- Oid referencee; /* table to delete second */
- int pred; /* workspace for TopoSortRels */
- struct DelConstraint *link; /* workspace for TopoSortRels */
-} DelConstraint;
-
-
/* Local functions */
static void recomputeNamespacePath(void);
static void InitTempTableNamespace(void);
static void RemoveTempRelations(Oid tempNamespaceId);
-static List *FindTempRelations(Oid tempNamespaceId);
-static List *FindDeletionConstraints(List *relOids);
-static List *TopoSortRels(List *relOids, List *constraintList);
static void RemoveTempRelationsCallback(void);
static void NamespaceCallback(Datum arg, Oid relid);
@@ -1531,56 +1517,22 @@ AtEOXact_Namespace(bool isCommit)
static void
RemoveTempRelations(Oid tempNamespaceId)
{
- List *tempRelList;
- List *constraintList;
- List *lptr;
-
- /* Get a list of relations to delete */
- tempRelList = FindTempRelations(tempNamespaceId);
-
- if (tempRelList == NIL)
- return; /* nothing to do */
-
- /* If more than one, sort them to respect any deletion-order constraints */
- if (length(tempRelList) > 1)
- {
- constraintList = FindDeletionConstraints(tempRelList);
- if (constraintList != NIL)
- tempRelList = TopoSortRels(tempRelList, constraintList);
- }
-
- /* Scan the list and delete all entries */
- foreach(lptr, tempRelList)
- {
- Oid reloid = (Oid) lfirsti(lptr);
-
- heap_drop_with_catalog(reloid, true);
- /*
- * Advance cmd counter to make catalog changes visible, in case
- * a later entry depends on this one.
- */
- CommandCounterIncrement();
- }
-}
-
-/*
- * Find all relations in the specified temp namespace.
- *
- * Returns a list of relation OIDs.
- */
-static List *
-FindTempRelations(Oid tempNamespaceId)
-{
- List *tempRelList = NIL;
Relation pgclass;
HeapScanDesc scan;
HeapTuple tuple;
ScanKeyData key;
+ ObjectAddress object;
/*
* Scan pg_class to find all the relations in the target namespace.
* Ignore indexes, though, on the assumption that they'll go away
* when their tables are deleted.
+ *
+ * NOTE: if there are deletion constraints between temp relations,
+ * then our CASCADE delete call may cause as-yet-unvisited objects
+ * to go away. This is okay because we are using SnapshotNow; when
+ * the scan does reach those pg_class tuples, they'll be ignored as
+ * already deleted.
*/
ScanKeyEntryInitialize(&key, 0x0,
Anum_pg_class_relnamespace,
@@ -1597,7 +1549,10 @@ FindTempRelations(Oid tempNamespaceId)
case RELKIND_RELATION:
case RELKIND_SEQUENCE:
case RELKIND_VIEW:
- tempRelList = lconsi(tuple->t_data->t_oid, tempRelList);
+ object.classId = RelOid_pg_class;
+ object.objectId = tuple->t_data->t_oid;
+ object.objectSubId = 0;
+ performDeletion(&object, DROP_CASCADE);
break;
default:
break;
@@ -1606,164 +1561,6 @@ FindTempRelations(Oid tempNamespaceId)
heap_endscan(scan);
heap_close(pgclass, AccessShareLock);
-
- return tempRelList;
-}
-
-/*
- * Find deletion-order constraints involving the given relation OIDs.
- *
- * Returns a list of DelConstraint objects.
- */
-static List *
-FindDeletionConstraints(List *relOids)
-{
- List *constraintList = NIL;
- Relation inheritsrel;
- HeapScanDesc scan;
- HeapTuple tuple;
-
- /*
- * Scan pg_inherits to find parents and children that are in the list.
- */
- inheritsrel = heap_openr(InheritsRelationName, AccessShareLock);
- scan = heap_beginscan(inheritsrel, SnapshotNow, 0, NULL);
-
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
- {
- Oid inhrelid = ((Form_pg_inherits) GETSTRUCT(tuple))->inhrelid;
- Oid inhparent = ((Form_pg_inherits) GETSTRUCT(tuple))->inhparent;
-
- if (intMember(inhrelid, relOids) && intMember(inhparent, relOids))
- {
- DelConstraint *item;
-
- item = (DelConstraint *) palloc(sizeof(DelConstraint));
- item->referencer = inhrelid;
- item->referencee = inhparent;
- constraintList = lcons(item, constraintList);
- }
- }
-
- heap_endscan(scan);
- heap_close(inheritsrel, AccessShareLock);
-
- return constraintList;
-}
-
-/*
- * TopoSortRels -- topological sort of a list of rels to delete
- *
- * This is a lot simpler and slower than, for example, the topological sort
- * algorithm shown in Knuth's Volume 1. However, we are not likely to be
- * working with more than a few constraints, so the apparent slowness of the
- * algorithm won't really matter.
- */
-static List *
-TopoSortRels(List *relOids, List *constraintList)
-{
- int queue_size = length(relOids);
- Oid *rels;
- int *beforeConstraints;
- DelConstraint **afterConstraints;
- List *resultList = NIL;
- List *lptr;
- int i,
- j,
- k,
- last;
-
- /* Allocate workspace */
- rels = (Oid *) palloc(queue_size * sizeof(Oid));
- beforeConstraints = (int *) palloc(queue_size * sizeof(int));
- afterConstraints = (DelConstraint **)
- palloc(queue_size * sizeof(DelConstraint*));
-
- /* Build an array of the target relation OIDs */
- i = 0;
- foreach(lptr, relOids)
- {
- rels[i++] = (Oid) lfirsti(lptr);
- }
-
- /*
- * Scan the constraints, and for each rel in the array, generate a
- * count of the number of constraints that say it must be before
- * something else, plus a list of the constraints that say it must be
- * after something else. The count for the j'th rel is stored in
- * beforeConstraints[j], and the head of its list in
- * afterConstraints[j]. Each constraint stores its list link in
- * its link field (note any constraint will be in just one list).
- * The array index for the before-rel of each constraint is
- * remembered in the constraint's pred field.
- */
- MemSet(beforeConstraints, 0, queue_size * sizeof(int));
- MemSet(afterConstraints, 0, queue_size * sizeof(DelConstraint*));
- foreach(lptr, constraintList)
- {
- DelConstraint *constraint = (DelConstraint *) lfirst(lptr);
- Oid rel;
-
- /* Find the referencer rel in the array */
- rel = constraint->referencer;
- for (j = queue_size; --j >= 0;)
- {
- if (rels[j] == rel)
- break;
- }
- Assert(j >= 0); /* should have found a match */
- /* Find the referencee rel in the array */
- rel = constraint->referencee;
- for (k = queue_size; --k >= 0;)
- {
- if (rels[k] == rel)
- break;
- }
- Assert(k >= 0); /* should have found a match */
- beforeConstraints[j]++; /* referencer must come before */
- /* add this constraint to list of after-constraints for referencee */
- constraint->pred = j;
- constraint->link = afterConstraints[k];
- afterConstraints[k] = constraint;
- }
- /*--------------------
- * Now scan the rels array backwards. At each step, output the
- * last rel that has no remaining before-constraints, and decrease
- * the beforeConstraints count of each of the rels it was constrained
- * against. (This is the right order since we are building the result
- * list back-to-front.)
- * i = counter for number of rels left to output
- * j = search index for rels[]
- * dc = temp for scanning constraint list for rel j
- * last = last valid index in rels (avoid redundant searches)
- *--------------------
- */
- last = queue_size - 1;
- for (i = queue_size; --i >= 0;)
- {
- DelConstraint *dc;
-
- /* Find next candidate to output */
- while (rels[last] == InvalidOid)
- last--;
- for (j = last; j >= 0; j--)
- {
- if (rels[j] != InvalidOid && beforeConstraints[j] == 0)
- break;
- }
- /* If no available candidate, topological sort fails */
- if (j < 0)
- elog(ERROR, "TopoSortRels: failed to find a workable deletion ordering");
- /* Output candidate, and mark it done by zeroing rels[] entry */
- resultList = lconsi(rels[j], resultList);
- rels[j] = InvalidOid;
- /* Update beforeConstraints counts of its predecessors */
- for (dc = afterConstraints[j]; dc; dc = dc->link)
- beforeConstraints[dc->pred]--;
- }
-
- /* Done */
- return resultList;
}
/*
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
new file mode 100644
index 00000000000..47712dde1d6
--- /dev/null
+++ b/src/backend/catalog/pg_constraint.c
@@ -0,0 +1,453 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_constraint.c
+ * routines to support manipulation of the pg_constraint relation
+ *
+ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/catalog/pg_constraint.c,v 1.1 2002/07/12 18:43:15 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/genam.h"
+#include "catalog/catalog.h"
+#include "catalog/catname.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_constraint.h"
+#include "miscadmin.h"
+#include "utils/array.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/syscache.h"
+
+
+/*
+ * CreateConstraintEntry
+ * Create a constraint table entry.
+ *
+ * Subsidiary records (such as triggers or indexes to implement the
+ * constraint) are *not* created here. But we do make dependency links
+ * from the constraint to the things it depends on.
+ */
+Oid
+CreateConstraintEntry(const char *constraintName,
+ Oid constraintNamespace,
+ char constraintType,
+ bool isDeferrable,
+ bool isDeferred,
+ Oid relId,
+ const int16 *constraintKey,
+ int constraintNKeys,
+ Oid domainId,
+ Oid foreignRelId,
+ const int16 *foreignKey,
+ int foreignNKeys,
+ char foreignUpdateType,
+ char foreignDeleteType,
+ char foreignMatchType,
+ const char *conBin,
+ const char *conSrc)
+{
+ Relation conDesc;
+ Oid conOid;
+ HeapTuple tup;
+ char nulls[Natts_pg_constraint];
+ Datum values[Natts_pg_constraint];
+ ArrayType *conkeyArray;
+ ArrayType *confkeyArray;
+ NameData cname;
+ int i;
+ ObjectAddress conobject;
+
+ conDesc = heap_openr(ConstraintRelationName, RowExclusiveLock);
+
+ Assert(constraintName);
+ namestrcpy(&cname, constraintName);
+
+ /*
+ * Convert C arrays into Postgres arrays.
+ */
+ if (constraintNKeys > 0)
+ {
+ Datum *conkey;
+
+ conkey = (Datum *) palloc(constraintNKeys * sizeof(Datum));
+ for (i = 0; i < constraintNKeys; i++)
+ conkey[i] = Int16GetDatum(constraintKey[i]);
+ conkeyArray = construct_array(conkey, constraintNKeys,
+ true, 2, 's');
+ }
+ else
+ conkeyArray = NULL;
+
+ if (foreignNKeys > 0)
+ {
+ Datum *confkey;
+
+ confkey = (Datum *) palloc(foreignNKeys * sizeof(Datum));
+ for (i = 0; i < foreignNKeys; i++)
+ confkey[i] = Int16GetDatum(foreignKey[i]);
+ confkeyArray = construct_array(confkey, foreignNKeys,
+ true, 2, 's');
+ }
+ else
+ confkeyArray = NULL;
+
+ /* initialize nulls and values */
+ for (i = 0; i < Natts_pg_constraint; i++)
+ {
+ nulls[i] = ' ';
+ values[i] = (Datum) NULL;
+ }
+
+ values[Anum_pg_constraint_conname - 1] = NameGetDatum(&cname);
+ values[Anum_pg_constraint_connamespace - 1] = ObjectIdGetDatum(constraintNamespace);
+ values[Anum_pg_constraint_contype - 1] = CharGetDatum(constraintType);
+ values[Anum_pg_constraint_condeferrable - 1] = BoolGetDatum(isDeferrable);
+ values[Anum_pg_constraint_condeferred - 1] = BoolGetDatum(isDeferred);
+ values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(relId);
+ values[Anum_pg_constraint_contypid - 1] = ObjectIdGetDatum(domainId);
+ values[Anum_pg_constraint_confrelid - 1] = ObjectIdGetDatum(foreignRelId);
+ values[Anum_pg_constraint_confupdtype - 1] = CharGetDatum(foreignUpdateType);
+ values[Anum_pg_constraint_confdeltype - 1] = CharGetDatum(foreignDeleteType);
+ values[Anum_pg_constraint_confmatchtype - 1] = CharGetDatum(foreignMatchType);
+
+ if (conkeyArray)
+ values[Anum_pg_constraint_conkey - 1] = PointerGetDatum(conkeyArray);
+ else
+ nulls[Anum_pg_constraint_conkey - 1] = 'n';
+
+ if (confkeyArray)
+ values[Anum_pg_constraint_confkey - 1] = PointerGetDatum(confkeyArray);
+ else
+ nulls[Anum_pg_constraint_confkey - 1] = 'n';
+
+ /*
+ * initialize the binary form of the check constraint.
+ */
+ if (conBin)
+ values[Anum_pg_constraint_conbin - 1] = DirectFunctionCall1(textin,
+ CStringGetDatum(conBin));
+ else
+ nulls[Anum_pg_constraint_conbin - 1] = 'n';
+
+ /*
+ * initialize the text form of the check constraint
+ */
+ if (conSrc)
+ values[Anum_pg_constraint_consrc - 1] = DirectFunctionCall1(textin,
+ CStringGetDatum(conSrc));
+ else
+ nulls[Anum_pg_constraint_consrc - 1] = 'n';
+
+ tup = heap_formtuple(RelationGetDescr(conDesc), values, nulls);
+
+ conOid = simple_heap_insert(conDesc, tup);
+
+ /* Handle Indices */
+ if (RelationGetForm(conDesc)->relhasindex)
+ {
+ Relation idescs[Num_pg_constraint_indices];
+
+ CatalogOpenIndices(Num_pg_constraint_indices, Name_pg_constraint_indices, idescs);
+ CatalogIndexInsert(idescs, Num_pg_constraint_indices, conDesc, tup);
+ CatalogCloseIndices(Num_pg_constraint_indices, idescs);
+ }
+
+ conobject.classId = RelationGetRelid(conDesc);
+ conobject.objectId = conOid;
+ conobject.objectSubId = 0;
+
+ heap_close(conDesc, RowExclusiveLock);
+
+ if (OidIsValid(relId))
+ {
+ /*
+ * Register auto dependency from constraint to owning relation,
+ * or to specific column(s) if any are mentioned.
+ */
+ ObjectAddress relobject;
+
+ relobject.classId = RelOid_pg_class;
+ relobject.objectId = relId;
+ if (constraintNKeys > 0)
+ {
+ for (i = 0; i < constraintNKeys; i++)
+ {
+ relobject.objectSubId = constraintKey[i];
+
+ recordDependencyOn(&conobject, &relobject, DEPENDENCY_AUTO);
+ }
+ }
+ else
+ {
+ relobject.objectSubId = 0;
+
+ recordDependencyOn(&conobject, &relobject, DEPENDENCY_AUTO);
+ }
+ }
+
+ if (OidIsValid(foreignRelId))
+ {
+ /*
+ * Register dependency from constraint to foreign relation,
+ * or to specific column(s) if any are mentioned.
+ *
+ * In normal case of two separate relations, make this a NORMAL
+ * dependency (so dropping the FK table would require CASCADE).
+ * However, for a self-reference just make it AUTO.
+ */
+ DependencyType deptype;
+ ObjectAddress relobject;
+
+ deptype = (foreignRelId == relId) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL;
+ relobject.classId = RelOid_pg_class;
+ relobject.objectId = foreignRelId;
+ if (foreignNKeys > 0)
+ {
+ for (i = 0; i < foreignNKeys; i++)
+ {
+ relobject.objectSubId = foreignKey[i];
+
+ recordDependencyOn(&conobject, &relobject, deptype);
+ }
+ }
+ else
+ {
+ relobject.objectSubId = 0;
+
+ recordDependencyOn(&conobject, &relobject, deptype);
+ }
+ }
+
+ return conOid;
+}
+
+
+/*
+ * Test whether given name is currently used as a constraint name
+ * for the given relation.
+ *
+ * NB: Caller should hold exclusive lock on the given relation, else
+ * this test is not very meaningful.
+ */
+bool
+ConstraintNameIsUsed(Oid relId, Oid relNamespace, const char *cname)
+{
+ bool found;
+ Relation conDesc;
+ SysScanDesc conscan;
+ ScanKeyData skey[2];
+ HeapTuple tup;
+
+ conDesc = heap_openr(ConstraintRelationName, RowExclusiveLock);
+
+ found = false;
+
+ ScanKeyEntryInitialize(&skey[0], 0x0,
+ Anum_pg_constraint_conname, F_NAMEEQ,
+ CStringGetDatum(cname));
+
+ ScanKeyEntryInitialize(&skey[1], 0x0,
+ Anum_pg_constraint_connamespace, F_OIDEQ,
+ ObjectIdGetDatum(relNamespace));
+
+ conscan = systable_beginscan(conDesc, ConstraintNameNspIndex, true,
+ SnapshotNow, 2, skey);
+
+ while (HeapTupleIsValid(tup = systable_getnext(conscan)))
+ {
+ Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tup);
+
+ if (con->conrelid == relId)
+ {
+ found = true;
+ break;
+ }
+ }
+
+ systable_endscan(conscan);
+ heap_close(conDesc, RowExclusiveLock);
+
+ return found;
+}
+
+/*
+ * Generate a currently-unused constraint name for the given relation.
+ *
+ * The passed counter should be initialized to 0 the first time through.
+ * If multiple constraint names are to be generated in a single command,
+ * pass the new counter value to each successive call, else the same
+ * name will be generated each time.
+ *
+ * NB: Caller should hold exclusive lock on the given relation, else
+ * someone else might choose the same name concurrently!
+ */
+char *
+GenerateConstraintName(Oid relId, Oid relNamespace, int *counter)
+{
+ bool found;
+ Relation conDesc;
+ char *cname;
+
+ cname = (char *) palloc(NAMEDATALEN * sizeof(char));
+
+ conDesc = heap_openr(ConstraintRelationName, RowExclusiveLock);
+
+ /* Loop until we find a non-conflicting constraint name */
+ /* We assume there will be one eventually ... */
+ do
+ {
+ SysScanDesc conscan;
+ ScanKeyData skey[2];
+ HeapTuple tup;
+
+ ++(*counter);
+ snprintf(cname, NAMEDATALEN, "$%d", *counter);
+
+ /*
+ * This duplicates ConstraintNameIsUsed() so that we can avoid
+ * re-opening pg_constraint for each iteration.
+ */
+ found = false;
+
+ ScanKeyEntryInitialize(&skey[0], 0x0,
+ Anum_pg_constraint_conname, F_NAMEEQ,
+ CStringGetDatum(cname));
+
+ ScanKeyEntryInitialize(&skey[1], 0x0,
+ Anum_pg_constraint_connamespace, F_OIDEQ,
+ ObjectIdGetDatum(relNamespace));
+
+ conscan = systable_beginscan(conDesc, ConstraintNameNspIndex, true,
+ SnapshotNow, 2, skey);
+
+ while (HeapTupleIsValid(tup = systable_getnext(conscan)))
+ {
+ Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tup);
+
+ if (con->conrelid == relId)
+ {
+ found = true;
+ break;
+ }
+ }
+
+ systable_endscan(conscan);
+ } while (found);
+
+ heap_close(conDesc, RowExclusiveLock);
+
+ return cname;
+}
+
+/*
+ * Does the given name look like a generated constraint name?
+ *
+ * This is a test on the form of the name, *not* on whether it has
+ * actually been assigned.
+ */
+bool
+ConstraintNameIsGenerated(const char *cname)
+{
+ if (cname[0] != '$')
+ return false;
+ if (strspn(cname+1, "0123456789") != strlen(cname+1))
+ return false;
+ return true;
+}
+
+/*
+ * Delete a single constraint record.
+ */
+void
+RemoveConstraintById(Oid conId)
+{
+ Relation conDesc;
+ ScanKeyData skey[1];
+ SysScanDesc conscan;
+ HeapTuple tup;
+ Form_pg_constraint con;
+
+ conDesc = heap_openr(ConstraintRelationName, RowExclusiveLock);
+
+ ScanKeyEntryInitialize(&skey[0], 0x0,
+ ObjectIdAttributeNumber, F_OIDEQ,
+ ObjectIdGetDatum(conId));
+
+ conscan = systable_beginscan(conDesc, ConstraintOidIndex, true,
+ SnapshotNow, 1, skey);
+
+ tup = systable_getnext(conscan);
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "RemoveConstraintById: constraint %u not found",
+ conId);
+ con = (Form_pg_constraint) GETSTRUCT(tup);
+
+ /*
+ * If the constraint is for a relation, open and exclusive-lock
+ * the relation it's for.
+ *
+ * XXX not clear what we should lock, if anything, for other constraints.
+ */
+ if (OidIsValid(con->conrelid))
+ {
+ Relation rel;
+
+ rel = heap_open(con->conrelid, AccessExclusiveLock);
+
+ /*
+ * We need to update the relcheck count if it is a check constraint
+ * being dropped. This update will force backends to rebuild
+ * relcache entries when we commit.
+ */
+ if (con->contype == CONSTRAINT_CHECK)
+ {
+ Relation pgrel;
+ HeapTuple relTup;
+ Form_pg_class classForm;
+ Relation ridescs[Num_pg_class_indices];
+
+ pgrel = heap_openr(RelationRelationName, RowExclusiveLock);
+ relTup = SearchSysCacheCopy(RELOID,
+ ObjectIdGetDatum(con->conrelid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(relTup))
+ elog(ERROR, "cache lookup of relation %u failed",
+ con->conrelid);
+ classForm = (Form_pg_class) GETSTRUCT(relTup);
+
+ if (classForm->relchecks == 0)
+ elog(ERROR, "RemoveConstraintById: relation %s has relchecks = 0",
+ RelationGetRelationName(rel));
+ classForm->relchecks--;
+
+ simple_heap_update(pgrel, &relTup->t_self, relTup);
+
+ CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs);
+ CatalogIndexInsert(ridescs, Num_pg_class_indices, pgrel, relTup);
+ CatalogCloseIndices(Num_pg_class_indices, ridescs);
+
+ heap_freetuple(relTup);
+
+ heap_close(pgrel, RowExclusiveLock);
+ }
+
+ /* Keep lock on constraint's rel until end of xact */
+ heap_close(rel, NoLock);
+ }
+
+ /* Fry the constraint itself */
+ simple_heap_delete(conDesc, &tup->t_self);
+
+ /* Clean up */
+ systable_endscan(conscan);
+ heap_close(conDesc, RowExclusiveLock);
+}
diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c
new file mode 100644
index 00000000000..4057374069a
--- /dev/null
+++ b/src/backend/catalog/pg_depend.c
@@ -0,0 +1,147 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_depend.c
+ * routines to support manipulation of the pg_depend relation
+ *
+ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/catalog/pg_depend.c,v 1.1 2002/07/12 18:43:15 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "catalog/catname.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)
+{
+ Relation dependDesc;
+ HeapTuple tup;
+ int i;
+ char nulls[Natts_pg_depend];
+ Datum values[Natts_pg_depend];
+ Relation idescs[Num_pg_depend_indices];
+
+ /*
+ * During bootstrap, do nothing since pg_depend may not exist yet.
+ * initdb will fill in appropriate pg_depend entries after bootstrap.
+ */
+ if (IsBootstrapProcessingMode())
+ return;
+
+ 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))
+ {
+ /*
+ * Record the Dependency. Note we don't bother to check for
+ * duplicate dependencies; there's no harm in them.
+ */
+ for (i = 0; i < Natts_pg_depend; ++i)
+ {
+ nulls[i] = ' ';
+ values[i] = (Datum) 0;
+ }
+
+ 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);
+ CatalogCloseIndices(Num_pg_depend_indices, idescs);
+ }
+
+ heap_close(dependDesc, RowExclusiveLock);
+}
+
+
+/*
+ * 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;
+ SysScanDesc scan;
+ HeapTuple tup;
+ ScanKeyData key[2];
+
+ ScanKeyEntryInitialize(&key[0], 0x0,
+ Anum_pg_depend_refclassid, F_OIDEQ,
+ ObjectIdGetDatum(object->classId));
+
+ ScanKeyEntryInitialize(&key[1], 0x0,
+ Anum_pg_depend_refobjid, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+
+ scan = systable_beginscan(rel, DependReferenceIndex, true,
+ SnapshotNow, 2, key);
+
+ /*
+ * Since we won't generate additional pg_depend entries for pinned
+ * 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.
+ */
+ tup = systable_getnext(scan);
+ if (HeapTupleIsValid(tup))
+ {
+ Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(tup);
+
+ if (foundDep->deptype == DEPENDENCY_PIN)
+ ret = true;
+ }
+
+ systable_endscan(scan);
+
+ return ret;
+}
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index 5b8b9a88c22..88f113beb01 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/catalog/pg_type.c,v 1.72 2002/06/20 20:29:26 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/catalog/pg_type.c,v 1.73 2002/07/12 18:43:15 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -16,6 +16,7 @@
#include "access/heapam.h"
#include "catalog/catname.h"
+#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/pg_type.h"
#include "miscadmin.h"
@@ -166,6 +167,8 @@ TypeCreate(const char *typeName,
NameData name;
TupleDesc tupDesc;
int i;
+ ObjectAddress myself,
+ referenced;
/*
* validate size specifications: either positive (fixed-length) or -1
@@ -299,6 +302,77 @@ TypeCreate(const char *typeName,
}
/*
+ * Create dependencies
+ */
+ myself.classId = RelOid_pg_type;
+ myself.objectId = typeObjectId;
+ myself.objectSubId = 0;
+
+ /* Normal dependencies on the I/O functions */
+ referenced.classId = RelOid_pg_proc;
+ referenced.objectId = inputProcedure;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+ referenced.classId = RelOid_pg_proc;
+ referenced.objectId = outputProcedure;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+ if (receiveProcedure != inputProcedure)
+ {
+ referenced.classId = RelOid_pg_proc;
+ referenced.objectId = receiveProcedure;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
+
+ if (sendProcedure != outputProcedure)
+ {
+ referenced.classId = RelOid_pg_proc;
+ referenced.objectId = sendProcedure;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
+
+ /*
+ * If the type is a rowtype for a relation, mark it as internally
+ * dependent on the relation. This allows it to be auto-dropped
+ * when the relation is, and not otherwise.
+ */
+ if (OidIsValid(relationOid))
+ {
+ referenced.classId = RelOid_pg_class;
+ referenced.objectId = relationOid;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ }
+
+ /*
+ * If the type is an array type, mark it auto-dependent on the
+ * base type. (This is a compromise between the typical case where the
+ * array type is automatically generated and the case where it is manually
+ * created: we'd prefer INTERNAL for the former case and NORMAL for the
+ * latter.)
+ */
+ if (OidIsValid(elementType))
+ {
+ referenced.classId = RelOid_pg_type;
+ referenced.objectId = elementType;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
+ }
+
+ /* Normal dependency from a domain to its base type. */
+ if (OidIsValid(baseType))
+ {
+ referenced.classId = RelOid_pg_type;
+ referenced.objectId = baseType;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
+
+ /*
* finish up
*/
heap_close(pg_type_desc, RowExclusiveLock);
diff --git a/src/backend/commands/aggregatecmds.c b/src/backend/commands/aggregatecmds.c
index c3c1ed16dfc..1b83f03f481 100644
--- a/src/backend/commands/aggregatecmds.c
+++ b/src/backend/commands/aggregatecmds.c
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/aggregatecmds.c,v 1.2 2002/04/27 03:45:00 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/aggregatecmds.c,v 1.3 2002/07/12 18:43:15 tgl Exp $
*
* DESCRIPTION
* The "DefineFoo" routines take the parse tree and pick out the
@@ -24,10 +24,10 @@
#include "access/heapam.h"
#include "catalog/catname.h"
+#include "catalog/dependency.h"
#include "catalog/namespace.h"
#include "catalog/pg_aggregate.h"
#include "catalog/pg_proc.h"
-#include "commands/comment.h"
#include "commands/defrem.h"
#include "miscadmin.h"
#include "parser/parse_func.h"
@@ -141,13 +141,19 @@ DefineAggregate(List *names, List *parameters)
}
+/*
+ * RemoveAggregate
+ * Deletes an aggregate.
+ */
void
-RemoveAggregate(List *aggName, TypeName *aggType)
+RemoveAggregate(RemoveAggrStmt *stmt)
{
- Relation relation;
- HeapTuple tup;
+ List *aggName = stmt->aggname;
+ TypeName *aggType = stmt->aggtype;
Oid basetypeID;
Oid procOid;
+ HeapTuple tup;
+ ObjectAddress object;
/*
* if a basetype is passed in, then attempt to find an aggregate for
@@ -164,8 +170,9 @@ RemoveAggregate(List *aggName, TypeName *aggType)
procOid = find_aggregate_func("RemoveAggregate", aggName, basetypeID);
- relation = heap_openr(ProcedureRelationName, RowExclusiveLock);
-
+ /*
+ * Find the function tuple, do permissions and validity checks
+ */
tup = SearchSysCache(PROCOID,
ObjectIdGetDatum(procOid),
0, 0, 0);
@@ -179,30 +186,16 @@ RemoveAggregate(List *aggName, TypeName *aggType)
GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, NameListToString(aggName));
- /* Delete any comments associated with this function */
- DeleteComments(procOid, RelationGetRelid(relation));
-
- /* Remove the pg_proc tuple */
- simple_heap_delete(relation, &tup->t_self);
+ /* find_aggregate_func already checked it is an aggregate */
ReleaseSysCache(tup);
- heap_close(relation, RowExclusiveLock);
-
- /* Remove the pg_aggregate tuple */
-
- relation = heap_openr(AggregateRelationName, RowExclusiveLock);
-
- tup = SearchSysCache(AGGFNOID,
- ObjectIdGetDatum(procOid),
- 0, 0, 0);
- if (!HeapTupleIsValid(tup)) /* should not happen */
- elog(ERROR, "RemoveAggregate: couldn't find pg_aggregate tuple for %s",
- NameListToString(aggName));
-
- simple_heap_delete(relation, &tup->t_self);
-
- ReleaseSysCache(tup);
+ /*
+ * Do the deletion
+ */
+ object.classId = RelOid_pg_proc;
+ object.objectId = procOid;
+ object.objectSubId = 0;
- heap_close(relation, RowExclusiveLock);
+ performDeletion(&object, stmt->behavior);
}
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 3306943fb04..837390744fb 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -15,7 +15,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.82 2002/06/20 20:29:26 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.83 2002/07/12 18:43:15 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -24,6 +24,7 @@
#include "access/genam.h"
#include "access/heapam.h"
+#include "catalog/dependency.h"
#include "catalog/heap.h"
#include "catalog/index.h"
#include "catalog/pg_index.h"
@@ -64,6 +65,7 @@ cluster(RangeVar *oldrelation, char *oldindexname)
OldIndex;
char NewHeapName[NAMEDATALEN];
char NewIndexName[NAMEDATALEN];
+ ObjectAddress object;
/*
* We grab exclusive access to the target rel and index for the
@@ -119,9 +121,14 @@ cluster(RangeVar *oldrelation, char *oldindexname)
CommandCounterIncrement();
/* Destroy old heap (along with its index) and rename new. */
- heap_drop_with_catalog(OIDOldHeap, allowSystemTableMods);
+ object.classId = RelOid_pg_class;
+ object.objectId = OIDOldHeap;
+ object.objectSubId = 0;
- CommandCounterIncrement();
+ /* XXX better to use DROP_CASCADE here? */
+ performDeletion(&object, DROP_RESTRICT);
+
+ /* performDeletion does CommandCounterIncrement at end */
renamerel(OIDNewHeap, oldrelation->relname);
@@ -198,6 +205,7 @@ copy_index(Oid OIDOldIndex, Oid OIDNewHeap, const char *NewIndexName)
OldIndex->rd_rel->relam,
OldIndex->rd_index->indclass,
OldIndex->rd_index->indisprimary,
+ false, /* XXX losing constraint status */
allowSystemTableMods);
setRelhasindex(OIDNewHeap, true,
diff --git a/src/backend/commands/comment.c b/src/backend/commands/comment.c
index 160a52246a1..b1ce1b2ce65 100644
--- a/src/backend/commands/comment.c
+++ b/src/backend/commands/comment.c
@@ -7,7 +7,7 @@
* Copyright (c) 1996-2001, PostgreSQL Global Development Group
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/comment.c,v 1.49 2002/06/20 20:51:45 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/comment.c,v 1.50 2002/07/12 18:43:15 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -225,38 +225,45 @@ CreateComments(Oid oid, Oid classoid, int32 subid, char *comment)
}
/*
- * DeleteComments --
+ * DeleteComments -- remove comments for an object
*
- * This routine is used to purge all comments associated with an object,
- * regardless of their objsubid. It is called, for example, when a relation
- * is destroyed.
+ * If subid is nonzero then only comments matching it will be removed.
+ * If subid is zero, all comments matching the oid/classoid will be removed
+ * (this corresponds to deleting a whole object).
*/
void
-DeleteComments(Oid oid, Oid classoid)
+DeleteComments(Oid oid, Oid classoid, int32 subid)
{
Relation description;
- ScanKeyData skey[2];
+ ScanKeyData skey[3];
+ int nkeys;
SysScanDesc sd;
HeapTuple oldtuple;
/* Use the index to search for all matching old tuples */
- ScanKeyEntryInitialize(&skey[0],
- (bits16) 0x0,
- (AttrNumber) 1,
- (RegProcedure) F_OIDEQ,
+ ScanKeyEntryInitialize(&skey[0], 0x0,
+ Anum_pg_description_objoid, F_OIDEQ,
ObjectIdGetDatum(oid));
- ScanKeyEntryInitialize(&skey[1],
- (bits16) 0x0,
- (AttrNumber) 2,
- (RegProcedure) F_OIDEQ,
+ ScanKeyEntryInitialize(&skey[1], 0x0,
+ Anum_pg_description_classoid, F_OIDEQ,
ObjectIdGetDatum(classoid));
+ if (subid != 0)
+ {
+ ScanKeyEntryInitialize(&skey[2], 0x0,
+ Anum_pg_description_objsubid, F_INT4EQ,
+ Int32GetDatum(subid));
+ nkeys = 3;
+ }
+ else
+ nkeys = 2;
+
description = heap_openr(DescriptionRelationName, RowExclusiveLock);
sd = systable_beginscan(description, DescriptionObjIndex, true,
- SnapshotNow, 2, skey);
+ SnapshotNow, nkeys, skey);
while ((oldtuple = systable_getnext(sd)) != NULL)
{
@@ -266,7 +273,7 @@ DeleteComments(Oid oid, Oid classoid)
/* Done */
systable_endscan(sd);
- heap_close(description, NoLock);
+ heap_close(description, RowExclusiveLock);
}
/*
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index 3526b91b997..c6bbb371860 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.95 2002/06/20 20:29:27 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.96 2002/07/12 18:43:15 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -456,8 +456,13 @@ dropdb(const char *dbname)
heap_endscan(pgdbscan);
- /* Delete any comments associated with the database */
- DeleteComments(db_id, RelationGetRelid(pgdbrel));
+ /*
+ * Delete any comments associated with the database
+ *
+ * NOTE: this is probably dead code since any such comments should have
+ * been in that database, not mine.
+ */
+ DeleteComments(db_id, RelationGetRelid(pgdbrel), 0);
/*
* Close pg_database, but keep exclusive lock till commit to ensure
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 638fd19a8eb..9a33810b073 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/functioncmds.c,v 1.7 2002/06/20 20:29:27 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/functioncmds.c,v 1.8 2002/07/12 18:43:16 tgl Exp $
*
* DESCRIPTION
* These routines take the parse tree and pick out the
@@ -33,11 +33,11 @@
#include "access/heapam.h"
#include "catalog/catname.h"
+#include "catalog/dependency.h"
#include "catalog/namespace.h"
#include "catalog/pg_language.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
-#include "commands/comment.h"
#include "commands/defrem.h"
#include "miscadmin.h"
#include "optimizer/cost.h"
@@ -532,25 +532,22 @@ CreateFunction(CreateFunctionStmt *stmt)
/*
* RemoveFunction
* Deletes a function.
- *
- * Exceptions:
- * BadArg if name is invalid.
- * "ERROR" if function nonexistent.
- * ...
*/
void
-RemoveFunction(List *functionName, /* function name to be removed */
- List *argTypes) /* list of TypeName nodes */
+RemoveFunction(RemoveFuncStmt *stmt)
{
+ List *functionName = stmt->funcname;
+ List *argTypes = stmt->args; /* list of TypeName nodes */
Oid funcOid;
- Relation relation;
HeapTuple tup;
+ ObjectAddress object;
+ /*
+ * Find the function, do permissions and validity checks
+ */
funcOid = LookupFuncNameTypeNames(functionName, argTypes,
true, "RemoveFunction");
- relation = heap_openr(ProcedureRelationName, RowExclusiveLock);
-
tup = SearchSysCache(PROCOID,
ObjectIdGetDatum(funcOid),
0, 0, 0);
@@ -576,12 +573,69 @@ RemoveFunction(List *functionName, /* function name to be removed */
NameListToString(functionName));
}
- /* Delete any comments associated with this function */
- DeleteComments(funcOid, RelationGetRelid(relation));
+ ReleaseSysCache(tup);
+
+ /*
+ * Do the deletion
+ */
+ object.classId = RelOid_pg_proc;
+ object.objectId = funcOid;
+ object.objectSubId = 0;
+
+ performDeletion(&object, stmt->behavior);
+}
+
+/*
+ * Guts of function deletion.
+ *
+ * Note: this is also used for aggregate deletion, since the OIDs of
+ * both functions and aggregates point to pg_proc.
+ */
+void
+RemoveFunctionById(Oid funcOid)
+{
+ Relation relation;
+ HeapTuple tup;
+ bool isagg;
+
+ /*
+ * Delete the pg_proc tuple.
+ */
+ relation = heap_openr(ProcedureRelationName, RowExclusiveLock);
+
+ tup = SearchSysCache(PROCOID,
+ ObjectIdGetDatum(funcOid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tup)) /* should not happen */
+ elog(ERROR, "RemoveFunctionById: couldn't find tuple for function %u",
+ funcOid);
+
+ isagg = ((Form_pg_proc) GETSTRUCT(tup))->proisagg;
simple_heap_delete(relation, &tup->t_self);
ReleaseSysCache(tup);
heap_close(relation, RowExclusiveLock);
+
+ /*
+ * If there's a pg_aggregate tuple, delete that too.
+ */
+ if (isagg)
+ {
+ relation = heap_openr(AggregateRelationName, RowExclusiveLock);
+
+ tup = SearchSysCache(AGGFNOID,
+ ObjectIdGetDatum(funcOid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tup)) /* should not happen */
+ elog(ERROR, "RemoveFunctionById: couldn't find pg_aggregate tuple for %u",
+ funcOid);
+
+ simple_heap_delete(relation, &tup->t_self);
+
+ ReleaseSysCache(tup);
+
+ heap_close(relation, RowExclusiveLock);
+ }
}
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 1338f16bb59..5cf03bd11aa 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.76 2002/07/01 15:27:45 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.77 2002/07/12 18:43:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -18,6 +18,7 @@
#include "access/heapam.h"
#include "catalog/catalog.h"
#include "catalog/catname.h"
+#include "catalog/dependency.h"
#include "catalog/index.h"
#include "catalog/namespace.h"
#include "catalog/pg_opclass.h"
@@ -68,6 +69,7 @@ DefineIndex(RangeVar *heapRelation,
List *attributeList,
bool unique,
bool primary,
+ bool isconstraint,
Expr *predicate,
List *rangetable)
{
@@ -208,7 +210,7 @@ DefineIndex(RangeVar *heapRelation,
index_create(relationId, indexRelationName,
indexInfo, accessMethodId, classObjectId,
- primary, allowSystemTableMods);
+ primary, isconstraint, allowSystemTableMods);
/*
* We update the relation's pg_class tuple even if it already has
@@ -566,6 +568,7 @@ RemoveIndex(RangeVar *relation, DropBehavior behavior)
{
Oid indOid;
HeapTuple tuple;
+ ObjectAddress object;
indOid = RangeVarGetRelid(relation, false);
tuple = SearchSysCache(RELOID,
@@ -580,7 +583,11 @@ RemoveIndex(RangeVar *relation, DropBehavior behavior)
ReleaseSysCache(tuple);
- index_drop(indOid);
+ object.classId = RelOid_pg_class;
+ object.objectId = indOid;
+ object.objectSubId = 0;
+
+ performDeletion(&object, behavior);
}
/*
diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c
index fcf96c5e9c1..1c4e5f3bee3 100644
--- a/src/backend/commands/operatorcmds.c
+++ b/src/backend/commands/operatorcmds.c
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/operatorcmds.c,v 1.4 2002/07/01 15:27:46 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/operatorcmds.c,v 1.5 2002/07/12 18:43:16 tgl Exp $
*
* DESCRIPTION
* The "DefineFoo" routines take the parse tree and pick out the
@@ -36,9 +36,9 @@
#include "access/heapam.h"
#include "catalog/catname.h"
+#include "catalog/dependency.h"
#include "catalog/namespace.h"
#include "catalog/pg_operator.h"
-#include "commands/comment.h"
#include "commands/defrem.h"
#include "miscadmin.h"
#include "parser/parse_oper.h"
@@ -217,17 +217,15 @@ RemoveOperator(RemoveOperStmt *stmt)
TypeName *typeName1 = (TypeName *) lfirst(stmt->args);
TypeName *typeName2 = (TypeName *) lsecond(stmt->args);
Oid operOid;
- Relation relation;
HeapTuple tup;
+ ObjectAddress object;
operOid = LookupOperNameTypeNames(operatorName, typeName1, typeName2,
"RemoveOperator");
- relation = heap_openr(OperatorRelationName, RowExclusiveLock);
-
- tup = SearchSysCacheCopy(OPEROID,
- ObjectIdGetDatum(operOid),
- 0, 0, 0);
+ tup = SearchSysCache(OPEROID,
+ ObjectIdGetDatum(operOid),
+ 0, 0, 0);
if (!HeapTupleIsValid(tup)) /* should not happen */
elog(ERROR, "RemoveOperator: failed to find tuple for operator '%s'",
NameListToString(operatorName));
@@ -238,11 +236,39 @@ RemoveOperator(RemoveOperStmt *stmt)
GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, NameListToString(operatorName));
- /* Delete any comments associated with this operator */
- DeleteComments(operOid, RelationGetRelid(relation));
+ ReleaseSysCache(tup);
+
+ /*
+ * Do the deletion
+ */
+ object.classId = get_system_catalog_relid(OperatorRelationName);
+ object.objectId = operOid;
+ object.objectSubId = 0;
+
+ performDeletion(&object, stmt->behavior);
+}
+
+/*
+ * Guts of operator deletion.
+ */
+void
+RemoveOperatorById(Oid operOid)
+{
+ Relation relation;
+ HeapTuple tup;
+
+ relation = heap_openr(OperatorRelationName, RowExclusiveLock);
+
+ tup = SearchSysCache(OPEROID,
+ ObjectIdGetDatum(operOid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tup)) /* should not happen */
+ elog(ERROR, "RemoveOperatorById: failed to find tuple for operator %u",
+ operOid);
simple_heap_delete(relation, &tup->t_self);
- heap_freetuple(tup);
+ ReleaseSysCache(tup);
+
heap_close(relation, RowExclusiveLock);
}
diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c
index 158927067f1..56dc320e2ef 100644
--- a/src/backend/commands/proclang.c
+++ b/src/backend/commands/proclang.c
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/proclang.c,v 1.34 2002/06/20 20:29:27 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/proclang.c,v 1.35 2002/07/12 18:43:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -17,6 +17,7 @@
#include "access/heapam.h"
#include "catalog/catname.h"
+#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/pg_language.h"
@@ -140,7 +141,7 @@ DropProceduralLanguage(DropPLangStmt *stmt)
{
char languageName[NAMEDATALEN];
HeapTuple langTup;
- Relation rel;
+ ObjectAddress object;
/*
* Check permission
@@ -155,11 +156,9 @@ DropProceduralLanguage(DropPLangStmt *stmt)
*/
case_translate_language_name(stmt->plname, languageName);
- rel = heap_openr(LanguageRelationName, RowExclusiveLock);
-
- langTup = SearchSysCacheCopy(LANGNAME,
- PointerGetDatum(languageName),
- 0, 0, 0);
+ langTup = SearchSysCache(LANGNAME,
+ CStringGetDatum(languageName),
+ 0, 0, 0);
if (!HeapTupleIsValid(langTup))
elog(ERROR, "Language %s doesn't exist", languageName);
@@ -167,8 +166,39 @@ DropProceduralLanguage(DropPLangStmt *stmt)
elog(ERROR, "Language %s isn't a created procedural language",
languageName);
+ object.classId = get_system_catalog_relid(LanguageRelationName);
+ object.objectId = langTup->t_data->t_oid;
+ object.objectSubId = 0;
+
+ ReleaseSysCache(langTup);
+
+ /*
+ * Do the deletion
+ */
+ performDeletion(&object, stmt->behavior);
+}
+
+/*
+ * Guts of language dropping.
+ */
+void
+DropProceduralLanguageById(Oid langOid)
+{
+ Relation rel;
+ HeapTuple langTup;
+
+ rel = heap_openr(LanguageRelationName, RowExclusiveLock);
+
+ langTup = SearchSysCache(LANGOID,
+ ObjectIdGetDatum(langOid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(langTup))
+ elog(ERROR, "DropProceduralLanguageById: language %u not found",
+ langOid);
+
simple_heap_delete(rel, &langTup->t_self);
- heap_freetuple(langTup);
+ ReleaseSysCache(langTup);
+
heap_close(rel, RowExclusiveLock);
}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 901090c70ac..0d4d277bae4 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.19 2002/07/06 20:16:35 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.20 2002/07/12 18:43:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -18,11 +18,13 @@
#include "access/tuptoaster.h"
#include "catalog/catalog.h"
#include "catalog/catname.h"
+#include "catalog/dependency.h"
#include "catalog/heap.h"
#include "catalog/index.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/pg_attrdef.h"
+#include "catalog/pg_constraint.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
@@ -36,6 +38,7 @@
#include "optimizer/clauses.h"
#include "optimizer/planmain.h"
#include "optimizer/prep.h"
+#include "parser/gramparse.h"
#include "parser/parse_coerce.h"
#include "parser/parse_expr.h"
#include "parser/parse_relation.h"
@@ -57,6 +60,13 @@ static void setRelhassubclassInRelation(Oid relationId, bool relhassubclass);
static void drop_default(Oid relid, int16 attnum);
static void CheckTupleType(Form_pg_class tuple_class);
static bool needs_toast_table(Relation rel);
+static void validateForeignKeyConstraint(FkConstraint *fkconstraint,
+ Relation rel, Relation pkrel);
+static Oid createForeignKeyConstraint(Relation rel, Relation pkrel,
+ FkConstraint *fkconstraint);
+static void createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
+ Oid constrOid);
+static char *fkMatchTypeToString(char match_type);
/* Used by attribute and relation renaming routines: */
@@ -147,6 +157,7 @@ DefineRelation(CreateStmt *stmt, char relkind)
ConstrCheck *check = (ConstrCheck *) palloc(length(old_constraints) *
sizeof(ConstrCheck));
int ncheck = 0;
+ int constr_name_ctr = 0;
foreach(listptr, old_constraints)
{
@@ -167,8 +178,16 @@ DefineRelation(CreateStmt *stmt, char relkind)
}
else
{
+ /*
+ * Generate a constraint name. NB: this should match the
+ * form of names that GenerateConstraintName() may produce
+ * for names added later. We are assured that there is
+ * no name conflict, because MergeAttributes() did not pass
+ * back any names of this form.
+ */
check[ncheck].ccname = (char *) palloc(NAMEDATALEN);
- snprintf(check[ncheck].ccname, NAMEDATALEN, "$%d", ncheck + 1);
+ snprintf(check[ncheck].ccname, NAMEDATALEN, "$%d",
+ ++constr_name_ctr);
}
Assert(cdef->raw_expr == NULL && cdef->cooked_expr != NULL);
check[ncheck].ccbin = pstrdup(cdef->cooked_expr);
@@ -262,21 +281,20 @@ DefineRelation(CreateStmt *stmt, char relkind)
/*
* RemoveRelation
* Deletes a relation.
- *
- * Exceptions:
- * BadArg if name is invalid.
- *
- * Note:
- * If the relation has indices defined on it, then the index relations
- * themselves will be destroyed, too.
*/
void
RemoveRelation(const RangeVar *relation, DropBehavior behavior)
{
Oid relOid;
+ ObjectAddress object;
relOid = RangeVarGetRelid(relation, false);
- heap_drop_with_catalog(relOid, allowSystemTableMods);
+
+ object.classId = RelOid_pg_class;
+ object.objectId = relOid;
+ object.objectSubId = 0;
+
+ performDeletion(&object, behavior);
}
/*
@@ -580,7 +598,13 @@ MergeAttributes(List *schema, List *supers, bool istemp,
Node *expr;
cdef->contype = CONSTR_CHECK;
- if (check[i].ccname[0] == '$')
+ /*
+ * Do not inherit generated constraint names, since they
+ * might conflict across multiple inheritance parents.
+ * (But conflicts between user-assigned names will cause
+ * an error.)
+ */
+ if (ConstraintNameIsGenerated(check[i].ccname))
cdef->name = NULL;
else
cdef->name = pstrdup(check[i].ccname);
@@ -684,7 +708,7 @@ MergeAttributes(List *schema, List *supers, bool istemp,
/*
* complementary static functions for MergeAttributes().
*
- * Varattnos of pg_relcheck.rcbin must be rewritten when subclasses inherit
+ * Varattnos of pg_constraint.conbin must be rewritten when subclasses inherit
* constraints from parent classes, since the inherited attributes could
* be given different column numbers in multiple-inheritance cases.
*
@@ -747,7 +771,8 @@ StoreCatalogInheritance(Oid relationId, List *supers)
return;
/*
- * Catalog INHERITS information using direct ancestors only.
+ * Store INHERITS information in pg_inherits using direct ancestors only.
+ * Also enter dependencies on the direct ancestors.
*/
relation = heap_openr(InheritsRelationName, RowExclusiveLock);
desc = RelationGetDescr(relation);
@@ -758,6 +783,8 @@ StoreCatalogInheritance(Oid relationId, List *supers)
Oid entryOid = lfirsti(entry);
Datum datum[Natts_pg_inherits];
char nullarr[Natts_pg_inherits];
+ ObjectAddress childobject,
+ parentobject;
datum[0] = ObjectIdGetDatum(relationId); /* inhrel */
datum[1] = ObjectIdGetDatum(entryOid); /* inhparent */
@@ -782,6 +809,18 @@ StoreCatalogInheritance(Oid relationId, List *supers)
heap_freetuple(tuple);
+ /*
+ * Store a dependency too
+ */
+ parentobject.classId = RelOid_pg_class;
+ parentobject.objectId = entryOid;
+ parentobject.objectSubId = 0;
+ childobject.classId = RelOid_pg_class;
+ childobject.objectId = relationId;
+ childobject.objectSubId = 0;
+
+ recordDependencyOn(&childobject, &parentobject, DEPENDENCY_NORMAL);
+
seqNumber += 1;
}
@@ -2299,6 +2338,7 @@ AlterTableAddConstraint(Oid myrelid,
{
Relation rel;
List *listptr;
+ int counter = 0;
/*
* Grab an exclusive lock on the target table, which we will NOT
@@ -2343,7 +2383,12 @@ AlterTableAddConstraint(Oid myrelid,
foreach(listptr, newConstraints)
{
- Node *newConstraint = lfirst(listptr);
+ /*
+ * copy is because we may destructively alter the node below
+ * by inserting a generated name; this name is not necessarily
+ * correct for children or parents.
+ */
+ Node *newConstraint = copyObject(lfirst(listptr));
switch (nodeTag(newConstraint))
{
@@ -2370,12 +2415,23 @@ AlterTableAddConstraint(Oid myrelid,
RangeTblEntry *rte;
List *qual;
Node *expr;
- char *name;
+ /*
+ * Assign or validate constraint name
+ */
if (constr->name)
- name = constr->name;
+ {
+ if (ConstraintNameIsUsed(RelationGetRelid(rel),
+ RelationGetNamespace(rel),
+ constr->name))
+ elog(ERROR, "constraint \"%s\" already exists for relation \"%s\"",
+ constr->name,
+ RelationGetRelationName(rel));
+ }
else
- name = "<unnamed>";
+ constr->name = GenerateConstraintName(RelationGetRelid(rel),
+ RelationGetNamespace(rel),
+ &counter);
/*
* We need to make a parse state and range
@@ -2458,7 +2514,8 @@ AlterTableAddConstraint(Oid myrelid,
pfree(slot);
if (!successful)
- elog(ERROR, "AlterTableAddConstraint: rejected due to CHECK constraint %s", name);
+ elog(ERROR, "AlterTableAddConstraint: rejected due to CHECK constraint %s",
+ constr->name);
/*
* Call AddRelationRawConstraints to do
@@ -2481,17 +2538,32 @@ AlterTableAddConstraint(Oid myrelid,
{
FkConstraint *fkconstraint = (FkConstraint *) newConstraint;
Relation pkrel;
- HeapScanDesc scan;
- HeapTuple tuple;
- Trigger trig;
- List *list;
- int count;
+ Oid constrOid;
+
+ /*
+ * Assign or validate constraint name
+ */
+ if (fkconstraint->constr_name)
+ {
+ if (ConstraintNameIsUsed(RelationGetRelid(rel),
+ RelationGetNamespace(rel),
+ fkconstraint->constr_name))
+ elog(ERROR, "constraint \"%s\" already exists for relation \"%s\"",
+ fkconstraint->constr_name,
+ RelationGetRelationName(rel));
+ }
+ else
+ fkconstraint->constr_name = GenerateConstraintName(RelationGetRelid(rel),
+ RelationGetNamespace(rel),
+ &counter);
/*
* Grab an exclusive lock on the pk table, so that
* someone doesn't delete rows out from under us.
- *
- * XXX wouldn't a lesser lock be sufficient?
+ * (Although a lesser lock would do for that purpose,
+ * we'll need exclusive lock anyway to add triggers
+ * to the pk table; trying to start with a lesser lock
+ * will just create a risk of deadlock.)
*/
pkrel = heap_openrv(fkconstraint->pktable,
AccessExclusiveLock);
@@ -2500,119 +2572,471 @@ AlterTableAddConstraint(Oid myrelid,
* Validity checks
*/
if (pkrel->rd_rel->relkind != RELKIND_RELATION)
- elog(ERROR, "referenced table \"%s\" not a relation",
- fkconstraint->pktable->relname);
+ elog(ERROR, "referenced relation \"%s\" is not a table",
+ RelationGetRelationName(pkrel));
+
+ if (!allowSystemTableMods
+ && IsSystemRelation(pkrel))
+ elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog",
+ RelationGetRelationName(pkrel));
+
+ /* XXX shouldn't there be a permission check too? */
if (isTempNamespace(RelationGetNamespace(pkrel)) &&
!isTempNamespace(RelationGetNamespace(rel)))
- elog(ERROR, "ALTER TABLE / ADD CONSTRAINT: Unable to reference temporary table from permanent table constraint.");
+ elog(ERROR, "ALTER TABLE / ADD CONSTRAINT: Unable to reference temporary table from permanent table constraint");
/*
- * First we check for limited correctness of the
- * constraint.
+ * Check that the constraint is satisfied by existing
+ * rows (we can skip this during table creation).
*
* NOTE: we assume parser has already checked for
* existence of an appropriate unique index on the
* referenced relation, and that the column datatypes
* are comparable.
- *
- * Scan through each tuple, calling RI_FKey_check_ins
- * (insert trigger) as if that tuple had just been
- * inserted. If any of those fail, it should
- * elog(ERROR) and that's that.
*/
- MemSet(&trig, 0, sizeof(trig));
- trig.tgoid = InvalidOid;
- if (fkconstraint->constr_name)
- trig.tgname = fkconstraint->constr_name;
- else
- trig.tgname = "<unknown>";
- trig.tgenabled = TRUE;
- trig.tgisconstraint = TRUE;
- trig.tgconstrrelid = RelationGetRelid(pkrel);
- trig.tgdeferrable = FALSE;
- trig.tginitdeferred = FALSE;
-
- trig.tgargs = (char **) palloc(
- sizeof(char *) * (4 + length(fkconstraint->fk_attrs)
- + length(fkconstraint->pk_attrs)));
-
- trig.tgargs[0] = trig.tgname;
- trig.tgargs[1] = RelationGetRelationName(rel);
- trig.tgargs[2] = RelationGetRelationName(pkrel);
- trig.tgargs[3] = fkconstraint->match_type;
- count = 4;
- foreach(list, fkconstraint->fk_attrs)
- {
- Ident *fk_at = lfirst(list);
+ if (!fkconstraint->skip_validation)
+ validateForeignKeyConstraint(fkconstraint, rel, pkrel);
- trig.tgargs[count] = fk_at->name;
- count += 2;
- }
- count = 5;
- foreach(list, fkconstraint->pk_attrs)
- {
- Ident *pk_at = lfirst(list);
+ /*
+ * Record the FK constraint in pg_constraint.
+ */
+ constrOid = createForeignKeyConstraint(rel, pkrel,
+ fkconstraint);
- trig.tgargs[count] = pk_at->name;
- count += 2;
- }
- trig.tgnargs = count - 1;
+ /*
+ * Create the triggers that will enforce the constraint.
+ */
+ createForeignKeyTriggers(rel, fkconstraint, constrOid);
- scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
+ /*
+ * Close pk table, but keep lock until we've committed.
+ */
+ heap_close(pkrel, NoLock);
- while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
- {
- /* Make a call to the check function */
+ break;
+ }
+ default:
+ elog(ERROR, "ALTER TABLE / ADD CONSTRAINT unable to determine type of constraint passed");
+ }
+ }
- /*
- * No parameters are passed, but we do set a
- * context
- */
- FunctionCallInfoData fcinfo;
- TriggerData trigdata;
+ /* Close rel, but keep lock till commit */
+ heap_close(rel, NoLock);
+}
- MemSet(&fcinfo, 0, sizeof(fcinfo));
+/*
+ * Scan the existing rows in a table to verify they meet a proposed FK
+ * constraint.
+ *
+ * Caller must have opened and locked both relations.
+ */
+static void
+validateForeignKeyConstraint(FkConstraint *fkconstraint,
+ Relation rel,
+ Relation pkrel)
+{
+ HeapScanDesc scan;
+ HeapTuple tuple;
+ Trigger trig;
+ List *list;
+ int count;
+
+ /*
+ * Scan through each tuple, calling RI_FKey_check_ins
+ * (insert trigger) as if that tuple had just been
+ * inserted. If any of those fail, it should
+ * elog(ERROR) and that's that.
+ */
+ MemSet(&trig, 0, sizeof(trig));
+ trig.tgoid = InvalidOid;
+ trig.tgname = fkconstraint->constr_name;
+ trig.tgenabled = TRUE;
+ trig.tgisconstraint = TRUE;
+ trig.tgconstrrelid = RelationGetRelid(pkrel);
+ trig.tgdeferrable = FALSE;
+ trig.tginitdeferred = FALSE;
+
+ trig.tgargs = (char **) palloc(sizeof(char *) *
+ (4 + length(fkconstraint->fk_attrs)
+ + length(fkconstraint->pk_attrs)));
+
+ trig.tgargs[0] = trig.tgname;
+ trig.tgargs[1] = RelationGetRelationName(rel);
+ trig.tgargs[2] = RelationGetRelationName(pkrel);
+ trig.tgargs[3] = fkMatchTypeToString(fkconstraint->fk_matchtype);
+ count = 4;
+ foreach(list, fkconstraint->fk_attrs)
+ {
+ Ident *fk_at = lfirst(list);
- /*
- * We assume RI_FKey_check_ins won't look at
- * flinfo...
- */
+ trig.tgargs[count] = fk_at->name;
+ count += 2;
+ }
+ count = 5;
+ foreach(list, fkconstraint->pk_attrs)
+ {
+ Ident *pk_at = lfirst(list);
- trigdata.type = T_TriggerData;
- trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
- trigdata.tg_relation = rel;
- trigdata.tg_trigtuple = tuple;
- trigdata.tg_newtuple = NULL;
- trigdata.tg_trigger = &trig;
+ trig.tgargs[count] = pk_at->name;
+ count += 2;
+ }
+ trig.tgnargs = count - 1;
- fcinfo.context = (Node *) &trigdata;
+ scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
- RI_FKey_check_ins(&fcinfo);
- }
- heap_endscan(scan);
+ while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ {
+ FunctionCallInfoData fcinfo;
+ TriggerData trigdata;
- pfree(trig.tgargs);
+ /*
+ * Make a call to the trigger function
+ *
+ * No parameters are passed, but we do set a context
+ */
+ MemSet(&fcinfo, 0, sizeof(fcinfo));
- heap_close(pkrel, NoLock);
+ /*
+ * We assume RI_FKey_check_ins won't look at flinfo...
+ */
+ trigdata.type = T_TriggerData;
+ trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
+ trigdata.tg_relation = rel;
+ trigdata.tg_trigtuple = tuple;
+ trigdata.tg_newtuple = NULL;
+ trigdata.tg_trigger = &trig;
- break;
- }
- default:
- elog(ERROR, "ALTER TABLE / ADD CONSTRAINT unable to determine type of constraint passed");
- }
+ fcinfo.context = (Node *) &trigdata;
+
+ RI_FKey_check_ins(&fcinfo);
}
- /* Close rel, but keep lock till commit */
- heap_close(rel, NoLock);
+ heap_endscan(scan);
+
+ pfree(trig.tgargs);
}
+/*
+ * Record an FK constraint in pg_constraint.
+ */
+static Oid
+createForeignKeyConstraint(Relation rel, Relation pkrel,
+ FkConstraint *fkconstraint)
+{
+ int16 *fkattr;
+ int16 *pkattr;
+ int fkcount;
+ int pkcount;
+ List *l;
+ int i;
+
+ /* Convert foreign-key attr names to attr number array */
+ fkcount = length(fkconstraint->fk_attrs);
+ fkattr = (int16 *) palloc(fkcount * sizeof(int16));
+ i = 0;
+ foreach(l, fkconstraint->fk_attrs)
+ {
+ Ident *id = (Ident *) lfirst(l);
+
+ fkattr[i++] = get_attnum(RelationGetRelid(rel), id->name);
+ }
+
+ /* The same for the referenced primary key attrs */
+ pkcount = length(fkconstraint->pk_attrs);
+ pkattr = (int16 *) palloc(pkcount * sizeof(int16));
+ i = 0;
+ foreach(l, fkconstraint->pk_attrs)
+ {
+ Ident *id = (Ident *) lfirst(l);
+
+ pkattr[i++] = get_attnum(RelationGetRelid(pkrel), id->name);
+ }
+
+ /* Now we can make the pg_constraint entry */
+ return CreateConstraintEntry(fkconstraint->constr_name,
+ RelationGetNamespace(rel),
+ CONSTRAINT_FOREIGN,
+ fkconstraint->deferrable,
+ fkconstraint->initdeferred,
+ RelationGetRelid(rel),
+ fkattr,
+ fkcount,
+ InvalidOid, /* not a domain constraint */
+ RelationGetRelid(pkrel),
+ pkattr,
+ pkcount,
+ fkconstraint->fk_upd_action,
+ fkconstraint->fk_del_action,
+ fkconstraint->fk_matchtype,
+ NULL,
+ NULL);
+}
+
+/*
+ * Create the triggers that implement an FK constraint.
+ */
+static void
+createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
+ Oid constrOid)
+{
+ RangeVar *myRel;
+ CreateTrigStmt *fk_trigger;
+ List *fk_attr;
+ List *pk_attr;
+ Ident *id;
+ ObjectAddress trigobj,
+ constrobj;
+
+ /*
+ * Reconstruct a RangeVar for my relation (not passed in, unfortunately).
+ */
+ myRel = makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
+ RelationGetRelationName(rel));
+
+ /*
+ * Preset objectAddress fields
+ */
+ constrobj.classId = get_system_catalog_relid(ConstraintRelationName);
+ constrobj.objectId = constrOid;
+ constrobj.objectSubId = 0;
+ trigobj.classId = get_system_catalog_relid(TriggerRelationName);
+ trigobj.objectSubId = 0;
+
+ /* Make changes-so-far visible */
+ CommandCounterIncrement();
+
+ /*
+ * Build and execute a CREATE CONSTRAINT TRIGGER statement for the
+ * CHECK action.
+ */
+ fk_trigger = makeNode(CreateTrigStmt);
+ fk_trigger->trigname = fkconstraint->constr_name;
+ fk_trigger->relation = myRel;
+ fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
+ fk_trigger->before = false;
+ fk_trigger->row = true;
+ fk_trigger->actions[0] = 'i';
+ fk_trigger->actions[1] = 'u';
+ fk_trigger->actions[2] = '\0';
+ fk_trigger->lang = NULL;
+ fk_trigger->text = NULL;
+
+ fk_trigger->attr = NIL;
+ fk_trigger->when = NULL;
+ fk_trigger->isconstraint = true;
+ fk_trigger->deferrable = fkconstraint->deferrable;
+ fk_trigger->initdeferred = fkconstraint->initdeferred;
+ fk_trigger->constrrel = fkconstraint->pktable;
+
+ fk_trigger->args = NIL;
+ fk_trigger->args = lappend(fk_trigger->args,
+ makeString(fkconstraint->constr_name));
+ fk_trigger->args = lappend(fk_trigger->args,
+ makeString(myRel->relname));
+ fk_trigger->args = lappend(fk_trigger->args,
+ makeString(fkconstraint->pktable->relname));
+ fk_trigger->args = lappend(fk_trigger->args,
+ makeString(fkMatchTypeToString(fkconstraint->fk_matchtype)));
+ fk_attr = fkconstraint->fk_attrs;
+ pk_attr = fkconstraint->pk_attrs;
+ if (length(fk_attr) != length(pk_attr))
+ elog(ERROR, "number of key attributes in referenced table must be equal to foreign key"
+ "\n\tIllegal FOREIGN KEY definition references \"%s\"",
+ fkconstraint->pktable->relname);
+
+ while (fk_attr != NIL)
+ {
+ id = (Ident *) lfirst(fk_attr);
+ fk_trigger->args = lappend(fk_trigger->args, makeString(id->name));
+
+ id = (Ident *) lfirst(pk_attr);
+ fk_trigger->args = lappend(fk_trigger->args, makeString(id->name));
+
+ fk_attr = lnext(fk_attr);
+ pk_attr = lnext(pk_attr);
+ }
+
+ trigobj.objectId = CreateTrigger(fk_trigger, true);
+
+ /* Register dependency from trigger to constraint */
+ recordDependencyOn(&trigobj, &constrobj, DEPENDENCY_INTERNAL);
+
+ /* Make changes-so-far visible */
+ CommandCounterIncrement();
+
+ /*
+ * Build and execute a CREATE CONSTRAINT TRIGGER statement for the
+ * ON DELETE action on the referenced table.
+ */
+ fk_trigger = makeNode(CreateTrigStmt);
+ fk_trigger->trigname = fkconstraint->constr_name;
+ fk_trigger->relation = fkconstraint->pktable;
+ fk_trigger->before = false;
+ fk_trigger->row = true;
+ fk_trigger->actions[0] = 'd';
+ fk_trigger->actions[1] = '\0';
+ fk_trigger->lang = NULL;
+ fk_trigger->text = NULL;
+
+ fk_trigger->attr = NIL;
+ fk_trigger->when = NULL;
+ fk_trigger->isconstraint = true;
+ fk_trigger->deferrable = fkconstraint->deferrable;
+ fk_trigger->initdeferred = fkconstraint->initdeferred;
+ fk_trigger->constrrel = myRel;
+ switch (fkconstraint->fk_del_action)
+ {
+ case FKCONSTR_ACTION_NOACTION:
+ fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
+ break;
+ case FKCONSTR_ACTION_RESTRICT:
+ fk_trigger->deferrable = false;
+ fk_trigger->initdeferred = false;
+ fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
+ break;
+ case FKCONSTR_ACTION_CASCADE:
+ fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
+ break;
+ case FKCONSTR_ACTION_SETNULL:
+ fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
+ break;
+ case FKCONSTR_ACTION_SETDEFAULT:
+ fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
+ break;
+ default:
+ elog(ERROR, "Unrecognized ON DELETE action for FOREIGN KEY constraint");
+ break;
+ }
+
+ fk_trigger->args = NIL;
+ fk_trigger->args = lappend(fk_trigger->args,
+ makeString(fkconstraint->constr_name));
+ fk_trigger->args = lappend(fk_trigger->args,
+ makeString(myRel->relname));
+ fk_trigger->args = lappend(fk_trigger->args,
+ makeString(fkconstraint->pktable->relname));
+ fk_trigger->args = lappend(fk_trigger->args,
+ makeString(fkMatchTypeToString(fkconstraint->fk_matchtype)));
+ fk_attr = fkconstraint->fk_attrs;
+ pk_attr = fkconstraint->pk_attrs;
+ while (fk_attr != NIL)
+ {
+ id = (Ident *) lfirst(fk_attr);
+ fk_trigger->args = lappend(fk_trigger->args, makeString(id->name));
+
+ id = (Ident *) lfirst(pk_attr);
+ fk_trigger->args = lappend(fk_trigger->args, makeString(id->name));
+
+ fk_attr = lnext(fk_attr);
+ pk_attr = lnext(pk_attr);
+ }
+
+ trigobj.objectId = CreateTrigger(fk_trigger, true);
+
+ /* Register dependency from trigger to constraint */
+ recordDependencyOn(&trigobj, &constrobj, DEPENDENCY_INTERNAL);
+
+ /* Make changes-so-far visible */
+ CommandCounterIncrement();
+
+ /*
+ * Build and execute a CREATE CONSTRAINT TRIGGER statement for the
+ * ON UPDATE action on the referenced table.
+ */
+ fk_trigger = makeNode(CreateTrigStmt);
+ fk_trigger->trigname = fkconstraint->constr_name;
+ fk_trigger->relation = fkconstraint->pktable;
+ fk_trigger->before = false;
+ fk_trigger->row = true;
+ fk_trigger->actions[0] = 'u';
+ fk_trigger->actions[1] = '\0';
+ fk_trigger->lang = NULL;
+ fk_trigger->text = NULL;
+
+ fk_trigger->attr = NIL;
+ fk_trigger->when = NULL;
+ fk_trigger->isconstraint = true;
+ fk_trigger->deferrable = fkconstraint->deferrable;
+ fk_trigger->initdeferred = fkconstraint->initdeferred;
+ fk_trigger->constrrel = myRel;
+ switch (fkconstraint->fk_upd_action)
+ {
+ case FKCONSTR_ACTION_NOACTION:
+ fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
+ break;
+ case FKCONSTR_ACTION_RESTRICT:
+ fk_trigger->deferrable = false;
+ fk_trigger->initdeferred = false;
+ fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
+ break;
+ case FKCONSTR_ACTION_CASCADE:
+ fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
+ break;
+ case FKCONSTR_ACTION_SETNULL:
+ fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
+ break;
+ case FKCONSTR_ACTION_SETDEFAULT:
+ fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
+ break;
+ default:
+ elog(ERROR, "Unrecognized ON UPDATE action for FOREIGN KEY constraint");
+ break;
+ }
+
+ fk_trigger->args = NIL;
+ fk_trigger->args = lappend(fk_trigger->args,
+ makeString(fkconstraint->constr_name));
+ fk_trigger->args = lappend(fk_trigger->args,
+ makeString(myRel->relname));
+ fk_trigger->args = lappend(fk_trigger->args,
+ makeString(fkconstraint->pktable->relname));
+ fk_trigger->args = lappend(fk_trigger->args,
+ makeString(fkMatchTypeToString(fkconstraint->fk_matchtype)));
+ fk_attr = fkconstraint->fk_attrs;
+ pk_attr = fkconstraint->pk_attrs;
+ while (fk_attr != NIL)
+ {
+ id = (Ident *) lfirst(fk_attr);
+ fk_trigger->args = lappend(fk_trigger->args, makeString(id->name));
+
+ id = (Ident *) lfirst(pk_attr);
+ fk_trigger->args = lappend(fk_trigger->args, makeString(id->name));
+
+ fk_attr = lnext(fk_attr);
+ pk_attr = lnext(pk_attr);
+ }
+
+ trigobj.objectId = CreateTrigger(fk_trigger, true);
+
+ /* Register dependency from trigger to constraint */
+ recordDependencyOn(&trigobj, &constrobj, DEPENDENCY_INTERNAL);
+}
+
+/*
+ * fkMatchTypeToString -
+ * convert FKCONSTR_MATCH_xxx code to string to use in trigger args
+ */
+static char *
+fkMatchTypeToString(char match_type)
+{
+ switch (match_type)
+ {
+ case FKCONSTR_MATCH_FULL:
+ return pstrdup("FULL");
+ case FKCONSTR_MATCH_PARTIAL:
+ return pstrdup("PARTIAL");
+ case FKCONSTR_MATCH_UNSPECIFIED:
+ return pstrdup("UNSPECIFIED");
+ default:
+ elog(ERROR, "fkMatchTypeToString: Unknown MATCH TYPE '%c'",
+ match_type);
+ }
+ return NULL; /* can't get here */
+}
/*
* ALTER TABLE DROP CONSTRAINT
- * Note: It is legal to remove a constraint with name "" as it is possible
- * to add a constraint with name "".
- * Christopher Kings-Lynne
*/
void
AlterTableDropConstraint(Oid myrelid,
@@ -2620,14 +3044,7 @@ AlterTableDropConstraint(Oid myrelid,
DropBehavior behavior)
{
Relation rel;
- int deleted;
-
- /*
- * We don't support CASCADE yet - in fact, RESTRICT doesn't work to
- * the spec either!
- */
- if (behavior == DROP_CASCADE)
- elog(ERROR, "ALTER TABLE / DROP CONSTRAINT does not support the CASCADE keyword");
+ int deleted = 0;
/*
* Acquire an exclusive lock on the target relation for the duration
@@ -2649,26 +3066,39 @@ AlterTableDropConstraint(Oid myrelid,
aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(rel));
/*
- * Since all we have is the name of the constraint, we have to look
- * through all catalogs that could possibly contain a constraint for
- * this relation. We also keep a count of the number of constraints
- * removed.
+ * Process child tables if requested.
*/
+ if (inh)
+ {
+ List *child,
+ *children;
- deleted = 0;
+ /* This routine is actually in the planner */
+ children = find_all_inheritors(myrelid);
- /*
- * First, we remove all CHECK constraints with the given name
- */
+ /*
+ * find_all_inheritors does the recursive search of the
+ * inheritance hierarchy, so all we have to do is process all of
+ * the relids in the list that it returns.
+ */
+ foreach(child, children)
+ {
+ Oid childrelid = lfirsti(child);
+ Relation inhrel;
- deleted += RemoveCheckConstraint(rel, constrName, inh);
+ if (childrelid == myrelid)
+ continue;
+ inhrel = heap_open(childrelid, AccessExclusiveLock);
+ /* do NOT count child constraints in deleted. */
+ RemoveRelConstraints(inhrel, constrName, behavior);
+ heap_close(inhrel, NoLock);
+ }
+ }
/*
- * Now we remove NULL, UNIQUE, PRIMARY KEY and FOREIGN KEY
- * constraints.
- *
- * Unimplemented.
+ * Now do the thing on this relation.
*/
+ deleted += RemoveRelConstraints(rel, constrName, behavior);
/* Close the target relation */
heap_close(rel, NoLock);
@@ -2797,6 +3227,8 @@ AlterTableCreateToastTable(Oid relOid, bool silent)
char toast_idxname[NAMEDATALEN];
IndexInfo *indexInfo;
Oid classObjectId[2];
+ ObjectAddress baseobject,
+ toastobject;
/*
* Grab an exclusive lock on the target table, which we will NOT
@@ -2957,7 +3389,7 @@ AlterTableCreateToastTable(Oid relOid, bool silent)
toast_idxid = index_create(toast_relid, toast_idxname, indexInfo,
BTREE_AM_OID, classObjectId,
- true, true);
+ true, false, true);
/*
* Update toast rel's pg_class entry to show that it has an index. The
@@ -2982,6 +3414,19 @@ AlterTableCreateToastTable(Oid relOid, bool silent)
heap_freetuple(reltup);
/*
+ * Register dependency from the toast table to the master, so that
+ * the toast table will be deleted if the master is.
+ */
+ baseobject.classId = RelOid_pg_class;
+ baseobject.objectId = relOid;
+ baseobject.objectSubId = 0;
+ toastobject.classId = RelOid_pg_class;
+ toastobject.objectId = toast_relid;
+ toastobject.objectSubId = 0;
+
+ recordDependencyOn(&toastobject, &baseobject, DEPENDENCY_INTERNAL);
+
+ /*
* Close relations and make changes visible
*/
heap_close(class_rel, NoLock);
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 18484372ed5..b27dd0f4380 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.120 2002/06/20 20:29:27 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.121 2002/07/12 18:43:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -17,12 +17,12 @@
#include "access/heapam.h"
#include "catalog/catalog.h"
#include "catalog/catname.h"
+#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/pg_language.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_trigger.h"
-#include "commands/comment.h"
#include "commands/trigger.h"
#include "executor/executor.h"
#include "miscadmin.h"
@@ -50,8 +50,8 @@ static void DeferredTriggerExecute(DeferredTriggerEvent event, int itemno,
MemoryContext per_tuple_context);
-void
-CreateTrigger(CreateTrigStmt *stmt)
+Oid
+CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
{
int16 tgtype;
int16 tgattr[FUNC_MAX_ARGS];
@@ -69,11 +69,15 @@ CreateTrigger(CreateTrigStmt *stmt)
Oid fargtypes[FUNC_MAX_ARGS];
Oid funcoid;
Oid funclang;
+ Oid trigoid;
int found = 0;
int i;
char constrtrigname[NAMEDATALEN];
- char *constrname = "";
- Oid constrrelid = InvalidOid;
+ char *trigname;
+ char *constrname;
+ Oid constrrelid;
+ ObjectAddress myself,
+ referenced;
rel = heap_openrv(stmt->relation, AccessExclusiveLock);
@@ -91,21 +95,28 @@ CreateTrigger(CreateTrigStmt *stmt)
aclcheck_error(aclresult, RelationGetRelationName(rel));
/*
- * If trigger is an RI constraint, use trigger name as constraint name
- * and build a unique trigger name instead.
+ * If trigger is an RI constraint, use specified trigger name as
+ * constraint name and build a unique trigger name instead.
+ * This is mainly for backwards compatibility with CREATE CONSTRAINT
+ * TRIGGER commands.
*/
if (stmt->isconstraint)
{
- constrname = stmt->trigname;
snprintf(constrtrigname, sizeof(constrtrigname),
"RI_ConstraintTrigger_%u", newoid());
- stmt->trigname = constrtrigname;
-
- if (stmt->constrrel != NULL)
- constrrelid = RangeVarGetRelid(stmt->constrrel, false);
- else
- constrrelid = InvalidOid;
+ trigname = constrtrigname;
+ constrname = stmt->trigname;
}
+ else
+ {
+ trigname = stmt->trigname;
+ constrname = "";
+ }
+
+ if (stmt->constrrel != NULL)
+ constrrelid = RangeVarGetRelid(stmt->constrrel, false);
+ else
+ constrrelid = InvalidOid;
TRIGGER_CLEAR_TYPE(tgtype);
if (stmt->before)
@@ -160,9 +171,9 @@ CreateTrigger(CreateTrigStmt *stmt)
{
Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple);
- if (namestrcmp(&(pg_trigger->tgname), stmt->trigname) == 0)
+ if (namestrcmp(&(pg_trigger->tgname), trigname) == 0)
elog(ERROR, "CreateTrigger: trigger %s already defined on relation %s",
- stmt->trigname, stmt->relation->relname);
+ trigname, stmt->relation->relname);
found++;
}
systable_endscan(tgscan);
@@ -209,12 +220,13 @@ CreateTrigger(CreateTrigStmt *stmt)
values[Anum_pg_trigger_tgrelid - 1] = ObjectIdGetDatum(RelationGetRelid(rel));
values[Anum_pg_trigger_tgname - 1] = DirectFunctionCall1(namein,
- CStringGetDatum(stmt->trigname));
+ CStringGetDatum(trigname));
values[Anum_pg_trigger_tgfoid - 1] = ObjectIdGetDatum(funcoid);
values[Anum_pg_trigger_tgtype - 1] = Int16GetDatum(tgtype);
values[Anum_pg_trigger_tgenabled - 1] = BoolGetDatum(true);
values[Anum_pg_trigger_tgisconstraint - 1] = BoolGetDatum(stmt->isconstraint);
- values[Anum_pg_trigger_tgconstrname - 1] = PointerGetDatum(constrname);
+ values[Anum_pg_trigger_tgconstrname - 1] = DirectFunctionCall1(namein,
+ CStringGetDatum(constrname));
values[Anum_pg_trigger_tgconstrrelid - 1] = ObjectIdGetDatum(constrrelid);
values[Anum_pg_trigger_tgdeferrable - 1] = BoolGetDatum(stmt->deferrable);
values[Anum_pg_trigger_tginitdeferred - 1] = BoolGetDatum(stmt->initdeferred);
@@ -270,10 +282,16 @@ CreateTrigger(CreateTrigStmt *stmt)
/*
* Insert tuple into pg_trigger.
*/
- simple_heap_insert(tgrel, tuple);
+ trigoid = simple_heap_insert(tgrel, tuple);
+
CatalogOpenIndices(Num_pg_trigger_indices, Name_pg_trigger_indices, idescs);
CatalogIndexInsert(idescs, Num_pg_trigger_indices, tgrel, tuple);
CatalogCloseIndices(Num_pg_trigger_indices, idescs);
+
+ myself.classId = RelationGetRelid(tgrel);
+ myself.objectId = trigoid;
+ myself.objectSubId = 0;
+
heap_freetuple(tuple);
heap_close(tgrel, RowExclusiveLock);
@@ -294,10 +312,13 @@ CreateTrigger(CreateTrigStmt *stmt)
stmt->relation->relname);
((Form_pg_class) GETSTRUCT(tuple))->reltriggers = found + 1;
+
simple_heap_update(pgrel, &tuple->t_self, tuple);
+
CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs);
CatalogIndexInsert(ridescs, Num_pg_class_indices, pgrel, tuple);
CatalogCloseIndices(Num_pg_class_indices, ridescs);
+
heap_freetuple(tuple);
heap_close(pgrel, RowExclusiveLock);
@@ -307,25 +328,129 @@ CreateTrigger(CreateTrigStmt *stmt)
* upcoming CommandCounterIncrement...
*/
+ /*
+ * Record dependencies for trigger. Always place a normal dependency
+ * on the function. If we are doing this in response to an explicit
+ * CREATE TRIGGER command, also make trigger be auto-dropped if its
+ * relation is dropped or if the FK relation is dropped. (Auto drop
+ * is compatible with our pre-7.3 behavior.) If the trigger is being
+ * made for a constraint, we can skip the relation links; the dependency
+ * on the constraint will indirectly depend on the relations.
+ */
+ referenced.classId = RelOid_pg_proc;
+ referenced.objectId = funcoid;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+ if (!forConstraint)
+ {
+ referenced.classId = RelOid_pg_class;
+ referenced.objectId = RelationGetRelid(rel);
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
+ if (constrrelid != InvalidOid)
+ {
+ referenced.classId = RelOid_pg_class;
+ referenced.objectId = constrrelid;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
+ }
+ }
+
/* Keep lock on target rel until end of xact */
heap_close(rel, NoLock);
+
+ return trigoid;
}
/*
* DropTrigger - drop an individual trigger by name
*/
void
-DropTrigger(Oid relid, const char *trigname)
+DropTrigger(Oid relid, const char *trigname, DropBehavior behavior)
+{
+ Relation tgrel;
+ ScanKeyData skey[2];
+ SysScanDesc tgscan;
+ HeapTuple tup;
+ ObjectAddress object;
+
+ /*
+ * Find the trigger, verify permissions, set up object address
+ */
+ tgrel = heap_openr(TriggerRelationName, AccessShareLock);
+
+ ScanKeyEntryInitialize(&skey[0], 0x0,
+ Anum_pg_trigger_tgrelid, F_OIDEQ,
+ ObjectIdGetDatum(relid));
+
+ ScanKeyEntryInitialize(&skey[1], 0x0,
+ Anum_pg_trigger_tgname, F_NAMEEQ,
+ CStringGetDatum(trigname));
+
+ tgscan = systable_beginscan(tgrel, TriggerRelidNameIndex, true,
+ SnapshotNow, 2, skey);
+
+ tup = systable_getnext(tgscan);
+
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "DropTrigger: there is no trigger %s on relation %s",
+ trigname, get_rel_name(relid));
+
+ if (!pg_class_ownercheck(relid, GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, get_rel_name(relid));
+
+ object.classId = RelationGetRelid(tgrel);
+ object.objectId = tup->t_data->t_oid;
+ object.objectSubId = 0;
+
+ systable_endscan(tgscan);
+ heap_close(tgrel, AccessShareLock);
+
+ /*
+ * Do the deletion
+ */
+ performDeletion(&object, behavior);
+}
+
+/*
+ * Guts of trigger deletion.
+ */
+void
+RemoveTriggerById(Oid trigOid)
{
- Relation rel;
Relation tgrel;
SysScanDesc tgscan;
- ScanKeyData key;
+ ScanKeyData skey[1];
+ HeapTuple tup;
+ Oid relid;
+ Relation rel;
Relation pgrel;
HeapTuple tuple;
+ Form_pg_class classForm;
Relation ridescs[Num_pg_class_indices];
- int remaining = 0;
- int found = 0;
+
+ tgrel = heap_openr(TriggerRelationName, RowExclusiveLock);
+
+ /*
+ * Find the trigger to delete.
+ */
+ ScanKeyEntryInitialize(&skey[0], 0x0,
+ ObjectIdAttributeNumber, F_OIDEQ,
+ ObjectIdGetDatum(trigOid));
+
+ tgscan = systable_beginscan(tgrel, TriggerOidIndex, true,
+ SnapshotNow, 1, skey);
+
+ tup = systable_getnext(tgscan);
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "RemoveTriggerById: Trigger %u does not exist",
+ trigOid);
+
+ /*
+ * Open and exclusive-lock the relation the trigger belongs to.
+ */
+ relid = ((Form_pg_trigger) GETSTRUCT(tup))->tgrelid;
rel = heap_open(relid, AccessExclusiveLock);
@@ -337,55 +462,22 @@ DropTrigger(Oid relid, const char *trigname)
elog(ERROR, "DropTrigger: can't drop trigger for system relation %s",
RelationGetRelationName(rel));
- if (!pg_class_ownercheck(relid, GetUserId()))
- aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(rel));
-
/*
- * Search pg_trigger, delete target trigger, count remaining triggers
- * for relation. (Although we could fetch and delete the target
- * trigger directly, we'd still have to scan the remaining triggers,
- * so we may as well do both in one indexscan.)
- *
- * Note this is OK only because we have AccessExclusiveLock on the rel,
- * so no one else is creating/deleting triggers on this rel at the same
- * time.
+ * Delete the pg_trigger tuple.
*/
- tgrel = heap_openr(TriggerRelationName, RowExclusiveLock);
- ScanKeyEntryInitialize(&key, 0,
- Anum_pg_trigger_tgrelid,
- F_OIDEQ,
- ObjectIdGetDatum(relid));
- tgscan = systable_beginscan(tgrel, TriggerRelidNameIndex, true,
- SnapshotNow, 1, &key);
- while (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
- {
- Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple);
+ simple_heap_delete(tgrel, &tup->t_self);
- if (namestrcmp(&(pg_trigger->tgname), trigname) == 0)
- {
- /* Delete any comments associated with this trigger */
- DeleteComments(tuple->t_data->t_oid, RelationGetRelid(tgrel));
-
- simple_heap_delete(tgrel, &tuple->t_self);
- found++;
- }
- else
- remaining++;
- }
systable_endscan(tgscan);
heap_close(tgrel, RowExclusiveLock);
- if (found == 0)
- elog(ERROR, "DropTrigger: there is no trigger %s on relation %s",
- trigname, RelationGetRelationName(rel));
- if (found > 1) /* shouldn't happen */
- elog(NOTICE, "DropTrigger: found (and deleted) %d triggers %s on relation %s",
- found, trigname, RelationGetRelationName(rel));
-
/*
* Update relation's pg_class entry. Crucial side-effect: other
* backends (and this one too!) are sent SI message to make them
* rebuild relcache entries.
+ *
+ * Note this is OK only because we have AccessExclusiveLock on the rel,
+ * so no one else is creating/deleting triggers on this rel at the same
+ * time.
*/
pgrel = heap_openr(RelationRelationName, RowExclusiveLock);
tuple = SearchSysCacheCopy(RELOID,
@@ -394,116 +486,28 @@ DropTrigger(Oid relid, const char *trigname)
if (!HeapTupleIsValid(tuple))
elog(ERROR, "DropTrigger: relation %s not found in pg_class",
RelationGetRelationName(rel));
+ classForm = (Form_pg_class) GETSTRUCT(tuple);
+
+ if (classForm->reltriggers == 0)
+ elog(ERROR, "DropTrigger: relation %s has reltriggers = 0",
+ RelationGetRelationName(rel));
+ classForm->reltriggers--;
- ((Form_pg_class) GETSTRUCT(tuple))->reltriggers = remaining;
simple_heap_update(pgrel, &tuple->t_self, tuple);
+
CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs);
CatalogIndexInsert(ridescs, Num_pg_class_indices, pgrel, tuple);
CatalogCloseIndices(Num_pg_class_indices, ridescs);
+
heap_freetuple(tuple);
+
heap_close(pgrel, RowExclusiveLock);
- /* Keep lock on target rel until end of xact */
+ /* Keep lock on trigger's rel until end of xact */
heap_close(rel, NoLock);
}
/*
- * Remove all triggers for a relation that's being deleted.
- */
-void
-RelationRemoveTriggers(Relation rel)
-{
- Relation tgrel;
- SysScanDesc tgscan;
- ScanKeyData key;
- HeapTuple tup;
- bool found = false;
-
- tgrel = heap_openr(TriggerRelationName, RowExclusiveLock);
- ScanKeyEntryInitialize(&key, 0,
- Anum_pg_trigger_tgrelid,
- F_OIDEQ,
- ObjectIdGetDatum(RelationGetRelid(rel)));
- tgscan = systable_beginscan(tgrel, TriggerRelidNameIndex, true,
- SnapshotNow, 1, &key);
-
- while (HeapTupleIsValid(tup = systable_getnext(tgscan)))
- {
- /* Delete any comments associated with this trigger */
- DeleteComments(tup->t_data->t_oid, RelationGetRelid(tgrel));
-
- simple_heap_delete(tgrel, &tup->t_self);
-
- found = true;
- }
-
- systable_endscan(tgscan);
-
- /*
- * If we deleted any triggers, must update pg_class entry and advance
- * command counter to make the updated entry visible. This is fairly
- * annoying, since we'e just going to drop the durn thing later, but
- * it's necessary to have a consistent state in case we do
- * CommandCounterIncrement() below --- if RelationBuildTriggers()
- * runs, it will complain otherwise. Perhaps RelationBuildTriggers()
- * shouldn't be so picky...
- */
- if (found)
- {
- Relation pgrel;
- Relation ridescs[Num_pg_class_indices];
-
- pgrel = heap_openr(RelationRelationName, RowExclusiveLock);
- tup = SearchSysCacheCopy(RELOID,
- ObjectIdGetDatum(RelationGetRelid(rel)),
- 0, 0, 0);
- if (!HeapTupleIsValid(tup))
- elog(ERROR, "RelationRemoveTriggers: relation %u not found in pg_class",
- RelationGetRelid(rel));
-
- ((Form_pg_class) GETSTRUCT(tup))->reltriggers = 0;
- simple_heap_update(pgrel, &tup->t_self, tup);
- CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs);
- CatalogIndexInsert(ridescs, Num_pg_class_indices, pgrel, tup);
- CatalogCloseIndices(Num_pg_class_indices, ridescs);
- heap_freetuple(tup);
- heap_close(pgrel, RowExclusiveLock);
- CommandCounterIncrement();
- }
-
- /*
- * Also drop all constraint triggers referencing this relation
- */
- ScanKeyEntryInitialize(&key, 0,
- Anum_pg_trigger_tgconstrrelid,
- F_OIDEQ,
- ObjectIdGetDatum(RelationGetRelid(rel)));
- tgscan = systable_beginscan(tgrel, TriggerConstrRelidIndex, true,
- SnapshotNow, 1, &key);
-
- while (HeapTupleIsValid(tup = systable_getnext(tgscan)))
- {
- Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tup);
-
- elog(NOTICE, "DROP TABLE implicitly drops referential integrity trigger from table \"%s\"",
- get_rel_name(pg_trigger->tgrelid));
-
- DropTrigger(pg_trigger->tgrelid, NameStr(pg_trigger->tgname));
-
- /*
- * Need to do a command counter increment here to show up new
- * pg_class.reltriggers in the next loop iteration (in case there
- * are multiple referential integrity action triggers for the same
- * FK table defined on the PK table).
- */
- CommandCounterIncrement();
- }
- systable_endscan(tgscan);
-
- heap_close(tgrel, RowExclusiveLock);
-}
-
-/*
* renametrig - changes the name of a trigger on a relation
*
* trigger name is changed in trigger catalog.
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index a9b46855810..f148ff65891 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.4 2002/07/01 15:27:48 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.5 2002/07/12 18:43:16 tgl Exp $
*
* DESCRIPTION
* The "DefineFoo" routines take the parse tree and pick out the
@@ -33,10 +33,10 @@
#include "access/heapam.h"
#include "catalog/catname.h"
+#include "catalog/dependency.h"
#include "catalog/heap.h"
#include "catalog/namespace.h"
#include "catalog/pg_type.h"
-#include "commands/comment.h"
#include "commands/defrem.h"
#include "miscadmin.h"
#include "parser/parse_func.h"
@@ -262,17 +262,14 @@ DefineType(List *names, List *parameters)
/*
* RemoveType
* Removes a datatype.
- *
- * NOTE: since this tries to remove the associated array type too, it'll
- * only work on scalar types.
*/
void
RemoveType(List *names, DropBehavior behavior)
{
TypeName *typename;
- Relation relation;
Oid typeoid;
HeapTuple tup;
+ ObjectAddress object;
/* Make a TypeName so we can use standard type lookup machinery */
typename = makeNode(TypeName);
@@ -280,8 +277,6 @@ RemoveType(List *names, DropBehavior behavior)
typename->typmod = -1;
typename->arrayBounds = NIL;
- relation = heap_openr(TypeRelationName, RowExclusiveLock);
-
/* Use LookupTypeName here so that shell types can be removed. */
typeoid = LookupTypeName(typename);
if (!OidIsValid(typeoid))
@@ -301,30 +296,36 @@ RemoveType(List *names, DropBehavior behavior)
GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, TypeNameToString(typename));
- /* Delete any comments associated with this type */
- DeleteComments(typeoid, RelationGetRelid(relation));
+ ReleaseSysCache(tup);
- /* Remove the type tuple from pg_type */
- simple_heap_delete(relation, &tup->t_self);
+ /*
+ * Do the deletion
+ */
+ object.classId = RelOid_pg_type;
+ object.objectId = typeoid;
+ object.objectSubId = 0;
- ReleaseSysCache(tup);
+ performDeletion(&object, behavior);
+}
- /* Now, delete the "array of" that type */
- typename->arrayBounds = makeList1(makeInteger(1));
- typeoid = LookupTypeName(typename);
- if (!OidIsValid(typeoid))
- elog(ERROR, "Type \"%s\" does not exist",
- TypeNameToString(typename));
+/*
+ * Guts of type deletion.
+ */
+void
+RemoveTypeById(Oid typeOid)
+{
+ Relation relation;
+ HeapTuple tup;
+
+ relation = heap_openr(TypeRelationName, RowExclusiveLock);
tup = SearchSysCache(TYPEOID,
- ObjectIdGetDatum(typeoid),
+ ObjectIdGetDatum(typeOid),
0, 0, 0);
if (!HeapTupleIsValid(tup))
- elog(ERROR, "Type \"%s\" does not exist",
- TypeNameToString(typename));
-
- DeleteComments(typeoid, RelationGetRelid(relation));
+ elog(ERROR, "RemoveTypeById: type %u not found",
+ typeOid);
simple_heap_delete(relation, &tup->t_self);
@@ -365,6 +366,8 @@ DefineDomain(CreateDomainStmt *stmt)
HeapTuple typeTup;
List *schema = stmt->constraints;
List *listptr;
+ Oid basetypeoid;
+ Form_pg_type baseType;
/* Convert list of names to a name and namespace */
domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname,
@@ -389,40 +392,43 @@ DefineDomain(CreateDomainStmt *stmt)
*/
typeTup = typenameType(stmt->typename);
+ baseType = (Form_pg_type) GETSTRUCT(typeTup);
+ basetypeoid = typeTup->t_data->t_oid;
+
/*
* What we really don't want is domains of domains. This could cause all sorts
* of neat issues if we allow that.
*
* With testing, we may determine complex types should be allowed
*/
- typtype = ((Form_pg_type) GETSTRUCT(typeTup))->typtype;
+ typtype = baseType->typtype;
if (typtype != 'b')
elog(ERROR, "DefineDomain: %s is not a basetype",
TypeNameToString(stmt->typename));
/* passed by value */
- byValue = ((Form_pg_type) GETSTRUCT(typeTup))->typbyval;
+ byValue = baseType->typbyval;
/* Required Alignment */
- alignment = ((Form_pg_type) GETSTRUCT(typeTup))->typalign;
+ alignment = baseType->typalign;
/* TOAST Strategy */
- storage = ((Form_pg_type) GETSTRUCT(typeTup))->typstorage;
+ storage = baseType->typstorage;
/* Storage Length */
- internalLength = ((Form_pg_type) GETSTRUCT(typeTup))->typlen;
+ internalLength = baseType->typlen;
/* External Length (unused) */
- externalLength = ((Form_pg_type) GETSTRUCT(typeTup))->typprtlen;
+ externalLength = baseType->typprtlen;
/* Array element Delimiter */
- delimiter = ((Form_pg_type) GETSTRUCT(typeTup))->typdelim;
+ delimiter = baseType->typdelim;
/* I/O Functions */
- inputProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typinput;
- outputProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typoutput;
- receiveProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typreceive;
- sendProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typsend;
+ inputProcedure = baseType->typinput;
+ outputProcedure = baseType->typoutput;
+ receiveProcedure = baseType->typreceive;
+ sendProcedure = baseType->typsend;
/* Inherited default value */
datum = SysCacheGetAttr(TYPEOID, typeTup,
@@ -441,7 +447,7 @@ DefineDomain(CreateDomainStmt *stmt)
*
* This is what enables us to make a domain of an array
*/
- basetypelem = ((Form_pg_type) GETSTRUCT(typeTup))->typelem;
+ basetypelem = baseType->typelem;
/*
* Run through constraints manually to avoid the additional
@@ -474,7 +480,7 @@ DefineDomain(CreateDomainStmt *stmt)
* Note: Name is strictly for error message
*/
expr = cookDefault(pstate, colDef->raw_expr,
- typeTup->t_data->t_oid,
+ basetypeoid,
stmt->typename->typmod,
domainName);
/*
@@ -540,7 +546,7 @@ DefineDomain(CreateDomainStmt *stmt)
*/
TypeCreate(domainName, /* type name */
domainNamespace, /* namespace */
- InvalidOid, /* preassigned type oid (not done here) */
+ InvalidOid, /* preassigned type oid (none here) */
InvalidOid, /* relation oid (n/a here) */
internalLength, /* internal size */
externalLength, /* external size */
@@ -551,7 +557,7 @@ DefineDomain(CreateDomainStmt *stmt)
receiveProcedure, /* receive procedure */
sendProcedure, /* send procedure */
basetypelem, /* element type ID */
- typeTup->t_data->t_oid, /* base type ID */
+ basetypeoid, /* base type ID */
defaultValue, /* default type value (text) */
defaultValueBin, /* default type value (binary) */
byValue, /* passed by value */
@@ -571,19 +577,17 @@ DefineDomain(CreateDomainStmt *stmt)
/*
* RemoveDomain
* Removes a domain.
+ *
+ * This is identical to RemoveType except we insist it be a domain.
*/
void
RemoveDomain(List *names, DropBehavior behavior)
{
TypeName *typename;
- Relation relation;
Oid typeoid;
HeapTuple tup;
char typtype;
-
- /* CASCADE unsupported */
- if (behavior == DROP_CASCADE)
- elog(ERROR, "DROP DOMAIN does not support the CASCADE keyword");
+ ObjectAddress object;
/* Make a TypeName so we can use standard type lookup machinery */
typename = makeNode(TypeName);
@@ -591,15 +595,17 @@ RemoveDomain(List *names, DropBehavior behavior)
typename->typmod = -1;
typename->arrayBounds = NIL;
- relation = heap_openr(TypeRelationName, RowExclusiveLock);
-
- typeoid = typenameTypeId(typename);
+ /* Use LookupTypeName here so that shell types can be removed. */
+ typeoid = LookupTypeName(typename);
+ if (!OidIsValid(typeoid))
+ elog(ERROR, "Type \"%s\" does not exist",
+ TypeNameToString(typename));
tup = SearchSysCache(TYPEOID,
ObjectIdGetDatum(typeoid),
0, 0, 0);
if (!HeapTupleIsValid(tup))
- elog(ERROR, "RemoveDomain: type '%s' does not exist",
+ elog(ERROR, "RemoveDomain: type \"%s\" does not exist",
TypeNameToString(typename));
/* Permission check: must own type or its namespace */
@@ -615,17 +621,16 @@ RemoveDomain(List *names, DropBehavior behavior)
elog(ERROR, "%s is not a domain",
TypeNameToString(typename));
- /* Delete any comments associated with this type */
- DeleteComments(typeoid, RelationGetRelid(relation));
-
- /* Remove the type tuple from pg_type */
- simple_heap_delete(relation, &tup->t_self);
-
ReleaseSysCache(tup);
- /* At present, domains don't have associated array types */
+ /*
+ * Do the deletion
+ */
+ object.classId = RelOid_pg_type;
+ object.objectId = typeoid;
+ object.objectSubId = 0;
- heap_close(relation, RowExclusiveLock);
+ performDeletion(&object, behavior);
}
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index d27350fd467..519df157184 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -6,13 +6,14 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: view.c,v 1.65 2002/07/01 15:27:49 tgl Exp $
+ * $Id: view.c,v 1.66 2002/07/12 18:43:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/xact.h"
+#include "catalog/dependency.h"
#include "catalog/heap.h"
#include "catalog/namespace.h"
#include "commands/tablecmds.h"
@@ -252,16 +253,21 @@ DefineView(const RangeVar *view, Query *viewParse)
* RemoveView
*
* Remove a view given its name
+ *
+ * We just have to drop the relation; the associated rules will be
+ * cleaned up automatically.
*/
void
RemoveView(const RangeVar *view, DropBehavior behavior)
{
Oid viewOid;
+ ObjectAddress object;
viewOid = RangeVarGetRelid(view, false);
- /*
- * We just have to drop the relation; the associated rules will be
- * cleaned up automatically.
- */
- heap_drop_with_catalog(viewOid, allowSystemTableMods);
+
+ object.classId = RelOid_pg_class;
+ object.objectId = viewOid;
+ object.objectSubId = 0;
+
+ performDeletion(&object, behavior);
}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 739161b1791..4e568a3c531 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.192 2002/07/01 15:27:51 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.193 2002/07/12 18:43:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1505,11 +1505,12 @@ _copyFkConstraint(FkConstraint *from)
Node_Copy(from, newnode, pktable);
Node_Copy(from, newnode, fk_attrs);
Node_Copy(from, newnode, pk_attrs);
- if (from->match_type)
- newnode->match_type = pstrdup(from->match_type);
- newnode->actions = from->actions;
+ newnode->fk_matchtype = from->fk_matchtype;
+ newnode->fk_upd_action = from->fk_upd_action;
+ newnode->fk_del_action = from->fk_del_action;
newnode->deferrable = from->deferrable;
newnode->initdeferred = from->initdeferred;
+ newnode->skip_validation = from->skip_validation;
return newnode;
}
@@ -2089,6 +2090,7 @@ _copyIndexStmt(IndexStmt *from)
Node_Copy(from, newnode, rangetable);
newnode->unique = from->unique;
newnode->primary = from->primary;
+ newnode->isconstraint = from->isconstraint;
return newnode;
}
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 214493449b9..e15870b2c80 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -20,7 +20,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.139 2002/07/01 15:27:52 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.140 2002/07/12 18:43:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -912,6 +912,8 @@ _equalIndexStmt(IndexStmt *a, IndexStmt *b)
return false;
if (a->primary != b->primary)
return false;
+ if (a->isconstraint != b->isconstraint)
+ return false;
return true;
}
@@ -1734,14 +1736,18 @@ _equalFkConstraint(FkConstraint *a, FkConstraint *b)
return false;
if (!equal(a->pk_attrs, b->pk_attrs))
return false;
- if (!equalstr(a->match_type, b->match_type))
+ if (a->fk_matchtype != b->fk_matchtype)
return false;
- if (a->actions != b->actions)
+ if (a->fk_upd_action != b->fk_upd_action)
+ return false;
+ if (a->fk_del_action != b->fk_del_action)
return false;
if (a->deferrable != b->deferrable)
return false;
if (a->initdeferred != b->initdeferred)
return false;
+ if (a->skip_validation != b->skip_validation)
+ return false;
return true;
}
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 2b292635186..ae3139a6ea6 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -5,7 +5,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.161 2002/07/04 15:23:53 thomas Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.162 2002/07/12 18:43:16 tgl Exp $
*
* NOTES
* Every (plan) node in POSTGRES has an associated "out" routine which
@@ -136,9 +136,10 @@ _outIndexStmt(StringInfo str, IndexStmt *node)
_outNode(str, node->whereClause);
appendStringInfo(str, " :rangetable ");
_outNode(str, node->rangetable);
- appendStringInfo(str, " :unique %s :primary %s ",
+ appendStringInfo(str, " :unique %s :primary %s :isconstraint %s ",
booltostr(node->unique),
- booltostr(node->primary));
+ booltostr(node->primary),
+ booltostr(node->isconstraint));
}
static void
@@ -1447,12 +1448,13 @@ _outFkConstraint(StringInfo str, FkConstraint *node)
_outNode(str, node->fk_attrs);
appendStringInfo(str, " :pk_attrs ");
_outNode(str, node->pk_attrs);
- appendStringInfo(str, " :match_type ");
- _outToken(str, node->match_type);
- appendStringInfo(str, " :actions %d :deferrable %s :initdeferred %s",
- node->actions,
+ appendStringInfo(str, " :fk_matchtype %c :fk_upd_action %c :fk_del_action %c :deferrable %s :initdeferred %s :skip_validation %s",
+ node->fk_matchtype,
+ node->fk_upd_action,
+ node->fk_del_action,
booltostr(node->deferrable),
- booltostr(node->initdeferred));
+ booltostr(node->initdeferred),
+ booltostr(node->skip_validation));
}
static void
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index b1b94dc6fda..2fbca505987 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.237 2002/06/20 20:29:31 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.238 2002/07/12 18:43:17 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -47,7 +47,7 @@
/* State shared by transformCreateSchemaStmt and its subroutines */
typedef struct
{
- const char *stmtType; /* "CREATE TABLE" or "ALTER TABLE" */
+ const char *stmtType; /* "CREATE SCHEMA" or "ALTER SCHEMA" */
char *schemaname; /* name of schema */
char *authid; /* owner of schema */
List *tables; /* CREATE TABLE items */
@@ -1066,6 +1066,7 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
cxt->stmtType, (cxt->relation)->relname);
cxt->pkey = index;
}
+ index->isconstraint = true;
if (constraint->name != NULL)
index->idxname = pstrdup(constraint->name);
@@ -1304,15 +1305,8 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
static void
transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt)
{
- CreateTrigStmt *fk_trigger;
List *fkactions = NIL;
List *fkclist;
- List *fk_attr;
- List *pk_attr;
- Ident *id;
- Oid pktypoid[INDEX_MAX_KEYS];
- Oid fktypoid[INDEX_MAX_KEYS];
- int i;
if (cxt->fkconstraints == NIL)
return;
@@ -1323,15 +1317,12 @@ transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt)
foreach(fkclist, cxt->fkconstraints)
{
FkConstraint *fkconstraint = (FkConstraint *) lfirst(fkclist);
+ Oid pktypoid[INDEX_MAX_KEYS];
+ Oid fktypoid[INDEX_MAX_KEYS];
+ int i;
int attnum;
List *fkattrs;
- /*
- * If the constraint has no name, set it to <unnamed>
- */
- if (fkconstraint->constr_name == NULL)
- fkconstraint->constr_name = "<unnamed>";
-
for (attnum = 0; attnum < INDEX_MAX_KEYS; attnum++)
pktypoid[attnum] = fktypoid[attnum] = InvalidOid;
@@ -1473,203 +1464,24 @@ transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt)
}
/*
- * Build a CREATE CONSTRAINT TRIGGER statement for the CHECK
- * action.
+ * For ALTER TABLE ADD CONSTRAINT, we're done. For CREATE TABLE,
+ * gin up an ALTER TABLE ADD CONSTRAINT command to execute after
+ * the basic CREATE TABLE is complete.
*/
- fk_trigger = (CreateTrigStmt *) makeNode(CreateTrigStmt);
- fk_trigger->trigname = fkconstraint->constr_name;
- fk_trigger->relation = cxt->relation;
- fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
- fk_trigger->before = false;
- fk_trigger->row = true;
- fk_trigger->actions[0] = 'i';
- fk_trigger->actions[1] = 'u';
- fk_trigger->actions[2] = '\0';
- fk_trigger->lang = NULL;
- fk_trigger->text = NULL;
-
- fk_trigger->attr = NIL;
- fk_trigger->when = NULL;
- fk_trigger->isconstraint = true;
- fk_trigger->deferrable = fkconstraint->deferrable;
- fk_trigger->initdeferred = fkconstraint->initdeferred;
- fk_trigger->constrrel = fkconstraint->pktable;
-
- fk_trigger->args = NIL;
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->constr_name));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString((cxt->relation)->relname));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->pktable->relname));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->match_type));
- fk_attr = fkconstraint->fk_attrs;
- pk_attr = fkconstraint->pk_attrs;
- if (length(fk_attr) != length(pk_attr))
- elog(ERROR, "number of key attributes in referenced table must be equal to foreign key"
- "\n\tIllegal FOREIGN KEY definition references \"%s\"",
- fkconstraint->pktable->relname);
-
- while (fk_attr != NIL)
+ if (strcmp(cxt->stmtType, "CREATE TABLE") == 0)
{
- id = (Ident *) lfirst(fk_attr);
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(id->name));
-
- id = (Ident *) lfirst(pk_attr);
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(id->name));
+ AlterTableStmt *alterstmt = makeNode(AlterTableStmt);
- fk_attr = lnext(fk_attr);
- pk_attr = lnext(pk_attr);
- }
+ alterstmt->subtype = 'c'; /* preprocessed add constraint */
+ alterstmt->relation = cxt->relation;
+ alterstmt->name = NULL;
+ alterstmt->def = (Node *) makeList1(fkconstraint);
- fkactions = lappend(fkactions, (Node *) fk_trigger);
+ /* Don't need to scan the table contents in this case */
+ fkconstraint->skip_validation = true;
- /*
- * Build a CREATE CONSTRAINT TRIGGER statement for the ON DELETE
- * action fired on the PK table !!!
- */
- fk_trigger = (CreateTrigStmt *) makeNode(CreateTrigStmt);
- fk_trigger->trigname = fkconstraint->constr_name;
- fk_trigger->relation = fkconstraint->pktable;
- fk_trigger->before = false;
- fk_trigger->row = true;
- fk_trigger->actions[0] = 'd';
- fk_trigger->actions[1] = '\0';
- fk_trigger->lang = NULL;
- fk_trigger->text = NULL;
-
- fk_trigger->attr = NIL;
- fk_trigger->when = NULL;
- fk_trigger->isconstraint = true;
- fk_trigger->deferrable = fkconstraint->deferrable;
- fk_trigger->initdeferred = fkconstraint->initdeferred;
- fk_trigger->constrrel = cxt->relation;
- switch ((fkconstraint->actions & FKCONSTR_ON_DELETE_MASK)
- >> FKCONSTR_ON_DELETE_SHIFT)
- {
- case FKCONSTR_ON_KEY_NOACTION:
- fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
- break;
- case FKCONSTR_ON_KEY_RESTRICT:
- fk_trigger->deferrable = false;
- fk_trigger->initdeferred = false;
- fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
- break;
- case FKCONSTR_ON_KEY_CASCADE:
- fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
- break;
- case FKCONSTR_ON_KEY_SETNULL:
- fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
- break;
- case FKCONSTR_ON_KEY_SETDEFAULT:
- fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
- break;
- default:
- elog(ERROR, "Only one ON DELETE action can be specified for FOREIGN KEY constraint");
- break;
+ fkactions = lappend(fkactions, (Node *) alterstmt);
}
-
- fk_trigger->args = NIL;
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->constr_name));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString((cxt->relation)->relname));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->pktable->relname));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->match_type));
- fk_attr = fkconstraint->fk_attrs;
- pk_attr = fkconstraint->pk_attrs;
- while (fk_attr != NIL)
- {
- id = (Ident *) lfirst(fk_attr);
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(id->name));
-
- id = (Ident *) lfirst(pk_attr);
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(id->name));
-
- fk_attr = lnext(fk_attr);
- pk_attr = lnext(pk_attr);
- }
-
- fkactions = lappend(fkactions, (Node *) fk_trigger);
-
- /*
- * Build a CREATE CONSTRAINT TRIGGER statement for the ON UPDATE
- * action fired on the PK table !!!
- */
- fk_trigger = (CreateTrigStmt *) makeNode(CreateTrigStmt);
- fk_trigger->trigname = fkconstraint->constr_name;
- fk_trigger->relation = fkconstraint->pktable;
- fk_trigger->before = false;
- fk_trigger->row = true;
- fk_trigger->actions[0] = 'u';
- fk_trigger->actions[1] = '\0';
- fk_trigger->lang = NULL;
- fk_trigger->text = NULL;
-
- fk_trigger->attr = NIL;
- fk_trigger->when = NULL;
- fk_trigger->isconstraint = true;
- fk_trigger->deferrable = fkconstraint->deferrable;
- fk_trigger->initdeferred = fkconstraint->initdeferred;
- fk_trigger->constrrel = cxt->relation;
- switch ((fkconstraint->actions & FKCONSTR_ON_UPDATE_MASK)
- >> FKCONSTR_ON_UPDATE_SHIFT)
- {
- case FKCONSTR_ON_KEY_NOACTION:
- fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
- break;
- case FKCONSTR_ON_KEY_RESTRICT:
- fk_trigger->deferrable = false;
- fk_trigger->initdeferred = false;
- fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
- break;
- case FKCONSTR_ON_KEY_CASCADE:
- fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
- break;
- case FKCONSTR_ON_KEY_SETNULL:
- fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
- break;
- case FKCONSTR_ON_KEY_SETDEFAULT:
- fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
- break;
- default:
- elog(ERROR, "Only one ON UPDATE action can be specified for FOREIGN KEY constraint");
- break;
- }
-
- fk_trigger->args = NIL;
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->constr_name));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString((cxt->relation)->relname));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->pktable->relname));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->match_type));
- fk_attr = fkconstraint->fk_attrs;
- pk_attr = fkconstraint->pk_attrs;
- while (fk_attr != NIL)
- {
- id = (Ident *) lfirst(fk_attr);
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(id->name));
-
- id = (Ident *) lfirst(pk_attr);
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(id->name));
-
- fk_attr = lnext(fk_attr);
- pk_attr = lnext(pk_attr);
- }
-
- fkactions = lappend(fkactions, (Node *) fk_trigger);
}
/*
@@ -2642,6 +2454,14 @@ transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt,
*extras_after = nconc(cxt.alist, *extras_after);
break;
+ case 'c':
+ /*
+ * Already-transformed ADD CONSTRAINT, so just make it look
+ * like the standard case.
+ */
+ stmt->subtype = 'C';
+ break;
+
default:
break;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 18b349c4ee2..17128a814e5 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.338 2002/07/11 07:39:25 ishii Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.339 2002/07/12 18:43:17 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -309,8 +309,7 @@ static void doNegateFloat(Value *v);
%type <node> TableConstraint, TableLikeClause
%type <list> ColQualList
%type <node> ColConstraint, ColConstraintElem, ConstraintAttr
-%type <ival> key_actions, key_delete, key_update, key_reference
-%type <str> key_match
+%type <ival> key_actions, key_delete, key_match, key_update, key_action
%type <ival> ConstraintAttributeSpec, ConstraintDeferrabilitySpec,
ConstraintTimeSpec
@@ -1594,8 +1593,9 @@ ColConstraintElem:
n->pktable = $2;
n->fk_attrs = NIL;
n->pk_attrs = $3;
- n->match_type = $4;
- n->actions = $5;
+ n->fk_matchtype = $4;
+ n->fk_upd_action = (char) ($5 >> 8);
+ n->fk_del_action = (char) ($5 & 0xFF);
n->deferrable = FALSE;
n->initdeferred = FALSE;
$$ = (Node *)n;
@@ -1714,16 +1714,16 @@ ConstraintElem:
$$ = (Node *)n;
}
| FOREIGN KEY '(' columnList ')' REFERENCES qualified_name
- opt_column_list
- key_match key_actions ConstraintAttributeSpec
+ opt_column_list key_match key_actions ConstraintAttributeSpec
{
FkConstraint *n = makeNode(FkConstraint);
n->constr_name = NULL;
n->pktable = $7;
n->fk_attrs = $4;
n->pk_attrs = $8;
- n->match_type = $9;
- n->actions = $10;
+ n->fk_matchtype = $9;
+ n->fk_upd_action = (char) ($10 >> 8);
+ n->fk_del_action = (char) ($10 & 0xFF);
n->deferrable = ($11 & 1) != 0;
n->initdeferred = ($11 & 2) != 0;
$$ = (Node *)n;
@@ -1750,45 +1750,54 @@ columnElem: ColId
key_match: MATCH FULL
{
- $$ = "FULL";
+ $$ = FKCONSTR_MATCH_FULL;
}
| MATCH PARTIAL
{
elog(ERROR, "FOREIGN KEY/MATCH PARTIAL not yet implemented");
- $$ = "PARTIAL";
+ $$ = FKCONSTR_MATCH_PARTIAL;
}
| MATCH SIMPLE
{
- $$ = "UNSPECIFIED";
+ $$ = FKCONSTR_MATCH_UNSPECIFIED;
}
| /*EMPTY*/
{
- $$ = "UNSPECIFIED";
+ $$ = FKCONSTR_MATCH_UNSPECIFIED;
}
;
+/*
+ * We combine the update and delete actions into one value temporarily
+ * for simplicity of parsing, and then break them down again in the
+ * calling production. update is in the left 8 bits, delete in the right.
+ * Note that NOACTION is the default.
+ */
key_actions:
- key_delete { $$ = $1; }
- | key_update { $$ = $1; }
- | key_delete key_update { $$ = $1 | $2; }
- | key_update key_delete { $$ = $1 | $2; }
- | /*EMPTY*/ { $$ = 0; }
+ key_update
+ { $$ = ($1 << 8) | (FKCONSTR_ACTION_NOACTION & 0xFF); }
+ | key_delete
+ { $$ = (FKCONSTR_ACTION_NOACTION << 8) | ($1 & 0xFF); }
+ | key_update key_delete
+ { $$ = ($1 << 8) | ($2 & 0xFF); }
+ | key_delete key_update
+ { $$ = ($2 << 8) | ($1 & 0xFF); }
+ | /*EMPTY*/
+ { $$ = (FKCONSTR_ACTION_NOACTION << 8) | (FKCONSTR_ACTION_NOACTION & 0xFF); }
;
-key_delete: ON DELETE_P key_reference
- { $$ = $3 << FKCONSTR_ON_DELETE_SHIFT; }
+key_update: ON UPDATE key_action { $$ = $3; }
;
-key_update: ON UPDATE key_reference
- { $$ = $3 << FKCONSTR_ON_UPDATE_SHIFT; }
+key_delete: ON DELETE_P key_action { $$ = $3; }
;
-key_reference:
- NO ACTION { $$ = FKCONSTR_ON_KEY_NOACTION; }
- | RESTRICT { $$ = FKCONSTR_ON_KEY_RESTRICT; }
- | CASCADE { $$ = FKCONSTR_ON_KEY_CASCADE; }
- | SET NULL_P { $$ = FKCONSTR_ON_KEY_SETNULL; }
- | SET DEFAULT { $$ = FKCONSTR_ON_KEY_SETDEFAULT; }
+key_action:
+ NO ACTION { $$ = FKCONSTR_ACTION_NOACTION; }
+ | RESTRICT { $$ = FKCONSTR_ACTION_RESTRICT; }
+ | CASCADE { $$ = FKCONSTR_ACTION_CASCADE; }
+ | SET NULL_P { $$ = FKCONSTR_ACTION_SETNULL; }
+ | SET DEFAULT { $$ = FKCONSTR_ACTION_SETDEFAULT; }
;
OptInherit: INHERITS '(' qualified_name_list ')' { $$ = $3; }
@@ -2300,7 +2309,7 @@ drop_type: TABLE { $$ = DROP_TABLE; }
| INDEX { $$ = DROP_INDEX; }
| TYPE_P { $$ = DROP_TYPE; }
| DOMAIN_P { $$ = DROP_DOMAIN; }
- | CONVERSION_P { $$ = DROP_CONVERSION; }
+ | CONVERSION_P { $$ = DROP_CONVERSION; }
;
any_name_list:
@@ -6673,6 +6682,7 @@ unreserved_keyword:
| COMMIT
| COMMITTED
| CONSTRAINTS
+ | CONVERSION_P
| COPY
| CREATEDB
| CREATEUSER
@@ -6857,6 +6867,7 @@ col_name_keyword:
| SUBSTRING
| TIME
| TIMESTAMP
+ | TREAT
| TRIM
| VARCHAR
;
@@ -6963,7 +6974,6 @@ reserved_keyword:
| THEN
| TO
| TRAILING
- | TREAT
| TRUE_P
| UNION
| UNIQUE
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index 43f8ffd6f1f..5922cb4ab5b 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.73 2002/06/20 20:29:33 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.74 2002/07/12 18:43:17 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -16,6 +16,7 @@
#include "access/heapam.h"
#include "catalog/catname.h"
+#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/pg_rewrite.h"
#include "commands/view.h"
@@ -57,6 +58,8 @@ InsertRule(char *rulname,
TupleDesc tupDesc;
HeapTuple tup;
Oid rewriteObjectId;
+ ObjectAddress myself,
+ referenced;
if (IsDefinedRewriteRule(eventrel_oid, rulname))
elog(ERROR, "Attempt to insert rule \"%s\" failed: already exists",
@@ -103,6 +106,23 @@ InsertRule(char *rulname,
heap_freetuple(tup);
+ /*
+ * Install dependency on rule's relation to ensure it will go away
+ * on relation deletion. If the rule is ON SELECT, make the dependency
+ * implicit --- this prevents deleting a view's SELECT rule. Other
+ * kinds of rules can be AUTO.
+ */
+ myself.classId = RelationGetRelid(pg_rewrite_desc);
+ myself.objectId = rewriteObjectId;
+ myself.objectSubId = 0;
+
+ referenced.classId = RelOid_pg_class;
+ referenced.objectId = eventrel_oid;
+ referenced.objectSubId = 0;
+
+ recordDependencyOn(&myself, &referenced,
+ (evtype == CMD_SELECT) ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);
+
heap_close(pg_rewrite_desc, RowExclusiveLock);
return rewriteObjectId;
diff --git a/src/backend/rewrite/rewriteRemove.c b/src/backend/rewrite/rewriteRemove.c
index 71fd4d5ac40..4ce9f3f2b90 100644
--- a/src/backend/rewrite/rewriteRemove.c
+++ b/src/backend/rewrite/rewriteRemove.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteRemove.c,v 1.50 2002/06/20 20:29:34 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteRemove.c,v 1.51 2002/07/12 18:43:17 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -17,14 +17,15 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "catalog/catname.h"
+#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/pg_rewrite.h"
-#include "commands/comment.h"
#include "miscadmin.h"
#include "rewrite/rewriteRemove.h"
#include "rewrite/rewriteSupport.h"
#include "utils/acl.h"
#include "utils/fmgroids.h"
+#include "utils/lsyscache.h"
#include "utils/syscache.h"
@@ -34,77 +35,102 @@
* Delete a rule given its name.
*/
void
-RemoveRewriteRule(Oid owningRel, const char *ruleName)
+RemoveRewriteRule(Oid owningRel, const char *ruleName, DropBehavior behavior)
{
- Relation RewriteRelation;
- Relation event_relation;
HeapTuple tuple;
- Oid ruleId;
Oid eventRelationOid;
- bool hasMoreRules;
AclResult aclresult;
-
- /*
- * Open the pg_rewrite relation.
- */
- RewriteRelation = heap_openr(RewriteRelationName, RowExclusiveLock);
+ ObjectAddress object;
/*
* Find the tuple for the target rule.
*/
- tuple = SearchSysCacheCopy(RULERELNAME,
- ObjectIdGetDatum(owningRel),
- PointerGetDatum(ruleName),
- 0, 0);
+ tuple = SearchSysCache(RULERELNAME,
+ ObjectIdGetDatum(owningRel),
+ PointerGetDatum(ruleName),
+ 0, 0);
/*
- * complain if no rule with such name existed
+ * complain if no rule with such name exists
*/
if (!HeapTupleIsValid(tuple))
elog(ERROR, "Rule \"%s\" not found", ruleName);
/*
- * Save the OID of the rule (i.e. the tuple's OID) and the event
- * relation's OID
+ * Verify user has appropriate permissions.
*/
- ruleId = tuple->t_data->t_oid;
eventRelationOid = ((Form_pg_rewrite) GETSTRUCT(tuple))->ev_class;
Assert(eventRelationOid == owningRel);
+ aclresult = pg_class_aclcheck(eventRelationOid, GetUserId(), ACL_RULE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, get_rel_name(eventRelationOid));
/*
- * We had better grab AccessExclusiveLock so that we know no other
- * rule additions/deletions are going on for this relation. Else we
- * cannot set relhasrules correctly. Besides, we don't want to be
- * changing the ruleset while queries are executing on the rel.
+ * Do the deletion
*/
- event_relation = heap_open(eventRelationOid, AccessExclusiveLock);
+ object.classId = get_system_catalog_relid(RewriteRelationName);
+ object.objectId = tuple->t_data->t_oid;
+ object.objectSubId = 0;
+
+ ReleaseSysCache(tuple);
+
+ performDeletion(&object, behavior);
+}
+
+
+/*
+ * Guts of rule deletion.
+ */
+void
+RemoveRewriteRuleById(Oid ruleOid)
+{
+ Relation RewriteRelation;
+ ScanKeyData skey[1];
+ SysScanDesc rcscan;
+ Relation event_relation;
+ HeapTuple tuple;
+ Oid eventRelationOid;
+ bool hasMoreRules;
/*
- * Verify user has appropriate permissions.
+ * Open the pg_rewrite relation.
*/
- aclresult = pg_class_aclcheck(eventRelationOid, GetUserId(), ACL_RULE);
- if (aclresult != ACLCHECK_OK)
- aclcheck_error(aclresult, RelationGetRelationName(event_relation));
+ RewriteRelation = heap_openr(RewriteRelationName, RowExclusiveLock);
+
+ /*
+ * Find the tuple for the target rule.
+ */
+ ScanKeyEntryInitialize(&skey[0], 0x0,
+ ObjectIdAttributeNumber, F_OIDEQ,
+ ObjectIdGetDatum(ruleOid));
- /* do not allow the removal of a view's SELECT rule */
- if (event_relation->rd_rel->relkind == RELKIND_VIEW &&
- ((Form_pg_rewrite) GETSTRUCT(tuple))->ev_type == '1')
- elog(ERROR, "Cannot remove a view's SELECT rule");
+ rcscan = systable_beginscan(RewriteRelation, RewriteOidIndex, true,
+ SnapshotNow, 1, skey);
- hasMoreRules = event_relation->rd_rules != NULL &&
- event_relation->rd_rules->numLocks > 1;
+ tuple = systable_getnext(rcscan);
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "RemoveRewriteRuleById: Rule %u does not exist",
+ ruleOid);
/*
- * Delete any comments associated with this rule
+ * We had better grab AccessExclusiveLock so that we know no other
+ * rule additions/deletions are going on for this relation. Else we
+ * cannot set relhasrules correctly. Besides, we don't want to be
+ * changing the ruleset while queries are executing on the rel.
*/
- DeleteComments(ruleId, RelationGetRelid(RewriteRelation));
+ eventRelationOid = ((Form_pg_rewrite) GETSTRUCT(tuple))->ev_class;
+ event_relation = heap_open(eventRelationOid, AccessExclusiveLock);
+
+ hasMoreRules = event_relation->rd_rules != NULL &&
+ event_relation->rd_rules->numLocks > 1;
/*
* Now delete the pg_rewrite tuple for the rule
*/
simple_heap_delete(RewriteRelation, &tuple->t_self);
- heap_freetuple(tuple);
+ systable_endscan(rcscan);
heap_close(RewriteRelation, RowExclusiveLock);
@@ -120,49 +146,3 @@ RemoveRewriteRule(Oid owningRel, const char *ruleName)
/* Close rel, but keep lock till commit... */
heap_close(event_relation, NoLock);
}
-
-/*
- * RelationRemoveRules -
- * removes all rules associated with the relation when the relation is
- * being removed.
- */
-void
-RelationRemoveRules(Oid relid)
-{
- Relation RewriteRelation;
- SysScanDesc scanDesc;
- ScanKeyData scanKeyData;
- HeapTuple tuple;
-
- /*
- * Open the pg_rewrite relation.
- */
- RewriteRelation = heap_openr(RewriteRelationName, RowExclusiveLock);
-
- /*
- * Scan pg_rewrite for all the tuples that have the same ev_class
- * as relid (the relation to be removed).
- */
- ScanKeyEntryInitialize(&scanKeyData,
- 0,
- Anum_pg_rewrite_ev_class,
- F_OIDEQ,
- ObjectIdGetDatum(relid));
-
- scanDesc = systable_beginscan(RewriteRelation,
- RewriteRelRulenameIndex,
- true, SnapshotNow,
- 1, &scanKeyData);
-
- while (HeapTupleIsValid(tuple = systable_getnext(scanDesc)))
- {
- /* Delete any comments associated with this rule */
- DeleteComments(tuple->t_data->t_oid, RelationGetRelid(RewriteRelation));
-
- simple_heap_delete(RewriteRelation, &tuple->t_self);
- }
-
- systable_endscan(scanDesc);
-
- heap_close(RewriteRelation, RowExclusiveLock);
-}
diff --git a/src/backend/rewrite/rewriteSupport.c b/src/backend/rewrite/rewriteSupport.c
index ba590b8cc66..3f4c7f23871 100644
--- a/src/backend/rewrite/rewriteSupport.c
+++ b/src/backend/rewrite/rewriteSupport.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteSupport.c,v 1.52 2002/06/20 20:29:34 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteSupport.c,v 1.53 2002/07/12 18:43:17 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -18,6 +18,7 @@
#include "catalog/catname.h"
#include "catalog/indexing.h"
#include "rewrite/rewriteSupport.h"
+#include "utils/inval.h"
#include "utils/syscache.h"
@@ -44,9 +45,8 @@ IsDefinedRewriteRule(Oid owningRel, const char *ruleName)
* NOTE: an important side-effect of this operation is that an SI invalidation
* message is sent out to all backends --- including me --- causing relcache
* entries to be flushed or updated with the new set of rules for the table.
- * Therefore, we execute the update even if relhasrules has the right value
- * already. Possible future improvement: skip the disk update and just send
- * an SI message in that case.
+ * This must happen even if we find that no change is needed in the pg_class
+ * row.
*/
void
SetRelationRuleStatus(Oid relationId, bool relHasRules,
@@ -54,6 +54,7 @@ SetRelationRuleStatus(Oid relationId, bool relHasRules,
{
Relation relationRelation;
HeapTuple tuple;
+ Form_pg_class classForm;
Relation idescs[Num_pg_class_indices];
/*
@@ -66,18 +67,28 @@ SetRelationRuleStatus(Oid relationId, bool relHasRules,
0, 0, 0);
if (!HeapTupleIsValid(tuple))
elog(ERROR, "SetRelationRuleStatus: cache lookup failed for relation %u", relationId);
+ classForm = (Form_pg_class) GETSTRUCT(tuple);
- /* Do the update */
- ((Form_pg_class) GETSTRUCT(tuple))->relhasrules = relHasRules;
- if (relIsBecomingView)
- ((Form_pg_class) GETSTRUCT(tuple))->relkind = RELKIND_VIEW;
+ if (classForm->relhasrules != relHasRules ||
+ (relIsBecomingView && classForm->relkind != RELKIND_VIEW))
+ {
+ /* Do the update */
+ classForm->relhasrules = relHasRules;
+ if (relIsBecomingView)
+ classForm->relkind = RELKIND_VIEW;
- simple_heap_update(relationRelation, &tuple->t_self, tuple);
+ simple_heap_update(relationRelation, &tuple->t_self, tuple);
- /* Keep the catalog indices up to date */
- CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs);
- CatalogIndexInsert(idescs, Num_pg_class_indices, relationRelation, tuple);
- CatalogCloseIndices(Num_pg_class_indices, idescs);
+ /* Keep the catalog indices up to date */
+ CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs);
+ CatalogIndexInsert(idescs, Num_pg_class_indices, relationRelation, tuple);
+ CatalogCloseIndices(Num_pg_class_indices, idescs);
+ }
+ else
+ {
+ /* no need to change tuple, but force relcache rebuild anyway */
+ CacheInvalidateRelcache(relationId);
+ }
heap_freetuple(tuple);
heap_close(relationRelation, RowExclusiveLock);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index e5e57d4a6de..86373943667 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.161 2002/07/11 07:39:26 ishii Exp $
+ * $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.162 2002/07/12 18:43:17 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -582,6 +582,7 @@ ProcessUtility(Node *parsetree,
stmt->indexParams, /* parameters */
stmt->unique,
stmt->primary,
+ stmt->isconstraint,
(Expr *) stmt->whereClause,
stmt->rangetable);
}
@@ -596,19 +597,11 @@ ProcessUtility(Node *parsetree,
break;
case T_RemoveAggrStmt:
- {
- RemoveAggrStmt *stmt = (RemoveAggrStmt *) parsetree;
-
- RemoveAggregate(stmt->aggname, stmt->aggtype);
- }
+ RemoveAggregate((RemoveAggrStmt *) parsetree);
break;
case T_RemoveFuncStmt:
- {
- RemoveFuncStmt *stmt = (RemoveFuncStmt *) parsetree;
-
- RemoveFunction(stmt->funcname, stmt->args);
- }
+ RemoveFunction((RemoveFuncStmt *) parsetree);
break;
case T_RemoveOperStmt:
@@ -719,7 +712,7 @@ ProcessUtility(Node *parsetree,
break;
case T_CreateTrigStmt:
- CreateTrigger((CreateTrigStmt *) parsetree);
+ CreateTrigger((CreateTrigStmt *) parsetree, false);
break;
case T_DropPropertyStmt:
@@ -733,11 +726,13 @@ ProcessUtility(Node *parsetree,
{
case DROP_RULE:
/* RemoveRewriteRule checks permissions */
- RemoveRewriteRule(relId, stmt->property);
+ RemoveRewriteRule(relId, stmt->property,
+ stmt->behavior);
break;
case DROP_TRIGGER:
/* DropTrigger checks permissions */
- DropTrigger(relId, stmt->property);
+ DropTrigger(relId, stmt->property,
+ stmt->behavior);
break;
}
}
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 5063225afce..e383ab892d1 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.75 2002/07/06 20:16:36 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.76 2002/07/12 18:43:18 tgl Exp $
*
* NOTES
* Eventually, the index information should go through here, too.
@@ -671,6 +671,25 @@ get_relname_relid(const char *relname, Oid relnamespace)
0, 0);
}
+/*
+ * get_system_catalog_relid
+ * Get the OID of a system catalog identified by name.
+ */
+Oid
+get_system_catalog_relid(const char *catname)
+{
+ Oid relid;
+
+ relid = GetSysCacheOid(RELNAMENSP,
+ PointerGetDatum(catname),
+ ObjectIdGetDatum(PG_CATALOG_NAMESPACE),
+ 0, 0);
+ if (!OidIsValid(relid))
+ elog(ERROR, "get_system_catalog_relid: cannot find %s", catname);
+
+ return relid;
+}
+
#ifdef NOT_USED
/*
* get_relnatts
@@ -1060,7 +1079,7 @@ getBaseType(Oid typid)
/*
* getBaseTypeTypeMod
* If the given type is a domain, return its base type;
- * otherwise return the type's own OID.
+ * otherwise return the type's own OID. Also return base typmod.
*/
Oid
getBaseTypeTypeMod(Oid typid, int32 *typmod)
@@ -1077,7 +1096,7 @@ getBaseTypeTypeMod(Oid typid, int32 *typmod)
ObjectIdGetDatum(typid),
0, 0, 0);
if (!HeapTupleIsValid(tup))
- elog(ERROR, "getBaseType: failed to lookup type %u", typid);
+ elog(ERROR, "getBaseTypeTypeMod: failed to lookup type %u", typid);
typTup = (Form_pg_type) GETSTRUCT(tup);
if (typTup->typtype != 'd')
{
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index ec70be31b67..9714f4ba3aa 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.165 2002/06/20 20:29:39 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.166 2002/07/12 18:43:18 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -43,11 +43,11 @@
#include "catalog/pg_amproc.h"
#include "catalog/pg_attrdef.h"
#include "catalog/pg_attribute.h"
+#include "catalog/pg_constraint.h"
#include "catalog/pg_index.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_proc.h"
-#include "catalog/pg_relcheck.h"
#include "catalog/pg_rewrite.h"
#include "catalog/pg_type.h"
#include "commands/trigger.h"
@@ -296,7 +296,7 @@ static void RelationBuildTupleDesc(RelationBuildDescInfo buildinfo,
static Relation RelationBuildDesc(RelationBuildDescInfo buildinfo,
Relation oldrelation);
static void AttrDefaultFetch(Relation relation);
-static void RelCheckFetch(Relation relation);
+static void CheckConstraintFetch(Relation relation);
static List *insert_ordered_oid(List *list, Oid datum);
static void IndexSupportInitialize(Form_pg_index iform,
IndexStrategy indexStrategy,
@@ -451,7 +451,7 @@ AllocateRelationDesc(Relation relation, Form_pg_class relp)
* RelationBuildTupleDesc
*
* Form the relation's tuple descriptor from information in
- * the pg_attribute, pg_attrdef & pg_relcheck system catalogs.
+ * the pg_attribute, pg_attrdef & pg_constraint system catalogs.
*/
static void
RelationBuildTupleDesc(RelationBuildDescInfo buildinfo,
@@ -603,7 +603,7 @@ RelationBuildTupleDesc(RelationBuildDescInfo buildinfo,
MemoryContextAlloc(CacheMemoryContext,
constr->num_check * sizeof(ConstrCheck));
MemSet(constr->check, 0, constr->num_check * sizeof(ConstrCheck));
- RelCheckFetch(relation);
+ CheckConstraintFetch(relation);
}
else
constr->num_check = 0;
@@ -2483,62 +2483,60 @@ AttrDefaultFetch(Relation relation)
}
static void
-RelCheckFetch(Relation relation)
+CheckConstraintFetch(Relation relation)
{
ConstrCheck *check = relation->rd_att->constr->check;
int ncheck = relation->rd_att->constr->num_check;
- Relation rcrel;
- SysScanDesc rcscan;
- ScanKeyData skey;
+ Relation conrel;
+ SysScanDesc conscan;
+ ScanKeyData skey[1];
HeapTuple htup;
- Name rcname;
Datum val;
bool isnull;
- int found;
+ int found = 0;
- ScanKeyEntryInitialize(&skey,
- (bits16) 0x0,
- (AttrNumber) Anum_pg_relcheck_rcrelid,
- (RegProcedure) F_OIDEQ,
+ ScanKeyEntryInitialize(&skey[0], 0x0,
+ Anum_pg_constraint_conrelid, F_OIDEQ,
ObjectIdGetDatum(RelationGetRelid(relation)));
- rcrel = heap_openr(RelCheckRelationName, AccessShareLock);
- rcscan = systable_beginscan(rcrel, RelCheckIndex, true,
- SnapshotNow,
- 1, &skey);
- found = 0;
+ conrel = heap_openr(ConstraintRelationName, AccessShareLock);
+ conscan = systable_beginscan(conrel, ConstraintRelidIndex, true,
+ SnapshotNow, 1, skey);
- while (HeapTupleIsValid(htup = systable_getnext(rcscan)))
+ while (HeapTupleIsValid(htup = systable_getnext(conscan)))
{
+ Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(htup);
+
+ /* We want check constraints only */
+ if (conform->contype != CONSTRAINT_CHECK)
+ continue;
+
if (found == ncheck)
- elog(ERROR, "RelCheckFetch: unexpected record found for rel %s",
+ elog(ERROR, "CheckConstraintFetch: unexpected record found for rel %s",
RelationGetRelationName(relation));
- rcname = (Name) fastgetattr(htup,
- Anum_pg_relcheck_rcname,
- rcrel->rd_att, &isnull);
- if (isnull)
- elog(ERROR, "RelCheckFetch: rcname IS NULL for rel %s",
- RelationGetRelationName(relation));
check[found].ccname = MemoryContextStrdup(CacheMemoryContext,
- NameStr(*rcname));
+ NameStr(conform->conname));
+
+ /* Grab and test conbin is actually set */
val = fastgetattr(htup,
- Anum_pg_relcheck_rcbin,
- rcrel->rd_att, &isnull);
+ Anum_pg_constraint_conbin,
+ conrel->rd_att, &isnull);
if (isnull)
- elog(ERROR, "RelCheckFetch: rcbin IS NULL for rel %s",
+ elog(ERROR, "CheckConstraintFetch: conbin IS NULL for rel %s",
RelationGetRelationName(relation));
+
check[found].ccbin = MemoryContextStrdup(CacheMemoryContext,
DatumGetCString(DirectFunctionCall1(textout,
val)));
found++;
}
- systable_endscan(rcscan);
- heap_close(rcrel, AccessShareLock);
+ systable_endscan(conscan);
+ heap_close(conrel, AccessShareLock);
if (found != ncheck)
- elog(ERROR, "RelCheckFetch: %d record(s) not found for rel %s",
+ elog(ERROR, "CheckConstraintFetch: %d record(s) not found for rel %s",
ncheck - found, RelationGetRelationName(relation));
}
diff --git a/src/bin/initdb/initdb.sh b/src/bin/initdb/initdb.sh
index 3e7ccceae9c..144dad4dbab 100644
--- a/src/bin/initdb/initdb.sh
+++ b/src/bin/initdb/initdb.sh
@@ -27,7 +27,7 @@
# Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
# Portions Copyright (c) 1994, Regents of the University of California
#
-# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.156 2002/06/20 20:29:41 momjian Exp $
+# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.157 2002/07/12 18:43:18 tgl Exp $
#
#-------------------------------------------------------------------------
@@ -682,11 +682,11 @@ $ECHO_N "enabling unlimited row size for system tables... "$ECHO_C
"$PGPATH"/postgres $PGSQL_OPT template1 >/dev/null <<EOF
ALTER TABLE pg_attrdef CREATE TOAST TABLE;
+ALTER TABLE pg_constraint CREATE TOAST TABLE;
ALTER TABLE pg_database CREATE TOAST TABLE;
ALTER TABLE pg_description CREATE TOAST TABLE;
ALTER TABLE pg_group CREATE TOAST TABLE;
ALTER TABLE pg_proc CREATE TOAST TABLE;
-ALTER TABLE pg_relcheck CREATE TOAST TABLE;
ALTER TABLE pg_rewrite CREATE TOAST TABLE;
ALTER TABLE pg_shadow CREATE TOAST TABLE;
ALTER TABLE pg_statistic CREATE TOAST TABLE;
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 01ec480e3f1..dd6aad5503d 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -22,7 +22,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.270 2002/07/04 15:35:07 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.271 2002/07/12 18:43:18 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -4656,8 +4656,8 @@ dumpOneTable(Archive *fout, TableInfo *tbinfo, TableInfo *g_tblinfo)
if (tbinfo->ncheck > 0)
{
PGresult *res2;
- int i_rcname,
- i_rcsrc;
+ int i_conname,
+ i_consrc;
int ntups2;
if (g_verbose)
@@ -4666,24 +4666,25 @@ dumpOneTable(Archive *fout, TableInfo *tbinfo, TableInfo *g_tblinfo)
resetPQExpBuffer(query);
if (g_fout->remoteVersion >= 70300)
- appendPQExpBuffer(query, "SELECT rcname, rcsrc"
- " from pg_catalog.pg_relcheck c1"
- " where rcrelid = '%s'::pg_catalog.oid "
+ appendPQExpBuffer(query, "SELECT conname, consrc"
+ " from pg_catalog.pg_constraint c1"
+ " where conrelid = '%s'::pg_catalog.oid "
+ " and contype = 'c' "
" and not exists "
" (select 1 from "
- " pg_catalog.pg_relcheck c2, "
+ " pg_catalog.pg_constraint c2, "
" pg_catalog.pg_inherits i "
- " where i.inhrelid = c1.rcrelid "
- " and (c2.rcname = c1.rcname "
- " or (c2.rcname[0] = '$' "
- " and c1.rcname[0] = '$')"
+ " where i.inhrelid = c1.conrelid "
+ " and (c2.conname = c1.conname "
+ " or (c2.conname[0] = '$' "
+ " and c1.conname[0] = '$')"
" )"
- " and c2.rcsrc = c1.rcsrc "
- " and c2.rcrelid = i.inhparent) "
- " order by rcname ",
+ " and c2.consrc = c1.consrc "
+ " and c2.conrelid = i.inhparent) "
+ " order by conname ",
tbinfo->oid);
else
- appendPQExpBuffer(query, "SELECT rcname, rcsrc"
+ appendPQExpBuffer(query, "SELECT rcname as conname, rcsrc as consrc"
" from pg_relcheck c1"
" where rcrelid = '%s'::oid "
" and not exists "
@@ -4714,13 +4715,13 @@ dumpOneTable(Archive *fout, TableInfo *tbinfo, TableInfo *g_tblinfo)
exit_nicely();
}
- i_rcname = PQfnumber(res2, "rcname");
- i_rcsrc = PQfnumber(res2, "rcsrc");
+ i_conname = PQfnumber(res2, "conname");
+ i_consrc = PQfnumber(res2, "consrc");
for (j = 0; j < ntups2; j++)
{
- const char *name = PQgetvalue(res2, j, i_rcname);
- const char *expr = PQgetvalue(res2, j, i_rcsrc);
+ const char *name = PQgetvalue(res2, j, i_conname);
+ const char *expr = PQgetvalue(res2, j, i_consrc);
if (actual_atts + j > 0)
appendPQExpBuffer(q, ",\n\t");
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 297875e0bec..d3c386284b4 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -3,7 +3,7 @@
*
* Copyright 2000 by PostgreSQL Global Development Group
*
- * $Header: /cvsroot/pgsql/src/bin/psql/describe.c,v 1.54 2002/05/13 17:45:30 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/bin/psql/describe.c,v 1.55 2002/07/12 18:43:19 tgl Exp $
*/
#include "postgres_fe.h"
#include "describe.h"
@@ -747,7 +747,7 @@ describeTableDetails(const char *name, bool desc)
*result3 = NULL,
*result4 = NULL;
int index_count = 0,
- constr_count = 0,
+ check_count = 0,
rule_count = 0,
trigger_count = 0;
int count_footers = 0;
@@ -770,19 +770,20 @@ describeTableDetails(const char *name, bool desc)
index_count = PQntuples(result1);
}
- /* count table (and column) constraints */
+ /* count table (and column) check constraints */
if (tableinfo.checks)
{
printfPQExpBuffer(&buf,
- "SELECT rcsrc, rcname\n"
- "FROM pg_relcheck r, pg_class c\n"
- "WHERE c.relname='%s' AND c.oid = r.rcrelid",
+ "SELECT consrc, conname\n"
+ "FROM pg_constraint r, pg_class c\n"
+ "WHERE c.relname='%s' AND c.oid = r.conrelid\n"
+ "AND r.contype = 'c'",
name);
result2 = PSQLexec(buf.data);
if (!result2)
goto error_return;
else
- constr_count = PQntuples(result2);
+ check_count = PQntuples(result2);
}
/* count rules */
@@ -815,7 +816,7 @@ describeTableDetails(const char *name, bool desc)
trigger_count = PQntuples(result4);
}
- footers = xmalloc((index_count + constr_count + rule_count + trigger_count + 1)
+ footers = xmalloc((index_count + check_count + rule_count + trigger_count + 1)
* sizeof(*footers));
/* print indexes */
@@ -846,8 +847,8 @@ describeTableDetails(const char *name, bool desc)
}
- /* print constraints */
- for (i = 0; i < constr_count; i++)
+ /* print check constraints */
+ for (i = 0; i < check_count; i++)
{
char *s = _("Check constraints");
diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h
index e6e0b1aaebb..0f277d1c9e8 100644
--- a/src/include/access/tupdesc.h
+++ b/src/include/access/tupdesc.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: tupdesc.h,v 1.35 2002/06/20 20:29:43 momjian Exp $
+ * $Id: tupdesc.h,v 1.36 2002/07/12 18:43:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -43,7 +43,7 @@ typedef struct tupleConstr
/*
* This structure contains all information (i.e. from Classes
- * pg_attribute, pg_attrdef, pg_relcheck) for a tuple.
+ * pg_attribute, pg_attrdef, pg_constraint) for a tuple.
*/
typedef struct tupleDesc
{
diff --git a/src/include/catalog/catname.h b/src/include/catalog/catname.h
index 6f755c9f9dc..0e452a9ca3e 100644
--- a/src/include/catalog/catname.h
+++ b/src/include/catalog/catname.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: catname.h,v 1.27 2002/07/11 07:39:27 ishii Exp $
+ * $Id: catname.h,v 1.28 2002/07/12 18:43:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -20,8 +20,10 @@
#define AccessMethodOperatorRelationName "pg_amop"
#define AccessMethodProcedureRelationName "pg_amproc"
#define AttributeRelationName "pg_attribute"
+#define ConstraintRelationName "pg_constraint"
#define ConversionRelationName "pg_conversion"
#define DatabaseRelationName "pg_database"
+#define DependRelationName "pg_depend"
#define DescriptionRelationName "pg_description"
#define GroupRelationName "pg_group"
#define IndexRelationName "pg_index"
@@ -40,7 +42,6 @@
#define TypeRelationName "pg_type"
#define VersionRelationName "pg_version"
#define AttrDefaultRelationName "pg_attrdef"
-#define RelCheckRelationName "pg_relcheck"
#define TriggerRelationName "pg_trigger"
#endif /* CATNAME_H */
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 81f52bd9b34..9d3acbe35f5 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: catversion.h,v 1.138 2002/07/11 07:39:27 ishii Exp $
+ * $Id: catversion.h,v 1.139 2002/07/12 18:43:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 200207111
+#define CATALOG_VERSION_NO 200207112
#endif
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
new file mode 100644
index 00000000000..c890acb687a
--- /dev/null
+++ b/src/include/catalog/dependency.h
@@ -0,0 +1,92 @@
+/*-------------------------------------------------------------------------
+ *
+ * dependency.h
+ * Routines to support inter-object dependencies.
+ *
+ *
+ * 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 $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef DEPENDENCY_H
+#define DEPENDENCY_H
+
+#include "nodes/parsenodes.h" /* for DropBehavior */
+
+
+/*
+ * Precise semantics of a dependency relationship are specified by the
+ * DependencyType code (which is stored in a "char" field in pg_depend,
+ * so we assign ASCII-code values to the enumeration members).
+ *
+ * In all cases, a dependency relationship indicates that the referenced
+ * object may not be dropped without also dropping the dependent object.
+ * However, there are several subflavors:
+ *
+ * DEPENDENCY_NORMAL ('n'): normal relationship between separately-created
+ * objects. The dependent object may be dropped without affecting the
+ * referenced object. The referenced object may only be dropped by
+ * specifying CASCADE, in which case the dependent object is dropped too.
+ * Example: a table column has a normal dependency on its datatype.
+ *
+ * DEPENDENCY_AUTO ('a'): the dependent object can be dropped separately
+ * from the referenced object, and should be automatically dropped
+ * (regardless of RESTRICT or CASCADE mode) if the referenced object
+ * is dropped.
+ * Example: a named constraint on a table is made auto-dependent on
+ * the table, so that it will go away if the table is dropped.
+ *
+ * DEPENDENCY_INTERNAL ('i'): the dependent object was created as part
+ * of creation of the referenced object, and is really just a part of
+ * its internal implementation. A DROP of the dependent object will be
+ * disallowed outright (we'll tell the user to issue a DROP against the
+ * referenced object, instead). A DROP of the referenced object will be
+ * propagated through to drop the dependent object whether CASCADE is
+ * specified or not.
+ * Example: a trigger that's created to enforce a foreign-key constraint
+ * is made internally dependent on the constraint's pg_constraint entry.
+ *
+ * DEPENDENCY_PIN ('p'): there is no dependent object; this type of entry
+ * is a signal that the system itself depends on the referenced object,
+ * and so that object must never be deleted. Entries of this type are
+ * created only during initdb. The fields for the dependent object
+ * contain zeroes.
+ *
+ * Other dependency flavors may be needed in future.
+ */
+
+typedef enum DependencyType
+{
+ DEPENDENCY_NORMAL = 'n',
+ DEPENDENCY_AUTO = 'a',
+ DEPENDENCY_INTERNAL = 'i',
+ DEPENDENCY_PIN = 'p'
+} DependencyType;
+
+
+/*
+ * The two objects related by a dependency are identified by ObjectAddresses.
+ */
+typedef struct ObjectAddress
+{
+ Oid classId; /* Class Id from pg_class */
+ Oid objectId; /* OID of the object */
+ int32 objectSubId; /* Subitem within the object (column of table) */
+} ObjectAddress;
+
+
+/* in dependency.c */
+
+extern void performDeletion(const ObjectAddress *object,
+ DropBehavior behavior);
+
+/* in pg_depend.c */
+
+extern void recordDependencyOn(const ObjectAddress *depender,
+ const ObjectAddress *referenced,
+ DependencyType behavior);
+
+#endif /* DEPENDENCY_H */
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index a9570dd3ab5..5cf87e2631f 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.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: heap.h,v 1.51 2002/06/20 20:29:43 momjian Exp $
+ * $Id: heap.h,v 1.52 2002/07/12 18:43:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -44,7 +44,7 @@ extern Oid heap_create_with_catalog(const char *relname,
bool relhasoids,
bool allow_system_table_mods);
-extern void heap_drop_with_catalog(Oid rid, bool allow_system_table_mods);
+extern void heap_drop_with_catalog(Oid rid);
extern void heap_truncate(Oid rid);
@@ -58,7 +58,8 @@ extern Node *cookDefault(ParseState *pstate,
int32 atttypmod,
char *attname);
-extern int RemoveCheckConstraint(Relation rel, const char *constrName, bool inh);
+extern int RemoveRelConstraints(Relation rel, const char *constrName,
+ DropBehavior behavior);
extern Form_pg_attribute SystemAttributeDefinition(AttrNumber attno,
bool relhasoids);
diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h
index fb4afac8a2d..738f1079dc2 100644
--- a/src/include/catalog/index.h
+++ b/src/include/catalog/index.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: index.h,v 1.48 2002/06/20 20:29:43 momjian Exp $
+ * $Id: index.h,v 1.49 2002/07/12 18:43:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -35,6 +35,7 @@ extern Oid index_create(Oid heapRelationId,
Oid accessMethodObjectId,
Oid *classObjectId,
bool primary,
+ bool isconstraint,
bool allow_system_table_mods);
extern void index_drop(Oid indexId);
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 9413e5c8586..d4ca744172b 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.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: indexing.h,v 1.68 2002/07/11 07:39:27 ishii Exp $
+ * $Id: indexing.h,v 1.69 2002/07/12 18:43:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -27,8 +27,10 @@
#define Num_pg_attr_indices 2
#define Num_pg_attrdef_indices 1
#define Num_pg_class_indices 2
+#define Num_pg_constraint_indices 3
#define Num_pg_conversion_indices 2
#define Num_pg_database_indices 2
+#define Num_pg_depend_indices 2
#define Num_pg_description_indices 1
#define Num_pg_group_indices 2
#define Num_pg_index_indices 2
@@ -39,7 +41,6 @@
#define Num_pg_opclass_indices 2
#define Num_pg_operator_indices 2
#define Num_pg_proc_indices 2
-#define Num_pg_relcheck_indices 1
#define Num_pg_rewrite_indices 2
#define Num_pg_shadow_indices 2
#define Num_pg_statistic_indices 1
@@ -60,10 +61,15 @@
#define AttributeRelidNumIndex "pg_attribute_relid_attnum_index"
#define ClassNameNspIndex "pg_class_relname_nsp_index"
#define ClassOidIndex "pg_class_oid_index"
+#define ConstraintNameNspIndex "pg_constraint_conname_nsp_index"
+#define ConstraintOidIndex "pg_constraint_oid_index"
+#define ConstraintRelidIndex "pg_constraint_conrelid_index"
#define ConversionDefaultIndex "pg_conversion_default_index"
#define ConversionNameNspIndex "pg_conversion_name_nsp_index"
#define DatabaseNameIndex "pg_database_datname_index"
#define DatabaseOidIndex "pg_database_oid_index"
+#define DependDependerIndex "pg_depend_depender_index"
+#define DependReferenceIndex "pg_depend_reference_index"
#define DescriptionObjIndex "pg_description_o_c_o_index"
#define GroupNameIndex "pg_group_name_index"
#define GroupSysidIndex "pg_group_sysid_index"
@@ -81,7 +87,6 @@
#define OperatorOidIndex "pg_operator_oid_index"
#define ProcedureNameNspIndex "pg_proc_proname_args_nsp_index"
#define ProcedureOidIndex "pg_proc_oid_index"
-#define RelCheckIndex "pg_relcheck_rcrelid_index"
#define RewriteOidIndex "pg_rewrite_oid_index"
#define RewriteRelRulenameIndex "pg_rewrite_rel_rulename_index"
#define ShadowNameIndex "pg_shadow_usename_index"
@@ -102,8 +107,10 @@ extern char *Name_pg_amproc_indices[];
extern char *Name_pg_attr_indices[];
extern char *Name_pg_attrdef_indices[];
extern char *Name_pg_class_indices[];
+extern char *Name_pg_constraint_indices[];
extern char *Name_pg_conversion_indices[];
extern char *Name_pg_database_indices[];
+extern char *Name_pg_depend_indices[];
extern char *Name_pg_description_indices[];
extern char *Name_pg_group_indices[];
extern char *Name_pg_index_indices[];
@@ -114,7 +121,6 @@ extern char *Name_pg_namespace_indices[];
extern char *Name_pg_opclass_indices[];
extern char *Name_pg_operator_indices[];
extern char *Name_pg_proc_indices[];
-extern char *Name_pg_relcheck_indices[];
extern char *Name_pg_rewrite_indices[];
extern char *Name_pg_shadow_indices[];
extern char *Name_pg_statistic_indices[];
@@ -160,10 +166,19 @@ DECLARE_UNIQUE_INDEX(pg_attribute_relid_attnum_index on pg_attribute using btree
DECLARE_UNIQUE_INDEX(pg_class_oid_index on pg_class using btree(oid oid_ops));
DECLARE_UNIQUE_INDEX(pg_class_relname_nsp_index on pg_class using btree(relname name_ops, relnamespace oid_ops));
/* This following index is not used for a cache and is not unique */
+DECLARE_INDEX(pg_constraint_conname_nsp_index on pg_constraint using btree(conname name_ops, connamespace oid_ops));
+/* This following index is not used for a cache and is not unique */
+DECLARE_INDEX(pg_constraint_conrelid_index on pg_constraint using btree(conrelid oid_ops));
+DECLARE_UNIQUE_INDEX(pg_constraint_oid_index on pg_constraint using btree(oid oid_ops));
+/* This following index is not used for a cache and is not unique */
DECLARE_INDEX(pg_conversion_default_index on pg_conversion using btree(connamespace oid_ops, conforencoding int4_ops, contoencoding int4_ops));
DECLARE_UNIQUE_INDEX(pg_conversion_name_nsp_index on pg_conversion using btree(conname name_ops, connamespace oid_ops));
DECLARE_UNIQUE_INDEX(pg_database_datname_index on pg_database using btree(datname name_ops));
DECLARE_UNIQUE_INDEX(pg_database_oid_index on pg_database using btree(oid oid_ops));
+/* This following index is not used for a cache and is not unique */
+DECLARE_INDEX(pg_depend_depender_index on pg_depend using btree(classid oid_ops, objid oid_ops, objsubid int4_ops));
+/* This following index is not used for a cache and is not unique */
+DECLARE_INDEX(pg_depend_reference_index on pg_depend using btree(refclassid oid_ops, refobjid oid_ops, refobjsubid int4_ops));
DECLARE_UNIQUE_INDEX(pg_description_o_c_o_index on pg_description using btree(objoid oid_ops, classoid oid_ops, objsubid int4_ops));
DECLARE_UNIQUE_INDEX(pg_group_name_index on pg_group using btree(groname name_ops));
DECLARE_UNIQUE_INDEX(pg_group_sysid_index on pg_group using btree(grosysid int4_ops));
@@ -183,7 +198,6 @@ DECLARE_UNIQUE_INDEX(pg_operator_oprname_l_r_n_index on pg_operator using btree(
DECLARE_UNIQUE_INDEX(pg_proc_oid_index on pg_proc using btree(oid oid_ops));
DECLARE_UNIQUE_INDEX(pg_proc_proname_args_nsp_index on pg_proc using btree(proname name_ops, pronargs int2_ops, proargtypes oidvector_ops, pronamespace oid_ops));
/* This following index is not used for a cache and is not unique */
-DECLARE_INDEX(pg_relcheck_rcrelid_index on pg_relcheck using btree(rcrelid oid_ops));
DECLARE_UNIQUE_INDEX(pg_rewrite_oid_index on pg_rewrite using btree(oid oid_ops));
DECLARE_UNIQUE_INDEX(pg_rewrite_rel_rulename_index on pg_rewrite using btree(ev_class oid_ops, rulename name_ops));
DECLARE_UNIQUE_INDEX(pg_shadow_usename_index on pg_shadow using btree(usename name_ops));
diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h
new file mode 100644
index 00000000000..ec493146524
--- /dev/null
+++ b/src/include/catalog/pg_constraint.h
@@ -0,0 +1,172 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_constraint.h
+ * definition of the system "constraint" relation (pg_constraint)
+ * along with the relation's initial contents.
+ *
+ *
+ * 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 $
+ *
+ * NOTES
+ * the genbki.sh script reads this file and generates .bki
+ * information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_CONSTRAINT_H
+#define PG_CONSTRAINT_H
+
+/* ----------------
+ * postgres.h contains the system type definintions and the
+ * CATALOG(), BOOTSTRAP and DATA() sugar words so this file
+ * can be read by both genbki.sh and the C compiler.
+ * ----------------
+ */
+
+/* ----------------
+ * pg_constraint definition. cpp turns this into
+ * typedef struct FormData_pg_constraint
+ * ----------------
+ */
+CATALOG(pg_constraint)
+{
+ /*
+ * conname + connamespace is deliberately not unique; we allow, for
+ * example, the same name to be used for constraints of different
+ * relations. This is partly for backwards compatibility with past
+ * Postgres practice, and partly because we don't want to have to obtain
+ * a global lock to generate a globally unique name for a nameless
+ * constraint. We associate a namespace with constraint names only
+ * for SQL92 compatibility.
+ */
+ NameData conname; /* name of this constraint */
+ Oid connamespace; /* OID of namespace containing constraint */
+ char contype; /* constraint type; see codes below */
+ bool condeferrable; /* deferrable constraint? */
+ bool condeferred; /* deferred by default? */
+
+ /*
+ * conrelid and conkey are only meaningful if the constraint applies
+ * to a specific relation (this excludes domain constraints and
+ * assertions). Otherwise conrelid is 0 and conkey is NULL.
+ */
+ Oid conrelid; /* relation this constraint constrains */
+
+ /*
+ * contypid links to the pg_type row for a domain if this is a domain
+ * constraint. Otherwise it's 0.
+ *
+ * For SQL-style global ASSERTIONs, both conrelid and contypid would
+ * be zero. This is not presently supported, however.
+ */
+ Oid contypid; /* domain this constraint constrains */
+
+ /*
+ * These fields, plus confkey, are only meaningful for a foreign-key
+ * constraint. Otherwise confrelid is 0 and the char fields are spaces.
+ */
+ Oid confrelid; /* relation referenced by foreign key */
+ char confupdtype; /* foreign key's ON UPDATE action */
+ char confdeltype; /* foreign key's ON DELETE action */
+ char confmatchtype; /* foreign key's match type */
+
+ /*
+ * VARIABLE LENGTH FIELDS start here. These fields may be NULL, too.
+ */
+
+ /*
+ * Columns of conrelid that the constraint applies to
+ */
+ int2 conkey[1];
+
+ /*
+ * If a foreign key, the referenced columns of confrelid
+ */
+ int2 confkey[1];
+
+ /*
+ * If a check constraint, nodeToString representation of expression
+ */
+ text conbin;
+
+ /*
+ * If a check constraint, source-text representation of expression
+ */
+ text consrc;
+} FormData_pg_constraint;
+
+/* ----------------
+ * Form_pg_constraint corresponds to a pointer to a tuple with
+ * the format of pg_constraint relation.
+ * ----------------
+ */
+typedef FormData_pg_constraint *Form_pg_constraint;
+
+/* ----------------
+ * compiler constants for pg_constraint
+ * ----------------
+ */
+#define Natts_pg_constraint 15
+#define Anum_pg_constraint_conname 1
+#define Anum_pg_constraint_connamespace 2
+#define Anum_pg_constraint_contype 3
+#define Anum_pg_constraint_condeferrable 4
+#define Anum_pg_constraint_condeferred 5
+#define Anum_pg_constraint_conrelid 6
+#define Anum_pg_constraint_contypid 7
+#define Anum_pg_constraint_confrelid 8
+#define Anum_pg_constraint_confupdtype 9
+#define Anum_pg_constraint_confdeltype 10
+#define Anum_pg_constraint_confmatchtype 11
+#define Anum_pg_constraint_conkey 12
+#define Anum_pg_constraint_confkey 13
+#define Anum_pg_constraint_conbin 14
+#define Anum_pg_constraint_consrc 15
+
+
+/* Valid values for contype */
+#define CONSTRAINT_CHECK 'c'
+#define CONSTRAINT_FOREIGN 'f'
+#define CONSTRAINT_PRIMARY 'p'
+#define CONSTRAINT_UNIQUE 'u'
+
+/*
+ * Valid values for confupdtype and confdeltype are the FKCONSTR_ACTION_xxx
+ * constants defined in parsenodes.h. Valid values for confmatchtype are
+ * the FKCONSTR_MATCH_xxx constants defined in parsenodes.h.
+ */
+
+
+/*
+ * prototypes for functions in pg_constraint.c
+ */
+extern Oid CreateConstraintEntry(const char *constraintName,
+ Oid constraintNamespace,
+ char constraintType,
+ bool isDeferrable,
+ bool isDeferred,
+ Oid relId,
+ const int16 *constraintKey,
+ int constraintNKeys,
+ Oid domainId,
+ Oid foreignRelId,
+ const int16 *foreignKey,
+ int foreignNKeys,
+ char foreignUpdateType,
+ char foreignDeleteType,
+ char foreignMatchType,
+ const char *conBin,
+ const char *conSrc);
+
+extern void RemoveConstraintById(Oid conId);
+
+extern bool ConstraintNameIsUsed(Oid relId, Oid relNamespace,
+ const char *cname);
+extern char *GenerateConstraintName(Oid relId, Oid relNamespace,
+ int *counter);
+extern bool ConstraintNameIsGenerated(const char *cname);
+
+#endif /* PG_CONSTRAINT_H */
diff --git a/src/include/catalog/pg_depend.h b/src/include/catalog/pg_depend.h
new file mode 100644
index 00000000000..169a98fc2cc
--- /dev/null
+++ b/src/include/catalog/pg_depend.h
@@ -0,0 +1,93 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_depend.h
+ * definition of the system "dependency" relation (pg_depend)
+ * along with the relation's initial contents.
+ *
+ *
+ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: pg_depend.h,v 1.1 2002/07/12 18:43:19 tgl Exp $
+ *
+ * NOTES
+ * the genbki.sh script reads this file and generates .bki
+ * information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_DEPEND_H
+#define PG_DEPEND_H
+
+/* ----------------
+ * postgres.h contains the system type definitions and the
+ * CATALOG(), BOOTSTRAP and DATA() sugar words so this file
+ * can be read by both genbki.sh and the C compiler.
+ * ----------------
+ */
+
+/* ----------------
+ * pg_depend definition. cpp turns this into
+ * typedef struct FormData_pg_depend
+ * ----------------
+ */
+CATALOG(pg_depend) BKI_WITHOUT_OIDS
+{
+ /*
+ * Identification of the dependent (referencing) object.
+ *
+ * These fields are all zeroes for a DEPENDENCY_PIN entry.
+ */
+ Oid classid; /* OID of table containing object */
+ Oid objid; /* OID of object itself */
+ int4 objsubid; /* column number, or 0 if not used */
+
+ /*
+ * Identification of the independent (referenced) object.
+ */
+ Oid refclassid; /* OID of table containing object */
+ Oid refobjid; /* OID of object itself */
+ int4 refobjsubid; /* column number, or 0 if not used */
+
+ /*
+ * Precise semantics of the relationship are specified by the deptype
+ * field. See DependencyType in catalog/dependency.h.
+ */
+ char deptype; /* see codes in dependency.h */
+} FormData_pg_depend;
+
+/* ----------------
+ * Form_pg_depend corresponds to a pointer to a row with
+ * the format of pg_depend relation.
+ * ----------------
+ */
+typedef FormData_pg_depend *Form_pg_depend;
+
+/* ----------------
+ * compiler constants for pg_depend
+ * ----------------
+ */
+#define Natts_pg_depend 7
+#define Anum_pg_depend_classid 1
+#define Anum_pg_depend_objid 2
+#define Anum_pg_depend_objsubid 3
+#define Anum_pg_depend_refclassid 4
+#define Anum_pg_depend_refobjid 5
+#define Anum_pg_depend_refobjsubid 6
+#define Anum_pg_depend_deptype 7
+
+
+/*
+ * pg_depend has no preloaded contents; system-defined dependencies are
+ * loaded into it during a late stage of the initdb process.
+ *
+ * NOTE: we do not represent all possible dependency pairs in pg_depend;
+ * for example, there's not much value in creating an explicit dependency
+ * from an attribute to its relation. Usually we make a dependency for
+ * cases where the relationship is conditional rather than essential
+ * (for example, not all triggers are dependent on constraints, but all
+ * attributes are dependent on relations) or where the dependency is not
+ * convenient to find from the contents of other catalogs.
+ */
+
+#endif /* PG_DEPEND_H */
diff --git a/src/include/catalog/pg_relcheck.h b/src/include/catalog/pg_relcheck.h
deleted file mode 100644
index f37cbe85b08..00000000000
--- a/src/include/catalog/pg_relcheck.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * pg_relcheck.h
- *
- *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- * NOTES
- * the genbki.sh script reads this file and generates .bki
- * information from the DATA() statements.
- *
- *-------------------------------------------------------------------------
- */
-#ifndef PG_RELCHECK_H
-#define PG_RELCHECK_H
-
-/* ----------------
- * postgres.h contains the system type definintions and the
- * CATALOG(), BOOTSTRAP and DATA() sugar words so this file
- * can be read by both genbki.sh and the C compiler.
- * ----------------
- */
-
-/* ----------------
- * pg_relcheck definition. cpp turns this into
- * typedef struct FormData_pg_relcheck
- * ----------------
- */
-CATALOG(pg_relcheck) BKI_WITHOUT_OIDS
-{
- Oid rcrelid;
- NameData rcname;
- text rcbin;
- text rcsrc;
-} FormData_pg_relcheck;
-
-/* ----------------
- * Form_pg_relcheck corresponds to a pointer to a tuple with
- * the format of pg_relcheck relation.
- * ----------------
- */
-typedef FormData_pg_relcheck *Form_pg_relcheck;
-
-/* ----------------
- * compiler constants for pg_relcheck
- * ----------------
- */
-#define Natts_pg_relcheck 4
-#define Anum_pg_relcheck_rcrelid 1
-#define Anum_pg_relcheck_rcname 2
-#define Anum_pg_relcheck_rcbin 3
-#define Anum_pg_relcheck_rcsrc 4
-
-#endif /* PG_RELCHECK_H */
diff --git a/src/include/commands/comment.h b/src/include/commands/comment.h
index bf2acfcfa0e..0972c13486a 100644
--- a/src/include/commands/comment.h
+++ b/src/include/commands/comment.h
@@ -27,7 +27,7 @@
extern void CommentObject(CommentStmt *stmt);
-extern void DeleteComments(Oid oid, Oid classoid);
+extern void DeleteComments(Oid oid, Oid classoid, int32 subid);
extern void CreateComments(Oid oid, Oid classoid, int32 subid, char *comment);
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index 551deae0956..169ec3f3dff 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.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: defrem.h,v 1.40 2002/07/01 15:27:56 tgl Exp $
+ * $Id: defrem.h,v 1.41 2002/07/12 18:43:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -27,6 +27,7 @@ extern void DefineIndex(RangeVar *heapRelation,
List *attributeList,
bool unique,
bool primary,
+ bool isconstraint,
Expr *predicate,
List *rangetable);
extern void RemoveIndex(RangeVar *relation, DropBehavior behavior);
@@ -39,16 +40,19 @@ extern void ReindexDatabase(const char *databaseName, bool force, bool all);
*/
extern void CreateFunction(CreateFunctionStmt *stmt);
-extern void RemoveFunction(List *functionName, List *argTypes);
+extern void RemoveFunction(RemoveFuncStmt *stmt);
+extern void RemoveFunctionById(Oid funcOid);
extern void DefineOperator(List *names, List *parameters);
extern void RemoveOperator(RemoveOperStmt *stmt);
+extern void RemoveOperatorById(Oid operOid);
extern void DefineAggregate(List *names, List *parameters);
-extern void RemoveAggregate(List *aggName, TypeName *aggType);
+extern void RemoveAggregate(RemoveAggrStmt *stmt);
extern void DefineType(List *names, List *parameters);
extern void RemoveType(List *names, DropBehavior behavior);
+extern void RemoveTypeById(Oid typeOid);
extern void DefineDomain(CreateDomainStmt *stmt);
extern void RemoveDomain(List *names, DropBehavior behavior);
diff --git a/src/include/commands/proclang.h b/src/include/commands/proclang.h
index 25cf7ab5ef8..f4fde9b1083 100644
--- a/src/include/commands/proclang.h
+++ b/src/include/commands/proclang.h
@@ -13,5 +13,6 @@
extern void CreateProceduralLanguage(CreatePLangStmt *stmt);
extern void DropProceduralLanguage(DropPLangStmt *stmt);
+extern void DropProceduralLanguageById(Oid langOid);
#endif /* PROCLANG_H */
diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h
index 9ff6c8e1eac..b9308cd57f3 100644
--- a/src/include/commands/trigger.h
+++ b/src/include/commands/trigger.h
@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: trigger.h,v 1.36 2002/06/20 20:29:49 momjian Exp $
+ * $Id: trigger.h,v 1.37 2002/07/12 18:43:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -101,9 +101,11 @@ typedef struct TriggerData
#define RI_MAX_ARGUMENTS (RI_FIRST_ATTNAME_ARGNO + (RI_MAX_NUMKEYS * 2))
-extern void CreateTrigger(CreateTrigStmt *stmt);
-extern void DropTrigger(Oid relid, const char *trigname);
-extern void RelationRemoveTriggers(Relation rel);
+extern Oid CreateTrigger(CreateTrigStmt *stmt, bool forConstraint);
+
+extern void DropTrigger(Oid relid, const char *trigname,
+ DropBehavior behavior);
+extern void RemoveTriggerById(Oid trigOid);
extern void renametrig(Oid relid, const char *oldname, const char *newname);
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 19693d57b3a..2fec7f66fbd 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.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: parsenodes.h,v 1.184 2002/07/11 07:39:27 ishii Exp $
+ * $Id: parsenodes.h,v 1.185 2002/07/12 18:43:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -762,6 +762,8 @@ typedef struct AlterTableStmt
* M = alter column storage
* D = drop column
* C = add constraint
+ * c = pre-processed add constraint
+ * (local in parser/analyze.c)
* X = drop constraint
* E = create toast table
* U = change owner
@@ -929,31 +931,41 @@ typedef struct Constraint
/* ----------
* Definitions for FOREIGN KEY constraints in CreateStmt
+ *
+ * Note: FKCONSTR_ACTION_xxx values are stored into pg_constraint.confupdtype
+ * and pg_constraint.confdeltype columns; FKCONSTR_MATCH_xxx values are
+ * stored into pg_constraint.confmatchtype. Changing the code values may
+ * require an initdb!
+ *
+ * If skip_validation is true then we skip checking that the existing rows
+ * in the table satisfy the constraint, and just install the catalog entries
+ * for the constraint. This is currently used only during CREATE TABLE
+ * (when we know the table must be empty).
* ----------
*/
-#define FKCONSTR_ON_KEY_NOACTION 0x0000
-#define FKCONSTR_ON_KEY_RESTRICT 0x0001
-#define FKCONSTR_ON_KEY_CASCADE 0x0002
-#define FKCONSTR_ON_KEY_SETNULL 0x0004
-#define FKCONSTR_ON_KEY_SETDEFAULT 0x0008
-
-#define FKCONSTR_ON_DELETE_MASK 0x000F
-#define FKCONSTR_ON_DELETE_SHIFT 0
+#define FKCONSTR_ACTION_NOACTION 'a'
+#define FKCONSTR_ACTION_RESTRICT 'r'
+#define FKCONSTR_ACTION_CASCADE 'c'
+#define FKCONSTR_ACTION_SETNULL 'n'
+#define FKCONSTR_ACTION_SETDEFAULT 'd'
-#define FKCONSTR_ON_UPDATE_MASK 0x00F0
-#define FKCONSTR_ON_UPDATE_SHIFT 4
+#define FKCONSTR_MATCH_FULL 'f'
+#define FKCONSTR_MATCH_PARTIAL 'p'
+#define FKCONSTR_MATCH_UNSPECIFIED 'u'
typedef struct FkConstraint
{
NodeTag type;
- char *constr_name; /* Constraint name */
+ char *constr_name; /* Constraint name, or NULL if unnamed */
RangeVar *pktable; /* Primary key table */
List *fk_attrs; /* Attributes of foreign key */
List *pk_attrs; /* Corresponding attrs in PK table */
- char *match_type; /* FULL or PARTIAL */
- int32 actions; /* ON DELETE/UPDATE actions */
+ char fk_matchtype; /* FULL, PARTIAL, UNSPECIFIED */
+ char fk_upd_action; /* ON UPDATE action */
+ char fk_del_action; /* ON DELETE action */
bool deferrable; /* DEFERRABLE */
bool initdeferred; /* INITIALLY DEFERRED */
+ bool skip_validation; /* skip validation of existing rows? */
} FkConstraint;
/* ----------------------
@@ -1201,6 +1213,7 @@ typedef struct IndexStmt
* transformStmt() */
bool unique; /* is index unique? */
bool primary; /* is index on primary key? */
+ bool isconstraint; /* is it from a CONSTRAINT clause? */
} IndexStmt;
/* ----------------------
diff --git a/src/include/rewrite/rewriteRemove.h b/src/include/rewrite/rewriteRemove.h
index 3af9c150481..664625982d5 100644
--- a/src/include/rewrite/rewriteRemove.h
+++ b/src/include/rewrite/rewriteRemove.h
@@ -7,14 +7,18 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: rewriteRemove.h,v 1.14 2002/06/20 20:29:52 momjian Exp $
+ * $Id: rewriteRemove.h,v 1.15 2002/07/12 18:43:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef REWRITEREMOVE_H
#define REWRITEREMOVE_H
-extern void RemoveRewriteRule(Oid owningRel, const char *ruleName);
-extern void RelationRemoveRules(Oid relid);
+#include "nodes/parsenodes.h"
+
+
+extern void RemoveRewriteRule(Oid owningRel, const char *ruleName,
+ DropBehavior behavior);
+extern void RemoveRewriteRuleById(Oid ruleOid);
#endif /* REWRITEREMOVE_H */
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index ca361ebc2cc..dcc310aab5e 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: lsyscache.h,v 1.54 2002/07/06 20:16:36 momjian Exp $
+ * $Id: lsyscache.h,v 1.55 2002/07/12 18:43:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -42,6 +42,7 @@ extern Oid get_func_rettype(Oid funcid);
extern bool get_func_retset(Oid funcid);
extern char func_volatile(Oid funcid);
extern Oid get_relname_relid(const char *relname, Oid relnamespace);
+extern Oid get_system_catalog_relid(const char *catname);
extern char *get_rel_name(Oid relid);
extern Oid get_rel_namespace(Oid relid);
extern Oid get_rel_type_id(Oid relid);
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index f2e31919cc5..2dc64f3ccfa 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -340,8 +340,6 @@ ERROR: UNIQUE constraint matching given keys for referenced table "tmp4" not fo
DROP TABLE tmp5;
DROP TABLE tmp4;
DROP TABLE tmp3;
-NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "tmp2"
-NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "tmp2"
DROP TABLE tmp2;
-- Foreign key adding test with mixed types
-- Note: these tables are TEMP to avoid name conflicts when this test
@@ -369,9 +367,9 @@ NOTICE: ALTER TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
-- As should this
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable(ptest1);
NOTICE: ALTER TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
-DROP TABLE pktable;
-NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "fktable"
-NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "fktable"
+DROP TABLE pktable cascade;
+NOTICE: Drop cascades to constraint $2 on table fktable
+NOTICE: Drop cascades to constraint $1 on table fktable
DROP TABLE fktable;
CREATE TEMP TABLE PKTABLE (ptest1 int, ptest2 inet,
PRIMARY KEY(ptest1, ptest2));
@@ -382,16 +380,16 @@ ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2) references pktable;
NOTICE: ALTER TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
ERROR: Unable to identify an operator '=' for types 'cidr' and 'integer'
You will have to retype this query using an explicit cast
--- Again, so should this...
DROP TABLE FKTABLE;
+-- Again, so should this...
CREATE TEMP TABLE FKTABLE (ftest1 cidr, ftest2 timestamp);
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2)
references pktable(ptest1, ptest2);
NOTICE: ALTER TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
ERROR: Unable to identify an operator '=' for types 'cidr' and 'integer'
You will have to retype this query using an explicit cast
--- This fails because we mixed up the column ordering
DROP TABLE FKTABLE;
+-- This fails because we mixed up the column ordering
CREATE TEMP TABLE FKTABLE (ftest1 int, ftest2 inet);
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2)
references pktable(ptest2, ptest1);
@@ -445,7 +443,7 @@ create table atacc1 (test int check (test>3), test2 int);
alter table atacc1 add check (test2>test);
-- should fail for $2
insert into atacc1 (test2, test) values (3, 4);
-ERROR: ExecInsert: rejected due to CHECK constraint $2
+ERROR: ExecInsert: rejected due to CHECK constraint $1
drop table atacc1;
-- inheritance related tests
create table atacc1 (test int);
@@ -628,7 +626,7 @@ alter table atacc1 add constraint "atacc1_pkey" primary key (test);
NOTICE: ALTER TABLE / ADD PRIMARY KEY will create implicit index 'atacc1_pkey' for table 'atacc1'
alter table atacc1 alter column test drop not null;
ERROR: ALTER TABLE: Attribute "test" is in a primary key
-drop index atacc1_pkey;
+alter table atacc1 drop constraint "atacc1_pkey";
alter table atacc1 alter column test drop not null;
insert into atacc1 values (null);
alter table atacc1 alter test set not null;
diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out
index 75c83fc7ebf..915b420b9a2 100644
--- a/src/test/regress/expected/domain.out
+++ b/src/test/regress/expected/domain.out
@@ -1,11 +1,13 @@
-- Test Comment / Drop
create domain domaindroptest int4;
comment on domain domaindroptest is 'About to drop this..';
+-- currently this will be disallowed
create domain basetypetest domaindroptest;
ERROR: DefineDomain: domaindroptest is not a basetype
-drop domain domaindroptest cascade;
-ERROR: DROP DOMAIN does not support the CASCADE keyword
drop domain domaindroptest;
+-- this should fail because already gone
+drop domain domaindroptest cascade;
+ERROR: Type "domaindroptest" does not exist
-- TEST Domains.
create domain domainvarchar varchar(5);
create domain domainnumeric numeric(8,2);
diff --git a/src/test/regress/expected/foreign_key.out b/src/test/regress/expected/foreign_key.out
index b8be87228bb..f5272891b9f 100644
--- a/src/test/regress/expected/foreign_key.out
+++ b/src/test/regress/expected/foreign_key.out
@@ -22,7 +22,7 @@ INSERT INTO FKTABLE VALUES (3, 4);
INSERT INTO FKTABLE VALUES (NULL, 1);
-- Insert a failed row into FK TABLE
INSERT INTO FKTABLE VALUES (100, 2);
-ERROR: <unnamed> referential integrity violation - key referenced from fktable not found in pktable
+ERROR: $1 referential integrity violation - key referenced from fktable not found in pktable
-- Check FKTABLE
SELECT * FROM FKTABLE;
ftest1 | ftest2
@@ -55,9 +55,8 @@ SELECT * FROM FKTABLE;
1 | 3
(3 rows)
-DROP TABLE PKTABLE;
-NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "fktable"
DROP TABLE FKTABLE;
+DROP TABLE PKTABLE;
--
-- check set NULL and table constraint on multiple columns
--
@@ -138,8 +137,8 @@ SELECT * FROM FKTABLE;
| | 8
(5 rows)
-DROP TABLE PKTABLE;
-NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "fktable"
+DROP TABLE PKTABLE CASCADE;
+NOTICE: Drop cascades to constraint constrname on table fktable
DROP TABLE FKTABLE;
--
-- check set default and table constraint on multiple columns
@@ -223,8 +222,13 @@ SELECT * FROM FKTABLE;
-1 | -2 | 8
(5 rows)
+-- this should fail for lack of CASCADE
DROP TABLE PKTABLE;
-NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "fktable"
+NOTICE: constraint constrname2 on table fktable depends on table pktable
+ERROR: Cannot drop table pktable because other objects depend on it
+ Use DROP ... CASCADE to drop the dependent objects too
+DROP TABLE PKTABLE CASCADE;
+NOTICE: Drop cascades to constraint constrname2 on table fktable
DROP TABLE FKTABLE;
--
-- First test, check with no on delete or on update
@@ -246,7 +250,7 @@ INSERT INTO FKTABLE VALUES (3, 4);
INSERT INTO FKTABLE VALUES (NULL, 1);
-- Insert a failed row into FK TABLE
INSERT INTO FKTABLE VALUES (100, 2);
-ERROR: <unnamed> referential integrity violation - key referenced from fktable not found in pktable
+ERROR: $1 referential integrity violation - key referenced from fktable not found in pktable
-- Check FKTABLE
SELECT * FROM FKTABLE;
ftest1 | ftest2
@@ -270,7 +274,7 @@ SELECT * FROM PKTABLE;
-- Delete a row from PK TABLE (should fail)
DELETE FROM PKTABLE WHERE ptest1=1;
-ERROR: <unnamed> referential integrity violation - key in pktable still referenced from fktable
+ERROR: $1 referential integrity violation - key in pktable still referenced from fktable
-- Delete a row from PK TABLE (should succeed)
DELETE FROM PKTABLE WHERE ptest1=5;
-- Check PKTABLE for deletes
@@ -285,7 +289,7 @@ SELECT * FROM PKTABLE;
-- Update a row from PK TABLE (should fail)
UPDATE PKTABLE SET ptest1=0 WHERE ptest1=2;
-ERROR: <unnamed> referential integrity violation - key in pktable still referenced from fktable
+ERROR: $1 referential integrity violation - key in pktable still referenced from fktable
-- Update a row from PK TABLE (should succeed)
UPDATE PKTABLE SET ptest1=0 WHERE ptest1=4;
-- Check PKTABLE for updates
@@ -298,9 +302,8 @@ SELECT * FROM PKTABLE;
0 | Test4
(4 rows)
-DROP TABLE PKTABLE;
-NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "fktable"
DROP TABLE FKTABLE;
+DROP TABLE PKTABLE;
-- MATCH unspecified
-- Base test restricting update/delete
CREATE TABLE PKTABLE ( ptest1 int, ptest2 int, ptest3 int, ptest4 text, PRIMARY KEY(ptest1, ptest2, ptest3) );
@@ -363,8 +366,6 @@ SELECT * from FKTABLE;
(5 rows)
DROP TABLE FKTABLE;
-NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable"
-NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable"
DROP TABLE PKTABLE;
-- cascade update/delete
CREATE TABLE PKTABLE ( ptest1 int, ptest2 int, ptest3 int, ptest4 text, PRIMARY KEY(ptest1, ptest2, ptest3) );
@@ -462,8 +463,6 @@ SELECT * from FKTABLE;
(4 rows)
DROP TABLE FKTABLE;
-NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable"
-NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable"
DROP TABLE PKTABLE;
-- set null update / set default delete
CREATE TABLE PKTABLE ( ptest1 int, ptest2 int, ptest3 int, ptest4 text, PRIMARY KEY(ptest1, ptest2, ptest3) );
@@ -568,8 +567,6 @@ SELECT * from FKTABLE;
(6 rows)
DROP TABLE FKTABLE;
-NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable"
-NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable"
DROP TABLE PKTABLE;
-- set default update / set null delete
CREATE TABLE PKTABLE ( ptest1 int, ptest2 int, ptest3 int, ptest4 text, PRIMARY KEY(ptest1, ptest2, ptest3) );
@@ -687,8 +684,6 @@ SELECT * from FKTABLE;
(7 rows)
DROP TABLE FKTABLE;
-NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable"
-NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable"
DROP TABLE PKTABLE;
CREATE TABLE PKTABLE (ptest1 int PRIMARY KEY);
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index 'pktable_pkey' for table 'pktable'
@@ -734,14 +729,10 @@ ERROR: Unable to identify an operator '=' for types 'inet' and 'integer'
CREATE TABLE FKTABLE (ftest1 varchar REFERENCES pktable);
NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
DROP TABLE FKTABLE;
-NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable"
-NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable"
-- As should this
CREATE TABLE FKTABLE (ftest1 varchar REFERENCES pktable(ptest1));
NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
DROP TABLE FKTABLE;
-NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable"
-NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable"
DROP TABLE PKTABLE;
-- Two columns, two tables
CREATE TABLE PKTABLE (ptest1 int, ptest2 inet, PRIMARY KEY(ptest1, ptest2));
@@ -775,14 +766,10 @@ ERROR: Unable to identify an operator '=' for types 'integer' and 'inet'
CREATE TABLE FKTABLE (ftest1 int, ftest2 inet, FOREIGN KEY(ftest2, ftest1) REFERENCES pktable(ptest2, ptest1));
NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
DROP TABLE FKTABLE;
-NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable"
-NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable"
-- As does this
CREATE TABLE FKTABLE (ftest1 int, ftest2 inet, FOREIGN KEY(ftest1, ftest2) REFERENCES pktable(ptest1, ptest2));
NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
DROP TABLE FKTABLE;
-NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable"
-NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable"
DROP TABLE PKTABLE;
-- Two columns, same table
-- Make sure this still works...
@@ -832,25 +819,23 @@ insert into pktable(base1) values (1);
insert into pktable(base1) values (2);
-- let's insert a non-existant fktable value
insert into fktable(ftest1) values (3);
-ERROR: <unnamed> referential integrity violation - key referenced from fktable not found in pktable
+ERROR: $1 referential integrity violation - key referenced from fktable not found in pktable
-- let's make a valid row for that
insert into pktable(base1) values (3);
insert into fktable(ftest1) values (3);
-- let's try removing a row that should fail from pktable
delete from pktable where base1>2;
-ERROR: <unnamed> referential integrity violation - key in pktable still referenced from fktable
+ERROR: $1 referential integrity violation - key in pktable still referenced from fktable
-- okay, let's try updating all of the base1 values to *4
-- which should fail.
update pktable set base1=base1*4;
-ERROR: <unnamed> referential integrity violation - key in pktable still referenced from fktable
+ERROR: $1 referential integrity violation - key in pktable still referenced from fktable
-- okay, let's try an update that should work.
update pktable set base1=base1*4 where base1<3;
-- and a delete that should work
delete from pktable where base1>3;
-- cleanup
drop table fktable;
-NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable"
-NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable"
delete from pktable;
-- Now 2 columns 2 tables, matching types
create table fktable (ftest1 int, ftest2 int, foreign key(ftest1, ftest2) references pktable(base1, ptest1));
@@ -860,25 +845,23 @@ insert into pktable(base1, ptest1) values (1, 1);
insert into pktable(base1, ptest1) values (2, 2);
-- let's insert a non-existant fktable value
insert into fktable(ftest1, ftest2) values (3, 1);
-ERROR: <unnamed> referential integrity violation - key referenced from fktable not found in pktable
+ERROR: $1 referential integrity violation - key referenced from fktable not found in pktable
-- let's make a valid row for that
insert into pktable(base1,ptest1) values (3, 1);
insert into fktable(ftest1, ftest2) values (3, 1);
-- let's try removing a row that should fail from pktable
delete from pktable where base1>2;
-ERROR: <unnamed> referential integrity violation - key in pktable still referenced from fktable
+ERROR: $1 referential integrity violation - key in pktable still referenced from fktable
-- okay, let's try updating all of the base1 values to *4
-- which should fail.
update pktable set base1=base1*4;
-ERROR: <unnamed> referential integrity violation - key in pktable still referenced from fktable
+ERROR: $1 referential integrity violation - key in pktable still referenced from fktable
-- okay, let's try an update that should work.
update pktable set base1=base1*4 where base1<3;
-- and a delete that should work
delete from pktable where base1>3;
-- cleanup
drop table fktable;
-NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable"
-NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable"
drop table pktable;
drop table pktable_base;
-- Now we'll do one all in 1 table with 2 columns of matching types
@@ -893,13 +876,13 @@ insert into pktable (base1, ptest1, base2, ptest2) values (2, 2, 2, 1);
insert into pktable (base1, ptest1, base2, ptest2) values (1, 3, 2, 2);
-- fails (3,2) isn't in base1, ptest1
insert into pktable (base1, ptest1, base2, ptest2) values (2, 3, 3, 2);
-ERROR: <unnamed> referential integrity violation - key referenced from pktable not found in pktable
+ERROR: $1 referential integrity violation - key referenced from pktable not found in pktable
-- fails (2,2) is being referenced
delete from pktable where base1=2;
-ERROR: <unnamed> referential integrity violation - key in pktable still referenced from pktable
+ERROR: $1 referential integrity violation - key in pktable still referenced from pktable
-- fails (1,1) is being referenced (twice)
update pktable set base1=3 where base1=1;
-ERROR: <unnamed> referential integrity violation - key referenced from pktable not found in pktable
+ERROR: $1 referential integrity violation - key referenced from pktable not found in pktable
-- this sequence of two deletes will work, since after the first there will be no (2,*) references
delete from pktable where base2=2;
delete from pktable where base1=2;
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 37544dd0ccb..2e0cf3a0330 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -38,8 +38,10 @@ SELECT relname, relhasindex
pg_attrdef | t
pg_attribute | t
pg_class | t
+ pg_constraint | t
pg_conversion | t
pg_database | t
+ pg_depend | t
pg_description | t
pg_group | t
pg_index | t
@@ -50,7 +52,6 @@ SELECT relname, relhasindex
pg_opclass | t
pg_operator | t
pg_proc | t
- pg_relcheck | t
pg_rewrite | t
pg_shadow | t
pg_statistic | t
@@ -61,5 +62,5 @@ SELECT relname, relhasindex
shighway | t
tenk1 | t
tenk2 | t
-(51 rows)
+(52 rows)
diff --git a/src/test/regress/output/constraints.source b/src/test/regress/output/constraints.source
index 3dafd9a8771..8935dae8734 100644
--- a/src/test/regress/output/constraints.source
+++ b/src/test/regress/output/constraints.source
@@ -123,7 +123,7 @@ INSERT INTO INSERT_TBL(y) VALUES ('Y');
ERROR: ExecInsert: rejected due to CHECK constraint insert_con
INSERT INTO INSERT_TBL(y) VALUES ('Y');
INSERT INTO INSERT_TBL(x,z) VALUES (1, -2);
-ERROR: ExecInsert: rejected due to CHECK constraint $2
+ERROR: ExecInsert: rejected due to CHECK constraint $1
INSERT INTO INSERT_TBL(z,x) VALUES (-7, 7);
INSERT INTO INSERT_TBL VALUES (5, 'check failed', -5);
ERROR: ExecInsert: rejected due to CHECK constraint insert_con
@@ -139,7 +139,7 @@ SELECT '' AS four, * FROM INSERT_TBL;
(4 rows)
INSERT INTO INSERT_TBL(y,z) VALUES ('check failed', 4);
-ERROR: ExecInsert: rejected due to CHECK constraint $2
+ERROR: ExecInsert: rejected due to CHECK constraint $1
INSERT INTO INSERT_TBL(x,y) VALUES (5, 'check failed');
ERROR: ExecInsert: rejected due to CHECK constraint insert_con
INSERT INTO INSERT_TBL(x,y) VALUES (5, '!check failed');
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index a46e5aaa22f..f39998073d7 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -250,7 +250,7 @@ CREATE TEMP TABLE FKTABLE (ftest1 varchar);
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable;
-- As should this
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable(ptest1);
-DROP TABLE pktable;
+DROP TABLE pktable cascade;
DROP TABLE fktable;
CREATE TEMP TABLE PKTABLE (ptest1 int, ptest2 inet,
@@ -258,13 +258,13 @@ CREATE TEMP TABLE PKTABLE (ptest1 int, ptest2 inet,
-- This should fail, because we just chose really odd types
CREATE TEMP TABLE FKTABLE (ftest1 cidr, ftest2 timestamp);
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2) references pktable;
--- Again, so should this...
DROP TABLE FKTABLE;
+-- Again, so should this...
CREATE TEMP TABLE FKTABLE (ftest1 cidr, ftest2 timestamp);
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2)
references pktable(ptest1, ptest2);
--- This fails because we mixed up the column ordering
DROP TABLE FKTABLE;
+-- This fails because we mixed up the column ordering
CREATE TEMP TABLE FKTABLE (ftest1 int, ftest2 inet);
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2)
references pktable(ptest2, ptest1);
@@ -486,7 +486,7 @@ alter table foo alter column bar drop not null;
create table atacc1 (test int not null);
alter table atacc1 add constraint "atacc1_pkey" primary key (test);
alter table atacc1 alter column test drop not null;
-drop index atacc1_pkey;
+alter table atacc1 drop constraint "atacc1_pkey";
alter table atacc1 alter column test drop not null;
insert into atacc1 values (null);
alter table atacc1 alter test set not null;
diff --git a/src/test/regress/sql/domain.sql b/src/test/regress/sql/domain.sql
index 055c377decb..77dccb2aaca 100644
--- a/src/test/regress/sql/domain.sql
+++ b/src/test/regress/sql/domain.sql
@@ -4,11 +4,14 @@
create domain domaindroptest int4;
comment on domain domaindroptest is 'About to drop this..';
+-- currently this will be disallowed
create domain basetypetest domaindroptest;
-drop domain domaindroptest cascade;
drop domain domaindroptest;
+-- this should fail because already gone
+drop domain domaindroptest cascade;
+
-- TEST Domains.
diff --git a/src/test/regress/sql/foreign_key.sql b/src/test/regress/sql/foreign_key.sql
index a7cb5842233..c6b50e4b32e 100644
--- a/src/test/regress/sql/foreign_key.sql
+++ b/src/test/regress/sql/foreign_key.sql
@@ -40,8 +40,8 @@ UPDATE PKTABLE SET ptest1=1 WHERE ptest1=2;
-- Check FKTABLE for update of matched row
SELECT * FROM FKTABLE;
-DROP TABLE PKTABLE;
DROP TABLE FKTABLE;
+DROP TABLE PKTABLE;
--
-- check set NULL and table constraint on multiple columns
@@ -92,7 +92,7 @@ UPDATE PKTABLE SET ptest1=1 WHERE ptest1=2;
-- Check FKTABLE for update of matched row
SELECT * FROM FKTABLE;
-DROP TABLE PKTABLE;
+DROP TABLE PKTABLE CASCADE;
DROP TABLE FKTABLE;
--
@@ -147,7 +147,9 @@ UPDATE PKTABLE SET ptest1=1 WHERE ptest1=2;
-- Check FKTABLE for update of matched row
SELECT * FROM FKTABLE;
+-- this should fail for lack of CASCADE
DROP TABLE PKTABLE;
+DROP TABLE PKTABLE CASCADE;
DROP TABLE FKTABLE;
@@ -197,8 +199,8 @@ UPDATE PKTABLE SET ptest1=0 WHERE ptest1=4;
-- Check PKTABLE for updates
SELECT * FROM PKTABLE;
-DROP TABLE PKTABLE;
DROP TABLE FKTABLE;
+DROP TABLE PKTABLE;
-- MATCH unspecified