]> granicus.if.org Git - postgresql/commitdiff
Added ALTER TABLE ... ADD CONSTRAINT (provided by Stephan Szabo).
authorJan Wieck <JanWieck@Yahoo.com>
Fri, 4 Feb 2000 18:49:34 +0000 (18:49 +0000)
committerJan Wieck <JanWieck@Yahoo.com>
Fri, 4 Feb 2000 18:49:34 +0000 (18:49 +0000)
Added constraint dumping capability to pg_dump (also from Stephan)

Fixed DROP TABLE -> RelationBuildTriggers: 2 record(s) not found for rel
error.

Fixed little error in gram.y I made the last days.

Jan

src/backend/commands/command.c
src/backend/commands/trigger.c
src/backend/parser/analyze.c
src/backend/parser/gram.y
src/bin/pg_dump/pg_dump.c

index ef6b66d9c334bfa59b81aeea42a95720c44dfa09..8f36515c8cbe12cc8f40b9a4ddcc40ab0878aa1c 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.67 2000/01/29 16:58:34 petere Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.68 2000/02/04 18:49:31 wieck Exp $
  *
  * NOTES
  *       The PortalExecutorHeapMemory crap needs to be eliminated
@@ -34,6 +34,7 @@
 #include "commands/rename.h"
 #include "executor/execdefs.h"
 #include "executor/executor.h"
+#include "executor/spi.h"
 #include "catalog/heap.h"
 #include "miscadmin.h"
 #include "optimizer/prep.h"
@@ -41,7 +42,7 @@
 #include "utils/builtins.h"
 #include "utils/syscache.h"
 #include "utils/temprel.h"
-
+#include "commands/trigger.h"
 
 /* ----------------
  *             PortalExecutorHeapMemory stuff
@@ -688,7 +689,95 @@ void
 AlterTableAddConstraint(const char *relationName,
                         bool inh, Node *newConstraint)
 {
-    elog(ERROR, "ALTER TABLE / ADD CONSTRAINT is not implemented");
+       if (newConstraint == NULL) 
+               elog(ERROR, "ALTER TABLE / ADD CONSTRAINT passed invalid constraint.");
+
+       switch (nodeTag(newConstraint)) 
+       {
+               case T_Constraint:
+                       elog(ERROR, "ALTER TABLE / ADD CONSTRAINT is not implemented");
+               case T_FkConstraint:
+                       {
+                               FkConstraint *fkconstraint=(FkConstraint *)newConstraint;
+                               Relation rel, classrel;
+                               HeapScanDesc scan;
+                               HeapTuple tuple;
+                               Trigger trig;
+                               List *list;
+                               int count;
+
+                               /* 
+                                * Grab an exclusive lock on the pk table, so that someone
+                                * doesn't delete rows out from under us.  
+                                */
+
+                               rel = heap_openr(fkconstraint->pktable_name, AccessExclusiveLock);
+                               heap_close(rel, NoLock);
+
+                               /* 
+                                * Grab an exclusive lock on the fk table, and then scan through
+                                * each tuple, calling the RI_FKey_Match_Ins (insert trigger)
+                                * as if that tuple had just been inserted.  If any of those
+                                * fail, it should elog(ERROR) and that's that.
+                                */
+                               rel = heap_openr(relationName, AccessExclusiveLock);
+                               trig.tgoid = 0;
+                               trig.tgname = "<unknown>";
+                               trig.tgfoid = 0; 
+                               trig.tgtype = 0;
+                               trig.tgenabled = TRUE;
+                               trig.tgisconstraint = TRUE;
+                               trig.tginitdeferred = FALSE;
+                               trig.tgdeferrable = FALSE;
+
+                               trig.tgargs = (char **)palloc(
+                                               sizeof(char *) * (4 + length(fkconstraint->fk_attrs) 
+                                                                   + length(fkconstraint->pk_attrs)));
+                               
+                               trig.tgargs[0] = "<unnamed>";
+                               trig.tgargs[1] = (char *)relationName;
+                               trig.tgargs[2] = fkconstraint->pktable_name;
+                               trig.tgargs[3] = fkconstraint->match_type;
+                               count = 4;
+                               foreach (list, fkconstraint->fk_attrs) 
+                               {
+                                       Ident *fk_at = lfirst(list);
+                                       trig.tgargs[count++] = fk_at->name;
+                               }
+                               foreach (list, fkconstraint->pk_attrs) 
+                               {
+                                       Ident *pk_at = lfirst(list);
+                                       trig.tgargs[count++] = pk_at->name;
+                               }
+                               trig.tgnargs = count;
+
+                               scan = heap_beginscan(rel, false, SnapshotNow, 0, NULL);
+                               AssertState(scan!=NULL);
+
+                               while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
+                               {
+                                       TriggerData newtrigdata;
+                                       newtrigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
+                                       newtrigdata.tg_relation = rel;
+                                       newtrigdata.tg_trigtuple = tuple;
+                                       newtrigdata.tg_newtuple = NULL;
+                                       newtrigdata.tg_trigger = &trig;
+
+                                       CurrentTriggerData = &newtrigdata;
+
+                                       RI_FKey_check_ins(NULL);
+
+                                       /* Make a call to the check function */
+                               }
+                               heap_endscan(scan);
+                               heap_close(rel, NoLock);         /* close rel but keep lock! */
+
+                               pfree(trig.tgargs);
+                       }
+                       break;
+               default:
+                       elog(ERROR, "ALTER TABLE / ADD CONSTRAINT unable to determine type of constraint passed");
+       }
 }
 
 
