Commit 12a47c6a authored by Tom Lane's avatar Tom Lane

Disallow referential integrity actions from being deferred; only the

NO ACTION check is deferrable.  This seems to be a closer approximation
to what the SQL spec says than what we were doing before, and it prevents
some anomalous behaviors that are possible now that triggers can fire
during the execution of PL functions.
Stephan Szabo.
parent 7627b91c
<!-- $PostgreSQL: pgsql/doc/src/sgml/ddl.sgml,v 1.30 2004/08/08 21:33:11 tgl Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/ddl.sgml,v 1.31 2004/10/21 21:33:59 tgl Exp $ -->
<chapter id="ddl"> <chapter id="ddl">
<title>Data Definition</title> <title>Data Definition</title>
...@@ -940,11 +940,17 @@ CREATE TABLE order_items ( ...@@ -940,11 +940,17 @@ CREATE TABLE order_items (
<para> <para>
Restricting and cascading deletes are the two most common options. Restricting and cascading deletes are the two most common options.
<literal>RESTRICT</literal> can also be written as <literal>NO <literal>RESTRICT</literal> prevents a statement from deleting a
ACTION</literal> and it's also the default if you do not specify referenced row. <literal>NO ACTION</literal> means that if any
anything. There are two other options for what should happen with referencing rows still exist when the constraint is checked, an error
the foreign key columns when a primary key is deleted: is raised; this is the default if you do not specify anything.
(The essential difference between these choices is that
<literal>NO ACTION</literal> allows the check to be deferred until
later in the transaction, whereas <literal>RESTRICT</literal> does not.)
There are two other options:
<literal>SET NULL</literal> and <literal>SET DEFAULT</literal>. <literal>SET NULL</literal> and <literal>SET DEFAULT</literal>.
These cause the referencing columns to be set to nulls or default
values, respectively, when the referenced row is deleted.
Note that these do not excuse you from observing any constraints. Note that these do not excuse you from observing any constraints.
For example, if an action specifies <literal>SET DEFAULT</literal> For example, if an action specifies <literal>SET DEFAULT</literal>
but the default value would not satisfy the foreign key, the but the default value would not satisfy the foreign key, the
...@@ -964,7 +970,7 @@ CREATE TABLE order_items ( ...@@ -964,7 +970,7 @@ CREATE TABLE order_items (
<para> <para>
Finally, we should mention that a foreign key must reference Finally, we should mention that a foreign key must reference
columns that are either a primary key or form a unique constraint. columns that either are a primary key or form a unique constraint.
If the foreign key references a unique constraint, there are some If the foreign key references a unique constraint, there are some
additional possibilities regarding how null values are matched. additional possibilities regarding how null values are matched.
These are explained in the reference documentation for These are explained in the reference documentation for
......
<!-- <!--
$PostgreSQL: pgsql/doc/src/sgml/ref/create_table.sgml,v 1.84 2004/08/02 04:25:31 tgl Exp $ $PostgreSQL: pgsql/doc/src/sgml/ref/create_table.sgml,v 1.85 2004/10/21 21:33:59 tgl Exp $
PostgreSQL documentation PostgreSQL documentation
--> -->
...@@ -417,12 +417,10 @@ and <replaceable class="PARAMETER">table_constraint</replaceable> is: ...@@ -417,12 +417,10 @@ and <replaceable class="PARAMETER">table_constraint</replaceable> is:
<listitem> <listitem>
<para> <para>
Theses clauses specify a foreign key constraint, which specifies These clauses specify a foreign key constraint, which requires
that a group of one or more columns of the new table must only that a group of one or more columns of the new table must only
contain values which match against values in the referenced contain values that match values in the referenced
column(s) <replaceable class="parameter">refcolumn</replaceable> column(s) of some row of the referenced table. If <replaceable
of the referenced table <replaceable
class="parameter">reftable</replaceable>. If <replaceable
class="parameter">refcolumn</replaceable> is omitted, the class="parameter">refcolumn</replaceable> is omitted, the
primary key of the <replaceable primary key of the <replaceable
class="parameter">reftable</replaceable> is used. The class="parameter">reftable</replaceable> is used. The
...@@ -431,7 +429,7 @@ and <replaceable class="PARAMETER">table_constraint</replaceable> is: ...@@ -431,7 +429,7 @@ and <replaceable class="PARAMETER">table_constraint</replaceable> is:
</para> </para>
<para> <para>
A value inserted into these columns is matched against the A value inserted into the referencing column(s) is matched against the
values of the referenced table and referenced columns using the values of the referenced table and referenced columns using the
given match type. There are three match types: <literal>MATCH given match type. There are three match types: <literal>MATCH
FULL</>, <literal>MATCH PARTIAL</>, and <literal>MATCH FULL</>, <literal>MATCH PARTIAL</>, and <literal>MATCH
...@@ -452,7 +450,9 @@ and <replaceable class="PARAMETER">table_constraint</replaceable> is: ...@@ -452,7 +450,9 @@ and <replaceable class="PARAMETER">table_constraint</replaceable> is:
clause specifies the action to perform when a referenced column clause specifies the action to perform when a referenced column
in the referenced table is being updated to a new value. If the in the referenced table is being updated to a new value. If the
row is updated, but the referenced column is not actually row is updated, but the referenced column is not actually
changed, no action is done. There are the following possible changed, no action is done. Referential actions apart from the
check of <literal>NO ACTION</literal> can not be deferred even if
the constraint is deferrable. There are the following possible
actions for each clause: actions for each clause:
<variablelist> <variablelist>
...@@ -461,8 +461,10 @@ and <replaceable class="PARAMETER">table_constraint</replaceable> is: ...@@ -461,8 +461,10 @@ and <replaceable class="PARAMETER">table_constraint</replaceable> is:
<listitem> <listitem>
<para> <para>
Produce an error indicating that the deletion or update Produce an error indicating that the deletion or update
would create a foreign key constraint violation. This is would create a foreign key constraint violation.
the default action. If the constraint is deferred, this
error will be produced at constraint check time if there still
exist any referencing rows. This is the default action.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -471,9 +473,10 @@ and <replaceable class="PARAMETER">table_constraint</replaceable> is: ...@@ -471,9 +473,10 @@ and <replaceable class="PARAMETER">table_constraint</replaceable> is:
<term><literal>RESTRICT</literal></term> <term><literal>RESTRICT</literal></term>
<listitem> <listitem>
<para> <para>
Same as <literal>NO ACTION</literal> except that this action Produce an error indicating that the deletion or update
will not be deferred even if the rest of the constraint is would create a foreign key constraint violation.
deferrable and deferred. This is the same as <literal>NO ACTION</literal> except that
the check is not deferrable.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -493,7 +496,7 @@ and <replaceable class="PARAMETER">table_constraint</replaceable> is: ...@@ -493,7 +496,7 @@ and <replaceable class="PARAMETER">table_constraint</replaceable> is:
<term><literal>SET NULL</literal></term> <term><literal>SET NULL</literal></term>
<listitem> <listitem>
<para> <para>
Set the referencing column values to null. Set the referencing column(s) to null.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -502,7 +505,7 @@ and <replaceable class="PARAMETER">table_constraint</replaceable> is: ...@@ -502,7 +505,7 @@ and <replaceable class="PARAMETER">table_constraint</replaceable> is:
<term><literal>SET DEFAULT</literal></term> <term><literal>SET DEFAULT</literal></term>
<listitem> <listitem>
<para> <para>
Set the referencing column values to their default value. Set the referencing column(s) to their default values.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -510,11 +513,10 @@ and <replaceable class="PARAMETER">table_constraint</replaceable> is: ...@@ -510,11 +513,10 @@ and <replaceable class="PARAMETER">table_constraint</replaceable> is:
</para> </para>
<para> <para>
If primary key column is updated frequently, it may be wise to If the referenced column(s) are changed frequently, it may be wise to
add an index to the foreign key column so that <literal>NO add an index to the foreign key column so that referential actions
ACTION</literal> and <literal>CASCADE</literal> actions associated with the foreign key column can be performed more
associated with the foreign key column can be more efficiently efficiently.
performed.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -844,9 +846,9 @@ CREATE TABLE distributors ( ...@@ -844,9 +846,9 @@ CREATE TABLE distributors (
<programlisting> <programlisting>
CREATE TABLE cinemas ( CREATE TABLE cinemas (
id serial, id serial,
name text, name text,
location text location text
) TABLESPACE diskvol1; ) TABLESPACE diskvol1;
</programlisting> </programlisting>
</para> </para>
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.135 2004/10/16 21:16:36 tgl Exp $ * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.136 2004/10/21 21:33:59 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -4361,12 +4361,12 @@ createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint, ...@@ -4361,12 +4361,12 @@ createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
fk_trigger->actions[1] = '\0'; fk_trigger->actions[1] = '\0';
fk_trigger->isconstraint = true; fk_trigger->isconstraint = true;
fk_trigger->deferrable = fkconstraint->deferrable;
fk_trigger->initdeferred = fkconstraint->initdeferred;
fk_trigger->constrrel = myRel; fk_trigger->constrrel = myRel;
switch (fkconstraint->fk_del_action) switch (fkconstraint->fk_del_action)
{ {
case FKCONSTR_ACTION_NOACTION: case FKCONSTR_ACTION_NOACTION:
fk_trigger->deferrable = fkconstraint->deferrable;
fk_trigger->initdeferred = fkconstraint->initdeferred;
fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del"); fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
break; break;
case FKCONSTR_ACTION_RESTRICT: case FKCONSTR_ACTION_RESTRICT:
...@@ -4375,12 +4375,18 @@ createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint, ...@@ -4375,12 +4375,18 @@ createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del"); fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
break; break;
case FKCONSTR_ACTION_CASCADE: case FKCONSTR_ACTION_CASCADE:
fk_trigger->deferrable = false;
fk_trigger->initdeferred = false;
fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del"); fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
break; break;
case FKCONSTR_ACTION_SETNULL: case FKCONSTR_ACTION_SETNULL:
fk_trigger->deferrable = false;
fk_trigger->initdeferred = false;
fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del"); fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
break; break;
case FKCONSTR_ACTION_SETDEFAULT: case FKCONSTR_ACTION_SETDEFAULT:
fk_trigger->deferrable = false;
fk_trigger->initdeferred = false;
fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del"); fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
break; break;
default: default:
...@@ -4425,12 +4431,12 @@ createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint, ...@@ -4425,12 +4431,12 @@ createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
fk_trigger->actions[0] = 'u'; fk_trigger->actions[0] = 'u';
fk_trigger->actions[1] = '\0'; fk_trigger->actions[1] = '\0';
fk_trigger->isconstraint = true; fk_trigger->isconstraint = true;
fk_trigger->deferrable = fkconstraint->deferrable;
fk_trigger->initdeferred = fkconstraint->initdeferred;
fk_trigger->constrrel = myRel; fk_trigger->constrrel = myRel;
switch (fkconstraint->fk_upd_action) switch (fkconstraint->fk_upd_action)
{ {
case FKCONSTR_ACTION_NOACTION: case FKCONSTR_ACTION_NOACTION:
fk_trigger->deferrable = fkconstraint->deferrable;
fk_trigger->initdeferred = fkconstraint->initdeferred;
fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd"); fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
break; break;
case FKCONSTR_ACTION_RESTRICT: case FKCONSTR_ACTION_RESTRICT:
...@@ -4439,12 +4445,18 @@ createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint, ...@@ -4439,12 +4445,18 @@ createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd"); fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
break; break;
case FKCONSTR_ACTION_CASCADE: case FKCONSTR_ACTION_CASCADE:
fk_trigger->deferrable = false;
fk_trigger->initdeferred = false;
fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd"); fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
break; break;
case FKCONSTR_ACTION_SETNULL: case FKCONSTR_ACTION_SETNULL:
fk_trigger->deferrable = false;
fk_trigger->initdeferred = false;
fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd"); fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
break; break;
case FKCONSTR_ACTION_SETDEFAULT: case FKCONSTR_ACTION_SETDEFAULT:
fk_trigger->deferrable = false;
fk_trigger->initdeferred = false;
fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd"); fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
break; break;
default: default:
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.172 2004/09/10 18:39:56 tgl Exp $ * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.173 2004/10/21 21:33:59 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -2729,11 +2729,17 @@ AfterTriggerSetState(ConstraintsSetStmt *stmt) ...@@ -2729,11 +2729,17 @@ AfterTriggerSetState(ConstraintsSetStmt *stmt)
/* /*
* If we found some, check that they fit the deferrability * If we found some, check that they fit the deferrability
* but skip ON <event> RESTRICT ones, since they are * but skip referential action ones, since they are
* silently never deferrable. * silently never deferrable.
*/ */
if (pg_trigger->tgfoid != F_RI_FKEY_RESTRICT_UPD && if (pg_trigger->tgfoid != F_RI_FKEY_RESTRICT_UPD &&
pg_trigger->tgfoid != F_RI_FKEY_RESTRICT_DEL) pg_trigger->tgfoid != F_RI_FKEY_RESTRICT_DEL &&
pg_trigger->tgfoid != F_RI_FKEY_CASCADE_UPD &&
pg_trigger->tgfoid != F_RI_FKEY_CASCADE_DEL &&
pg_trigger->tgfoid != F_RI_FKEY_SETNULL_UPD &&
pg_trigger->tgfoid != F_RI_FKEY_SETNULL_DEL &&
pg_trigger->tgfoid != F_RI_FKEY_SETDEFAULT_UPD &&
pg_trigger->tgfoid != F_RI_FKEY_SETDEFAULT_DEL)
{ {
if (stmt->deferred && !pg_trigger->tgdeferrable) if (stmt->deferred && !pg_trigger->tgdeferrable)
ereport(ERROR, ereport(ERROR,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment