mirror of
https://github.com/postgres/postgres.git
synced 2025-11-10 17:42:29 +03:00
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
This commit is contained in:
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 ')'
|
||||
|
||||
Reference in New Issue
Block a user