*
*
* 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
#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"
#include "utils/builtins.h"
#include "utils/syscache.h"
#include "utils/temprel.h"
-
+#include "commands/trigger.h"
/* ----------------
* PortalExecutorHeapMemory stuff
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");
+ }
}
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
}
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
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;
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
}
}
}
+
*
*
* 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
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 ')'
*
*
* 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
*
i_tgfoid,
i_tgtype,
i_tgnargs,
- i_tgargs;
+ i_tgargs,
+ i_tgisconstraint,
+ i_tgconstrname,
+ i_tgdeferrable,
+ i_tginitdeferred;
int ntups2;
int i2;
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);
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)
#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;
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++)
tgargs = p + 4;
}
appendPQExpBuffer(query, ");\n");
+
tblinfo[i].triggers[i2] = strdup(query->data);
/*** Initialize trcomments and troids ***/
tblinfo[i].trcomments[i2] = strdup(query->data);
tblinfo[i].troids[i2] = strdup(PQgetvalue(res2, i2, i_tgoid));
+ if (tgfunc)
+ {
+ free(tgfunc);
+ }
}
PQclear(res2);
}