mirror of
https://github.com/postgres/postgres.git
synced 2025-08-22 21:53:06 +03:00
Second phase of committing Rod Taylor's pg_depend/pg_constraint patch.
pg_relcheck is gone; CHECK, UNIQUE, PRIMARY KEY, and FOREIGN KEY constraints all have real live entries in pg_constraint. pg_depend exists, and RESTRICT/CASCADE options work on most kinds of DROP; however, pg_depend is not yet very well populated with dependencies. (Most of the ones that are present at this point just replace formerly hardwired associations, such as the implicit drop of a relation's pg_type entry when the relation is dropped.) Need to add more logic to create dependency entries, improve pg_dump to dump constraints in place of indexes and triggers, and add some regression tests.
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.237 2002/06/20 20:29:31 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.238 2002/07/12 18:43:17 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -47,7 +47,7 @@
|
||||
/* State shared by transformCreateSchemaStmt and its subroutines */
|
||||
typedef struct
|
||||
{
|
||||
const char *stmtType; /* "CREATE TABLE" or "ALTER TABLE" */
|
||||
const char *stmtType; /* "CREATE SCHEMA" or "ALTER SCHEMA" */
|
||||
char *schemaname; /* name of schema */
|
||||
char *authid; /* owner of schema */
|
||||
List *tables; /* CREATE TABLE items */
|
||||
@@ -1066,6 +1066,7 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
|
||||
cxt->stmtType, (cxt->relation)->relname);
|
||||
cxt->pkey = index;
|
||||
}
|
||||
index->isconstraint = true;
|
||||
|
||||
if (constraint->name != NULL)
|
||||
index->idxname = pstrdup(constraint->name);
|
||||
@@ -1304,15 +1305,8 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
|
||||
static void
|
||||
transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt)
|
||||
{
|
||||
CreateTrigStmt *fk_trigger;
|
||||
List *fkactions = NIL;
|
||||
List *fkclist;
|
||||
List *fk_attr;
|
||||
List *pk_attr;
|
||||
Ident *id;
|
||||
Oid pktypoid[INDEX_MAX_KEYS];
|
||||
Oid fktypoid[INDEX_MAX_KEYS];
|
||||
int i;
|
||||
|
||||
if (cxt->fkconstraints == NIL)
|
||||
return;
|
||||
@@ -1323,15 +1317,12 @@ transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt)
|
||||
foreach(fkclist, cxt->fkconstraints)
|
||||
{
|
||||
FkConstraint *fkconstraint = (FkConstraint *) lfirst(fkclist);
|
||||
Oid pktypoid[INDEX_MAX_KEYS];
|
||||
Oid fktypoid[INDEX_MAX_KEYS];
|
||||
int i;
|
||||
int attnum;
|
||||
List *fkattrs;
|
||||
|
||||
/*
|
||||
* If the constraint has no name, set it to <unnamed>
|
||||
*/
|
||||
if (fkconstraint->constr_name == NULL)
|
||||
fkconstraint->constr_name = "<unnamed>";
|
||||
|
||||
for (attnum = 0; attnum < INDEX_MAX_KEYS; attnum++)
|
||||
pktypoid[attnum] = fktypoid[attnum] = InvalidOid;
|
||||
|
||||
@@ -1473,203 +1464,24 @@ transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt)
|
||||
}
|
||||
|
||||
/*
|
||||
* Build a CREATE CONSTRAINT TRIGGER statement for the CHECK
|
||||
* action.
|
||||
* For ALTER TABLE ADD CONSTRAINT, we're done. For CREATE TABLE,
|
||||
* gin up an ALTER TABLE ADD CONSTRAINT command to execute after
|
||||
* the basic CREATE TABLE is complete.
|
||||
*/
|
||||
fk_trigger = (CreateTrigStmt *) makeNode(CreateTrigStmt);
|
||||
fk_trigger->trigname = fkconstraint->constr_name;
|
||||
fk_trigger->relation = cxt->relation;
|
||||
fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
|
||||
fk_trigger->before = false;
|
||||
fk_trigger->row = true;
|
||||
fk_trigger->actions[0] = 'i';
|
||||
fk_trigger->actions[1] = 'u';
|
||||
fk_trigger->actions[2] = '\0';
|
||||
fk_trigger->lang = NULL;
|
||||
fk_trigger->text = NULL;
|
||||
|
||||
fk_trigger->attr = NIL;
|
||||
fk_trigger->when = NULL;
|
||||
fk_trigger->isconstraint = true;
|
||||
fk_trigger->deferrable = fkconstraint->deferrable;
|
||||
fk_trigger->initdeferred = fkconstraint->initdeferred;
|
||||
fk_trigger->constrrel = fkconstraint->pktable;
|
||||
|
||||
fk_trigger->args = NIL;
|
||||
fk_trigger->args = lappend(fk_trigger->args,
|
||||
makeString(fkconstraint->constr_name));
|
||||
fk_trigger->args = lappend(fk_trigger->args,
|
||||
makeString((cxt->relation)->relname));
|
||||
fk_trigger->args = lappend(fk_trigger->args,
|
||||
makeString(fkconstraint->pktable->relname));
|
||||
fk_trigger->args = lappend(fk_trigger->args,
|
||||
makeString(fkconstraint->match_type));
|
||||
fk_attr = fkconstraint->fk_attrs;
|
||||
pk_attr = fkconstraint->pk_attrs;
|
||||
if (length(fk_attr) != length(pk_attr))
|
||||
elog(ERROR, "number of key attributes in referenced table must be equal to foreign key"
|
||||
"\n\tIllegal FOREIGN KEY definition references \"%s\"",
|
||||
fkconstraint->pktable->relname);
|
||||
|
||||
while (fk_attr != NIL)
|
||||
if (strcmp(cxt->stmtType, "CREATE TABLE") == 0)
|
||||
{
|
||||
id = (Ident *) lfirst(fk_attr);
|
||||
fk_trigger->args = lappend(fk_trigger->args,
|
||||
makeString(id->name));
|
||||
AlterTableStmt *alterstmt = makeNode(AlterTableStmt);
|
||||
|
||||
id = (Ident *) lfirst(pk_attr);
|
||||
fk_trigger->args = lappend(fk_trigger->args,
|
||||
makeString(id->name));
|
||||
alterstmt->subtype = 'c'; /* preprocessed add constraint */
|
||||
alterstmt->relation = cxt->relation;
|
||||
alterstmt->name = NULL;
|
||||
alterstmt->def = (Node *) makeList1(fkconstraint);
|
||||
|
||||
fk_attr = lnext(fk_attr);
|
||||
pk_attr = lnext(pk_attr);
|
||||
/* Don't need to scan the table contents in this case */
|
||||
fkconstraint->skip_validation = true;
|
||||
|
||||
fkactions = lappend(fkactions, (Node *) alterstmt);
|
||||
}
|
||||
|
||||
fkactions = lappend(fkactions, (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->relation = fkconstraint->pktable;
|
||||
fk_trigger->before = false;
|
||||
fk_trigger->row = true;
|
||||
fk_trigger->actions[0] = 'd';
|
||||
fk_trigger->actions[1] = '\0';
|
||||
fk_trigger->lang = NULL;
|
||||
fk_trigger->text = NULL;
|
||||
|
||||
fk_trigger->attr = NIL;
|
||||
fk_trigger->when = NULL;
|
||||
fk_trigger->isconstraint = true;
|
||||
fk_trigger->deferrable = fkconstraint->deferrable;
|
||||
fk_trigger->initdeferred = fkconstraint->initdeferred;
|
||||
fk_trigger->constrrel = cxt->relation;
|
||||
switch ((fkconstraint->actions & FKCONSTR_ON_DELETE_MASK)
|
||||
>> FKCONSTR_ON_DELETE_SHIFT)
|
||||
{
|
||||
case FKCONSTR_ON_KEY_NOACTION:
|
||||
fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
|
||||
break;
|
||||
case FKCONSTR_ON_KEY_RESTRICT:
|
||||
fk_trigger->deferrable = false;
|
||||
fk_trigger->initdeferred = false;
|
||||
fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
|
||||
break;
|
||||
case FKCONSTR_ON_KEY_CASCADE:
|
||||
fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
|
||||
break;
|
||||
case FKCONSTR_ON_KEY_SETNULL:
|
||||
fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
|
||||
break;
|
||||
case FKCONSTR_ON_KEY_SETDEFAULT:
|
||||
fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "Only one ON DELETE action can be specified for FOREIGN KEY constraint");
|
||||
break;
|
||||
}
|
||||
|
||||
fk_trigger->args = NIL;
|
||||
fk_trigger->args = lappend(fk_trigger->args,
|
||||
makeString(fkconstraint->constr_name));
|
||||
fk_trigger->args = lappend(fk_trigger->args,
|
||||
makeString((cxt->relation)->relname));
|
||||
fk_trigger->args = lappend(fk_trigger->args,
|
||||
makeString(fkconstraint->pktable->relname));
|
||||
fk_trigger->args = lappend(fk_trigger->args,
|
||||
makeString(fkconstraint->match_type));
|
||||
fk_attr = fkconstraint->fk_attrs;
|
||||
pk_attr = fkconstraint->pk_attrs;
|
||||
while (fk_attr != NIL)
|
||||
{
|
||||
id = (Ident *) lfirst(fk_attr);
|
||||
fk_trigger->args = lappend(fk_trigger->args,
|
||||
makeString(id->name));
|
||||
|
||||
id = (Ident *) lfirst(pk_attr);
|
||||
fk_trigger->args = lappend(fk_trigger->args,
|
||||
makeString(id->name));
|
||||
|
||||
fk_attr = lnext(fk_attr);
|
||||
pk_attr = lnext(pk_attr);
|
||||
}
|
||||
|
||||
fkactions = lappend(fkactions, (Node *) fk_trigger);
|
||||
|
||||
/*
|
||||
* Build a CREATE CONSTRAINT TRIGGER statement for the ON UPDATE
|
||||
* action fired on the PK table !!!
|
||||
*/
|
||||
fk_trigger = (CreateTrigStmt *) makeNode(CreateTrigStmt);
|
||||
fk_trigger->trigname = fkconstraint->constr_name;
|
||||
fk_trigger->relation = fkconstraint->pktable;
|
||||
fk_trigger->before = false;
|
||||
fk_trigger->row = true;
|
||||
fk_trigger->actions[0] = 'u';
|
||||
fk_trigger->actions[1] = '\0';
|
||||
fk_trigger->lang = NULL;
|
||||
fk_trigger->text = NULL;
|
||||
|
||||
fk_trigger->attr = NIL;
|
||||
fk_trigger->when = NULL;
|
||||
fk_trigger->isconstraint = true;
|
||||
fk_trigger->deferrable = fkconstraint->deferrable;
|
||||
fk_trigger->initdeferred = fkconstraint->initdeferred;
|
||||
fk_trigger->constrrel = cxt->relation;
|
||||
switch ((fkconstraint->actions & FKCONSTR_ON_UPDATE_MASK)
|
||||
>> FKCONSTR_ON_UPDATE_SHIFT)
|
||||
{
|
||||
case FKCONSTR_ON_KEY_NOACTION:
|
||||
fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
|
||||
break;
|
||||
case FKCONSTR_ON_KEY_RESTRICT:
|
||||
fk_trigger->deferrable = false;
|
||||
fk_trigger->initdeferred = false;
|
||||
fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
|
||||
break;
|
||||
case FKCONSTR_ON_KEY_CASCADE:
|
||||
fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
|
||||
break;
|
||||
case FKCONSTR_ON_KEY_SETNULL:
|
||||
fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
|
||||
break;
|
||||
case FKCONSTR_ON_KEY_SETDEFAULT:
|
||||
fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "Only one ON UPDATE action can be specified for FOREIGN KEY constraint");
|
||||
break;
|
||||
}
|
||||
|
||||
fk_trigger->args = NIL;
|
||||
fk_trigger->args = lappend(fk_trigger->args,
|
||||
makeString(fkconstraint->constr_name));
|
||||
fk_trigger->args = lappend(fk_trigger->args,
|
||||
makeString((cxt->relation)->relname));
|
||||
fk_trigger->args = lappend(fk_trigger->args,
|
||||
makeString(fkconstraint->pktable->relname));
|
||||
fk_trigger->args = lappend(fk_trigger->args,
|
||||
makeString(fkconstraint->match_type));
|
||||
fk_attr = fkconstraint->fk_attrs;
|
||||
pk_attr = fkconstraint->pk_attrs;
|
||||
while (fk_attr != NIL)
|
||||
{
|
||||
id = (Ident *) lfirst(fk_attr);
|
||||
fk_trigger->args = lappend(fk_trigger->args,
|
||||
makeString(id->name));
|
||||
|
||||
id = (Ident *) lfirst(pk_attr);
|
||||
fk_trigger->args = lappend(fk_trigger->args,
|
||||
makeString(id->name));
|
||||
|
||||
fk_attr = lnext(fk_attr);
|
||||
pk_attr = lnext(pk_attr);
|
||||
}
|
||||
|
||||
fkactions = lappend(fkactions, (Node *) fk_trigger);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2642,6 +2454,14 @@ transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt,
|
||||
*extras_after = nconc(cxt.alist, *extras_after);
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
/*
|
||||
* Already-transformed ADD CONSTRAINT, so just make it look
|
||||
* like the standard case.
|
||||
*/
|
||||
stmt->subtype = 'C';
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@@ -11,7 +11,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.338 2002/07/11 07:39:25 ishii Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.339 2002/07/12 18:43:17 tgl Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@@ -309,8 +309,7 @@ static void doNegateFloat(Value *v);
|
||||
%type <node> TableConstraint, TableLikeClause
|
||||
%type <list> ColQualList
|
||||
%type <node> ColConstraint, ColConstraintElem, ConstraintAttr
|
||||
%type <ival> key_actions, key_delete, key_update, key_reference
|
||||
%type <str> key_match
|
||||
%type <ival> key_actions, key_delete, key_match, key_update, key_action
|
||||
%type <ival> ConstraintAttributeSpec, ConstraintDeferrabilitySpec,
|
||||
ConstraintTimeSpec
|
||||
|
||||
@@ -1594,8 +1593,9 @@ ColConstraintElem:
|
||||
n->pktable = $2;
|
||||
n->fk_attrs = NIL;
|
||||
n->pk_attrs = $3;
|
||||
n->match_type = $4;
|
||||
n->actions = $5;
|
||||
n->fk_matchtype = $4;
|
||||
n->fk_upd_action = (char) ($5 >> 8);
|
||||
n->fk_del_action = (char) ($5 & 0xFF);
|
||||
n->deferrable = FALSE;
|
||||
n->initdeferred = FALSE;
|
||||
$$ = (Node *)n;
|
||||
@@ -1714,16 +1714,16 @@ ConstraintElem:
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| FOREIGN KEY '(' columnList ')' REFERENCES qualified_name
|
||||
opt_column_list
|
||||
key_match key_actions ConstraintAttributeSpec
|
||||
opt_column_list key_match key_actions ConstraintAttributeSpec
|
||||
{
|
||||
FkConstraint *n = makeNode(FkConstraint);
|
||||
n->constr_name = NULL;
|
||||
n->pktable = $7;
|
||||
n->fk_attrs = $4;
|
||||
n->pk_attrs = $8;
|
||||
n->match_type = $9;
|
||||
n->actions = $10;
|
||||
n->fk_matchtype = $9;
|
||||
n->fk_upd_action = (char) ($10 >> 8);
|
||||
n->fk_del_action = (char) ($10 & 0xFF);
|
||||
n->deferrable = ($11 & 1) != 0;
|
||||
n->initdeferred = ($11 & 2) != 0;
|
||||
$$ = (Node *)n;
|
||||
@@ -1750,45 +1750,54 @@ columnElem: ColId
|
||||
|
||||
key_match: MATCH FULL
|
||||
{
|
||||
$$ = "FULL";
|
||||
$$ = FKCONSTR_MATCH_FULL;
|
||||
}
|
||||
| MATCH PARTIAL
|
||||
{
|
||||
elog(ERROR, "FOREIGN KEY/MATCH PARTIAL not yet implemented");
|
||||
$$ = "PARTIAL";
|
||||
$$ = FKCONSTR_MATCH_PARTIAL;
|
||||
}
|
||||
| MATCH SIMPLE
|
||||
{
|
||||
$$ = "UNSPECIFIED";
|
||||
$$ = FKCONSTR_MATCH_UNSPECIFIED;
|
||||
}
|
||||
| /*EMPTY*/
|
||||
{
|
||||
$$ = "UNSPECIFIED";
|
||||
$$ = FKCONSTR_MATCH_UNSPECIFIED;
|
||||
}
|
||||
;
|
||||
|
||||
/*
|
||||
* We combine the update and delete actions into one value temporarily
|
||||
* for simplicity of parsing, and then break them down again in the
|
||||
* calling production. update is in the left 8 bits, delete in the right.
|
||||
* Note that NOACTION is the default.
|
||||
*/
|
||||
key_actions:
|
||||
key_delete { $$ = $1; }
|
||||
| key_update { $$ = $1; }
|
||||
| key_delete key_update { $$ = $1 | $2; }
|
||||
| key_update key_delete { $$ = $1 | $2; }
|
||||
| /*EMPTY*/ { $$ = 0; }
|
||||
key_update
|
||||
{ $$ = ($1 << 8) | (FKCONSTR_ACTION_NOACTION & 0xFF); }
|
||||
| key_delete
|
||||
{ $$ = (FKCONSTR_ACTION_NOACTION << 8) | ($1 & 0xFF); }
|
||||
| key_update key_delete
|
||||
{ $$ = ($1 << 8) | ($2 & 0xFF); }
|
||||
| key_delete key_update
|
||||
{ $$ = ($2 << 8) | ($1 & 0xFF); }
|
||||
| /*EMPTY*/
|
||||
{ $$ = (FKCONSTR_ACTION_NOACTION << 8) | (FKCONSTR_ACTION_NOACTION & 0xFF); }
|
||||
;
|
||||
|
||||
key_delete: ON DELETE_P key_reference
|
||||
{ $$ = $3 << FKCONSTR_ON_DELETE_SHIFT; }
|
||||
key_update: ON UPDATE key_action { $$ = $3; }
|
||||
;
|
||||
|
||||
key_update: ON UPDATE key_reference
|
||||
{ $$ = $3 << FKCONSTR_ON_UPDATE_SHIFT; }
|
||||
key_delete: ON DELETE_P key_action { $$ = $3; }
|
||||
;
|
||||
|
||||
key_reference:
|
||||
NO ACTION { $$ = FKCONSTR_ON_KEY_NOACTION; }
|
||||
| RESTRICT { $$ = FKCONSTR_ON_KEY_RESTRICT; }
|
||||
| CASCADE { $$ = FKCONSTR_ON_KEY_CASCADE; }
|
||||
| SET NULL_P { $$ = FKCONSTR_ON_KEY_SETNULL; }
|
||||
| SET DEFAULT { $$ = FKCONSTR_ON_KEY_SETDEFAULT; }
|
||||
key_action:
|
||||
NO ACTION { $$ = FKCONSTR_ACTION_NOACTION; }
|
||||
| RESTRICT { $$ = FKCONSTR_ACTION_RESTRICT; }
|
||||
| CASCADE { $$ = FKCONSTR_ACTION_CASCADE; }
|
||||
| SET NULL_P { $$ = FKCONSTR_ACTION_SETNULL; }
|
||||
| SET DEFAULT { $$ = FKCONSTR_ACTION_SETDEFAULT; }
|
||||
;
|
||||
|
||||
OptInherit: INHERITS '(' qualified_name_list ')' { $$ = $3; }
|
||||
@@ -2300,7 +2309,7 @@ drop_type: TABLE { $$ = DROP_TABLE; }
|
||||
| INDEX { $$ = DROP_INDEX; }
|
||||
| TYPE_P { $$ = DROP_TYPE; }
|
||||
| DOMAIN_P { $$ = DROP_DOMAIN; }
|
||||
| CONVERSION_P { $$ = DROP_CONVERSION; }
|
||||
| CONVERSION_P { $$ = DROP_CONVERSION; }
|
||||
;
|
||||
|
||||
any_name_list:
|
||||
@@ -6673,6 +6682,7 @@ unreserved_keyword:
|
||||
| COMMIT
|
||||
| COMMITTED
|
||||
| CONSTRAINTS
|
||||
| CONVERSION_P
|
||||
| COPY
|
||||
| CREATEDB
|
||||
| CREATEUSER
|
||||
@@ -6857,6 +6867,7 @@ col_name_keyword:
|
||||
| SUBSTRING
|
||||
| TIME
|
||||
| TIMESTAMP
|
||||
| TREAT
|
||||
| TRIM
|
||||
| VARCHAR
|
||||
;
|
||||
@@ -6963,7 +6974,6 @@ reserved_keyword:
|
||||
| THEN
|
||||
| TO
|
||||
| TRAILING
|
||||
| TREAT
|
||||
| TRUE_P
|
||||
| UNION
|
||||
| UNIQUE
|
||||
|
Reference in New Issue
Block a user