From: Jan Wieck Date: Fri, 4 Feb 2000 18:49:34 +0000 (+0000) Subject: Added ALTER TABLE ... ADD CONSTRAINT (provided by Stephan Szabo). X-Git-Tag: REL7_0~699 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=ddd596d38639796983eb08a90aa1349db6f600da;p=postgresql Added ALTER TABLE ... ADD CONSTRAINT (provided by Stephan Szabo). 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 --- diff --git a/src/backend/commands/command.c b/src/backend/commands/command.c index ef6b66d9c3..8f36515c8c 100644 --- a/src/backend/commands/command.c +++ b/src/backend/commands/command.c @@ -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 = ""; + 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] = ""; + 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"); + } } diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index f3c919a1ed..04c20e1827 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.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); } diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index fd3dda8f17..d44f0df091 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -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 + * + */ + if (fkconstraint->constr_name == NULL) + fkconstraint->constr_name = ""; + + /* + * 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) } } } + diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index f3cf934926..6562c46d4c 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.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 ')' diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index c4c8ba7366..65f9cde5c2 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.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); }