index f3c919a1ed9ac506564af98a30d7ce0133571878..04c20e1827daf639a9d5e49b9846b5bb1c1ec309 100644 (file)
@@ -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.56 2000/01/31 04:35:49 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.57 2000/02/04 18:49:31 wieck Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -399,6 +399,15 @@ RelationRemoveTriggers(Relation rel)
 
                DropTrigger(&stmt);
 
+               /* ----------
+                * Need to do a command counter increment here to show up
+                * new pg_class.reltriggers in the next loop invocation already
+                * (there are multiple referential integrity action
+                * triggers for the same FK table defined on the PK table).
+                * ----------
+                */
+               CommandCounterIncrement();
+
                pfree(stmt.relname);
                pfree(stmt.trigname);
        }
index fd3dda8f17e337899fe9c3e79236d4752d0cf9af..d44f0df091d481ed7ce7da13e4b862b559f22ad9 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *     $Id: analyze.c,v 1.134 2000/01/27 18:11:35 tgl Exp $
+ *     $Id: analyze.c,v 1.135 2000/02/04 18:49:32 wieck Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -40,6 +40,7 @@ static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt);
 static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt);
 static Query *transformCursorStmt(ParseState *pstate, SelectStmt *stmt);
 static Query *transformCreateStmt(ParseState *pstate, CreateStmt *stmt);
+static Query *transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt);
 
 static void transformForUpdate(Query *qry, List *forUpdate);
 static void transformFkeyGetPrimaryKey(FkConstraint *fkconstraint);
@@ -181,13 +182,7 @@ transformStmt(ParseState *pstate, Node *parseTree)
 
                case T_AlterTableStmt:
                        {
-                               AlterTableStmt *n = (AlterTableStmt *) parseTree;
-
-                               result = makeNode(Query);
-                               result->commandType = CMD_UTILITY;
-                               if (n->subtype == 'A') /* ADD COLUMN */
-                                       transformColumnType(pstate, (ColumnDef *) n->def);
-                               result->utilityStmt = (Node *) parseTree;
+                               result = transformAlterTableStmt(pstate, (AlterTableStmt *) parseTree);
                        }
                        break;
 
@@ -1459,6 +1454,254 @@ transformCursorStmt(ParseState *pstate, SelectStmt *stmt)
        return qry;
 }
 
+/*
+ * tranformAlterTableStmt -
+ *     transform an Alter Table Statement
+ *
+ */
+static Query *
+transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt) 
+{
+       Query *qry;
+       qry = makeNode(Query);
+       qry->commandType = CMD_UTILITY;
+
+       /* 
+        * The only subtypes that currently have special handling are
+        *      'A'dd column and Add 'C'onstraint.  In addition, right now
+        *      only Foreign Key 'C'onstraints have a special transformation.
+        *
+        */
+       switch (stmt->subtype) {
+               case 'A':
+                       transformColumnType(pstate, (ColumnDef *) stmt->def);
+                       break;
+               case 'C':  
+                       if (stmt->def && nodeTag(stmt->def) == T_FkConstraint )
+                       {
+                               CreateTrigStmt     *fk_trigger;
+                               List                       *fk_attr;
+                               List                       *pk_attr;
+                               Ident                      *id;
+                               FkConstraint    *fkconstraint;
+                               extras_after = NIL;
+                               elog(NOTICE, "ALTER TABLE ... ADD CONSTRAINT will create implicit trigger(s) for FOREIGN KEY check(s)");
+
+                               fkconstraint = (FkConstraint *) stmt->def;
+                               /*
+                                * If the constraint has no name, set it to <unnamed>
+                                *
+                                */
+                               if (fkconstraint->constr_name == NULL)
+                                       fkconstraint->constr_name = "<unnamed>";
+
+                               /*
+                                * If the attribute list for the referenced table was
+                                * omitted, lookup for the definition of the primary key
+                                *
+                                */
+                               if (fkconstraint->fk_attrs != NIL && fkconstraint->pk_attrs == NIL)
+                                       transformFkeyGetPrimaryKey(fkconstraint);
+
+                               /*
+                                * Build a CREATE CONSTRAINT TRIGGER statement for the CHECK
+                                * action.
+                                *
+                                */
+                               fk_trigger = (CreateTrigStmt *)makeNode(CreateTrigStmt);
+                               fk_trigger->trigname            = fkconstraint->constr_name;
+                               fk_trigger->relname                     = stmt->relname;
+                               fk_trigger->funcname            = "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->constrrelname       = fkconstraint->pktable_name;
+
+                               fk_trigger->args                = NIL;
+                               fk_trigger->args = lappend(fk_trigger->args,
+                                                                               fkconstraint->constr_name);
+                               fk_trigger->args = lappend(fk_trigger->args,
+                                                                               stmt->relname);
+                               fk_trigger->args = lappend(fk_trigger->args,
+                                                                               fkconstraint->pktable_name);
+                               fk_trigger->args = lappend(fk_trigger->args,
+                                                                               fkconstraint->match_type);
+                               fk_attr = fkconstraint->fk_attrs;
+                               pk_attr = fkconstraint->pk_attrs;
+                               if (length(fk_attr) != length(pk_attr))
+                               {
+                                       elog(NOTICE, "Illegal FOREIGN KEY definition REFERENCES \"%s\"",
+                                                               fkconstraint->pktable_name);
+                                       elog(ERROR, "number of key attributes in referenced table must be equal to foreign key");
+                               }
+                               while (fk_attr != NIL)
+                               {
+                                       id = (Ident *)lfirst(fk_attr);
+                                       fk_trigger->args = lappend(fk_trigger->args, id->name);
+       
+                                       id = (Ident *)lfirst(pk_attr);
+                                       fk_trigger->args = lappend(fk_trigger->args, id->name);
+
+                                       fk_attr = lnext(fk_attr);
+                                       pk_attr = lnext(pk_attr);
+                               }
+
+                               extras_after = lappend(extras_after, (Node *)fk_trigger);
+
+                               /*
+                                * 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->relname                     = fkconstraint->pktable_name;
+                               switch ((fkconstraint->actions & FKCONSTR_ON_DELETE_MASK)
+                                                               >> FKCONSTR_ON_DELETE_SHIFT)
+                               {
+                                       case FKCONSTR_ON_KEY_NOACTION:
+                                               fk_trigger->funcname = "RI_FKey_noaction_del";
+                                               break;
+                                       case FKCONSTR_ON_KEY_RESTRICT:
+                                               fk_trigger->funcname = "RI_FKey_restrict_del";
+                                               break;
+                                       case FKCONSTR_ON_KEY_CASCADE:
+                                               fk_trigger->funcname = "RI_FKey_cascade_del";
+                                               break;
+                                       case FKCONSTR_ON_KEY_SETNULL:
+                                               fk_trigger->funcname = "RI_FKey_setnull_del";
+                                               break;
+                                       case FKCONSTR_ON_KEY_SETDEFAULT:
+                                               fk_trigger->funcname = "RI_FKey_setdefault_del";
+                                               break;
+                                       default:
+                                               elog(ERROR, "Only one ON DELETE action can be specified for FOREIGN KEY constraint");
+                                               break;
+                               }
+                               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->constrrelname       = stmt->relname;
+
+                               fk_trigger->args                = NIL;
+                               fk_trigger->args = lappend(fk_trigger->args,
+                                                                               fkconstraint->constr_name);
+                               fk_trigger->args = lappend(fk_trigger->args,
+                                                                               stmt->relname);
+                               fk_trigger->args = lappend(fk_trigger->args,
+                                                                               fkconstraint->pktable_name);
+                               fk_trigger->args = lappend(fk_trigger->args,
+                                                                               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, id->name);
+
+                                       id = (Ident *)lfirst(pk_attr);
+                                       fk_trigger->args = lappend(fk_trigger->args, id->name);
+
+                                       fk_attr = lnext(fk_attr);
+                                       pk_attr = lnext(pk_attr);
+                               }
+
+                               extras_after = lappend(extras_after, (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->relname                     = fkconstraint->pktable_name;
+                               switch ((fkconstraint->actions & FKCONSTR_ON_UPDATE_MASK)
+                                                               >> FKCONSTR_ON_UPDATE_SHIFT)
+                               {
+                                       case FKCONSTR_ON_KEY_NOACTION:
+                                               fk_trigger->funcname = "RI_FKey_noaction_upd";
+                                               break;
+                                       case FKCONSTR_ON_KEY_RESTRICT:
+                                               fk_trigger->funcname = "RI_FKey_restrict_upd";
+                                               break;
+                                       case FKCONSTR_ON_KEY_CASCADE:
+                                               fk_trigger->funcname = "RI_FKey_cascade_upd";
+                                               break;
+                                       case FKCONSTR_ON_KEY_SETNULL:
+                                               fk_trigger->funcname = "RI_FKey_setnull_upd";
+                                               break;
+                                       case FKCONSTR_ON_KEY_SETDEFAULT:
+                                               fk_trigger->funcname = "RI_FKey_setdefault_upd";
+                                               break;
+                                       default:
+                                               elog(ERROR, "Only one ON UPDATE action can be specified for FOREIGN KEY constraint");
+                                               break;
+                               }
+                               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->constrrelname       = stmt->relname;
+
+                               fk_trigger->args                = NIL;
+                               fk_trigger->args = lappend(fk_trigger->args,
+                                                                               fkconstraint->constr_name);
+                               fk_trigger->args = lappend(fk_trigger->args,
+                                                                               stmt->relname);
+                               fk_trigger->args = lappend(fk_trigger->args,
+                                                                               fkconstraint->pktable_name);
+                               fk_trigger->args = lappend(fk_trigger->args,
+                                                                               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, id->name);
+
+                                       id = (Ident *)lfirst(pk_attr);
+                                       fk_trigger->args = lappend(fk_trigger->args, id->name);
+
+                                       fk_attr = lnext(fk_attr);
+                                       pk_attr = lnext(pk_attr);
+                               }
+
+                               extras_after = lappend(extras_after, (Node *)fk_trigger);
+                       }
+                       break;
+               default:
+                       break;
+       }
+       qry->utilityStmt = (Node *) stmt;
+       return qry;
+}
+
+
 /* This function steps through the tree
  * built up by the select_w_o_sort rule
  * and builds a list of all SelectStmt Nodes found
@@ -1748,3 +1991,4 @@ transformColumnType(ParseState *pstate, ColumnDef *column)
                }
        }
 }
+
index f3cf934926d643cc82db570bf248ccbdfd54b8d7..6562c46d4c50fb95b7d2ea47cbfb68e55f89e0ae 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.138 2000/02/02 20:54:17 wieck Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.139 2000/02/04 18:49:33 wieck Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -1489,7 +1489,7 @@ CreateTrigStmt:  CREATE TRIGGER name TriggerActionTime TriggerEvents ON
                                        n->constrrelname = NULL;
                                        $$ = (Node *)n;
                                }
-               | CREATE CONSTRAINT TRIGGER name AFTER TriggerOneEvent ON
+               | CREATE CONSTRAINT TRIGGER name AFTER TriggerEvents ON
                                relation_name OptConstrFromTable 
                                ConstraintAttributeSpec
                                FOR EACH ROW EXECUTE PROCEDURE name '(' TriggerFuncArgs ')'
index c4c8ba736689fdcd13ca6cd7d6a0caf1b116b423..65f9cde5c2c5b90f74e359b799117f0d5b614d1d 100644 (file)
@@ -22,7 +22,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.142 2000/02/02 13:20:15 petere Exp $
+ *       $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.143 2000/02/04 18:49:34 wieck Exp $
  *
  * Modifications - 6/10/96 - dave@bensoft.com - version 1.13.dhb
  *
@@ -1699,7 +1699,11 @@ getTables(int *numTables, FuncInfo *finfo, int numFuncs)
                                                i_tgfoid,
                                                i_tgtype,
                                                i_tgnargs,
-                                               i_tgargs;
+                                               i_tgargs,
+                                               i_tgisconstraint,
+                                               i_tgconstrname,
+                                               i_tgdeferrable,
+                                               i_tginitdeferred;
                        int                     ntups2;
                        int                     i2;
 
@@ -1710,7 +1714,7 @@ getTables(int *numTables, FuncInfo *finfo, int numFuncs)
                                                g_comment_end);
 
                        resetPQExpBuffer(query);
-                       appendPQExpBuffer(query, "SELECT tgname, tgfoid, tgtype, tgnargs, tgargs, oid "
+                       appendPQExpBuffer(query, "SELECT tgname, tgfoid, tgtype, tgnargs, tgargs, tgisconstraint, tgconstrname, tgdeferrable, tginitdeferred, oid "
                                        "from pg_trigger "
                                        "where tgrelid = '%s'::oid ",
                                        tblinfo[i].oid);
@@ -1734,34 +1738,79 @@ getTables(int *numTables, FuncInfo *finfo, int numFuncs)
                        i_tgnargs = PQfnumber(res2, "tgnargs");
                        i_tgargs = PQfnumber(res2, "tgargs");
                        i_tgoid = PQfnumber(res2, "oid");
+                       i_tgisconstraint = PQfnumber(res2, "tgisconstraint");
+                       i_tgconstrname = PQfnumber(res2, "tgconstrname");
+                       i_tgdeferrable = PQfnumber(res2, "tgdeferrable");
+                       i_tginitdeferred = PQfnumber(res2, "tginitdeferred");
+
                        tblinfo[i].triggers = (char **) malloc(ntups2 * sizeof(char *));
                        tblinfo[i].trcomments = (char **) malloc(ntups2 * sizeof(char *));
                        tblinfo[i].troids = (char **) malloc(ntups2 * sizeof(char *));
                        resetPQExpBuffer(query);
                        for (i2 = 0; i2 < ntups2; i2++)
                        {
-                               const char *tgfunc = PQgetvalue(res2, i2, i_tgfoid);
+                               const char *tgfuncoid = PQgetvalue(res2, i2, i_tgfoid);
+                               char *tgfunc = NULL;
                                int2            tgtype = atoi(PQgetvalue(res2, i2, i_tgtype));
                                int                     tgnargs = atoi(PQgetvalue(res2, i2, i_tgnargs));
                                const char *tgargs = PQgetvalue(res2, i2, i_tgargs);
+                               int tgisconstraint;
+                               int tgdeferrable;
+                               int tginitdeferred;
                                const char *p;
                                int                     findx;
 
+                               if (strcmp(PQgetvalue(res2, i2, i_tgisconstraint), "f") == 0)
+                                       tgisconstraint=0;
+                               else
+                                       tgisconstraint=1;
+
+                               if (strcmp(PQgetvalue(res2, i2, i_tgdeferrable), "f") == 0)
+                                       tgdeferrable=0;
+                               else
+                                       tgdeferrable=1;
+
+                               if (strcmp(PQgetvalue(res2, i2, i_tginitdeferred), "f") == 0)
+                                       tginitdeferred=0;
+                               else
+                                       tginitdeferred=1;
+
                                for (findx = 0; findx < numFuncs; findx++)
                                {
-                                       if (strcmp(finfo[findx].oid, tgfunc) == 0 &&
+                                       if (strcmp(finfo[findx].oid, tgfuncoid) == 0 &&
                                                finfo[findx].nargs == 0 &&
                                                strcmp(finfo[findx].prorettype, "0") == 0)
                                                break;
                                }
+                               
                                if (findx == numFuncs)
                                {
-                                       fprintf(stderr, "getTables(): relation '%s': cannot find function with oid %s for trigger %s\n",
-                                                       tblinfo[i].relname, tgfunc, PQgetvalue(res2, i2, i_tgname));
-                                       exit_nicely(g_conn);
+                                       PGresult *r;
+
+                                       /*
+                                        * the funcname is an oid which we use to find the name of the
+                                        * pg_proc.  We need to do this because getFuncs() only reads
+                                        * in the user-defined funcs not all the funcs.  We might not
+                                        * find what we want by looking in FuncInfo*
+                                        */
+                                       resetPQExpBuffer(query);
+                                       appendPQExpBuffer(query,
+                                               "SELECT proname from pg_proc "
+                                               "where pg_proc.oid = '%s'::oid",
+                                               tgfuncoid);  
+
+                                       r = PQexec(g_conn, query->data);
+                                       if (!r || PQresultStatus(r) != PGRES_TUPLES_OK)
+                                       {
+                                               fprintf(stderr, "getTables(): SELECT (funcname) failed.  Explanation from backend: '%s'.\n", PQerrorMessage(g_conn));
+                                               exit_nicely(g_conn);
+                                       }
+                                       tgfunc = strdup(PQgetvalue(r, 0, PQfnumber(r, "proname")));
+                                       PQclear(r);
+                               }
+                               else {
+                                       tgfunc = strdup(finfo[findx].proname);
                                }
-                               tgfunc = finfo[findx].proname;
-
 #if 0
                                /* XXX - how to emit this DROP TRIGGER? */
                                if (dropSchema)
@@ -1777,8 +1826,15 @@ getTables(int *numTables, FuncInfo *finfo, int numFuncs)
 #endif
 
                                resetPQExpBuffer(query);
-                               appendPQExpBuffer(query, "CREATE TRIGGER ");
-                               appendPQExpBuffer(query, fmtId(PQgetvalue(res2, i2, i_tgname), force_quotes));
+                               if (tgisconstraint)
+                               {
+                                       appendPQExpBuffer(query, "CREATE CONSTRAINT TRIGGER ");
+                                       appendPQExpBuffer(query, fmtId(PQgetvalue(res2, i2, i_tgconstrname), force_quotes));
+                               }
+                               else { 
+                                       appendPQExpBuffer(query, "CREATE TRIGGER ");
+                                       appendPQExpBuffer(query, fmtId(PQgetvalue(res2, i2, i_tgname), force_quotes));
+                               }
                                appendPQExpBufferChar(query, ' ');
                                /* Trigger type */
                                findx = 0;
@@ -1806,8 +1862,27 @@ getTables(int *numTables, FuncInfo *finfo, int numFuncs)
                                        else
                                                appendPQExpBuffer(query, " UPDATE");
                                }
-                               appendPQExpBuffer(query, " ON %s FOR EACH ROW",
-                                                                 fmtId(tblinfo[i].relname, force_quotes));
+                               appendPQExpBuffer(query, " ON %s ", fmtId(tblinfo[i].relname, force_quotes));
+
+                               if (tgisconstraint) 
+                               {
+                                       if (!tgdeferrable) 
+                                       {
+                                               appendPQExpBuffer(query, " NOT");
+                                       }
+                                       appendPQExpBuffer(query, " DEFERRABLE INITIALLY ");
+                                       if (tginitdeferred)
+                                       {
+                                               appendPQExpBuffer(query, "DEFERRED");
+                                       }
+                                       else 
+                                       {
+                                               appendPQExpBuffer(query, "IMMEDIATE");
+                                       }
+                                       
+                               }
+
+                               appendPQExpBuffer(query, " FOR EACH ROW");
                                appendPQExpBuffer(query, " EXECUTE PROCEDURE %s (",
                                                                  fmtId(tgfunc, force_quotes));
                                for (findx = 0; findx < tgnargs; findx++)
@@ -1847,6 +1922,7 @@ getTables(int *numTables, FuncInfo *finfo, int numFuncs)
                                        tgargs = p + 4;
                                }
                                appendPQExpBuffer(query, ");\n");
+
                                tblinfo[i].triggers[i2] = strdup(query->data);
 
                                /*** Initialize trcomments and troids ***/
@@ -1859,6 +1935,10 @@ getTables(int *numTables, FuncInfo *finfo, int numFuncs)
                                tblinfo[i].trcomments[i2] = strdup(query->data);
                                tblinfo[i].troids[i2] = strdup(PQgetvalue(res2, i2, i_tgoid));
 
+                               if (tgfunc)
+                               {
+                                       free(tgfunc);
+                               }
                        }
                        PQclear(res2);
                }