Improve documentation of trigger firing queue handling, cleanup.
Neil Conway
This commit is contained in:
parent
2132ac89bf
commit
b813554dbd
5 changed files with 319 additions and 181 deletions
|
@ -1,4 +1,4 @@
|
|||
<!-- $Header: /cvsroot/pgsql/doc/src/sgml/ref/set_constraints.sgml,v 1.4 2002/06/03 01:10:38 momjian Exp $ -->
|
||||
<!-- $Header: /cvsroot/pgsql/doc/src/sgml/ref/set_constraints.sgml,v 1.5 2002/08/17 12:15:48 momjian Exp $ -->
|
||||
<refentry id="SQL-SET-CONSTRAINTS">
|
||||
<refmeta>
|
||||
<refentrytitle id="SQL-SET-CONSTRAINTS-title">SET CONSTRAINTS</refentrytitle>
|
||||
|
@ -22,10 +22,32 @@ SET CONSTRAINTS { ALL | <replaceable class="parameter">constraint</replaceable>
|
|||
|
||||
<para>
|
||||
<command>SET CONSTRAINTS</command> sets the behavior of constraint
|
||||
evaluation in the current transaction. In
|
||||
<option>IMMEDIATE</option> mode, constraints are checked at the end
|
||||
of each statement. In <option>DEFERRED</option> mode, constraints
|
||||
are not checked until transaction commit.
|
||||
evaluation in the current transaction. In <option>IMMEDIATE
|
||||
</option> mode, constraints are checked at the end of each
|
||||
statement. In <option>DEFERRED</option> mode, constraints are not
|
||||
checked until transaction commit.
|
||||
</para>
|
||||
|
||||
<note>
|
||||
<para>
|
||||
This command only alters the behavior of constraints within the
|
||||
current transaction. Thus, if you execute this command outside
|
||||
of an explicit transaction block (such as one started with
|
||||
<command>BEGIN</command>), it will not appear to have any effect.
|
||||
If you wish to change the behavior of a constraint without needing
|
||||
to issue a <command>SET CONSTRAINTS</command> command in every
|
||||
transaction, specify <option>INITIALLY DEFERRED</option> or
|
||||
<option>INITIALLY IMMEDIATE</option> when you create the constraint.
|
||||
</para>
|
||||
</note>
|
||||
|
||||
<para>
|
||||
When you change the mode of a constraint to be <option>IMMEDIATE
|
||||
</option>, the new constraint mode takes effect retroactively:
|
||||
any outstanding data modifications that would have been checked
|
||||
at the end of the transaction (when using
|
||||
<option>DEFERRED</option>) are instead checked during the
|
||||
execution of the <command>SET CONSTRAINTS</command> command.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
|
@ -52,7 +74,11 @@ SET CONSTRAINTS { ALL | <replaceable class="parameter">constraint</replaceable>
|
|||
|
||||
<para>
|
||||
<command>SET CONSTRAINTS</command> is defined in
|
||||
<acronym>SQL92</acronym> and <acronym>SQL99</acronym>.
|
||||
<acronym>SQL92</acronym> and <acronym>SQL99</acronym>. The
|
||||
implementation in <productname>PostgreSQL</productname> complies
|
||||
with the behavior defined in the standard, except for the
|
||||
<productname>PostgreSQL</productname> limitation that <command>SET
|
||||
CONSTRAINTS</command> cannot be applied to check or unique constraints.
|
||||
</para>
|
||||
</refsect2>
|
||||
</refsect1>
|
||||
|
|
|
@ -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.125 2002/08/13 17:22:08 petere Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.126 2002/08/17 12:15:48 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -1266,12 +1266,10 @@ static MemoryContext deftrig_cxt = NULL;
|
|||
* state IMMEDIATE or DEFERRED.
|
||||
* ----------
|
||||
*/
|
||||
static bool deftrig_dfl_all_isset = false;
|
||||
static bool deftrig_dfl_all_isdeferred = false;
|
||||
static List *deftrig_dfl_trigstates = NIL;
|
||||
|
||||
static bool deftrig_all_isset;
|
||||
static bool deftrig_all_isdeferred;
|
||||
static bool deftrig_all_isset = false;
|
||||
static bool deftrig_all_isdeferred = false;
|
||||
static List *deftrig_trigstates;
|
||||
|
||||
/* ----------
|
||||
|
@ -1702,8 +1700,11 @@ DeferredTriggerBeginXact(void)
|
|||
ALLOCSET_DEFAULT_MAXSIZE);
|
||||
oldcxt = MemoryContextSwitchTo(deftrig_cxt);
|
||||
|
||||
deftrig_all_isset = deftrig_dfl_all_isset;
|
||||
deftrig_all_isdeferred = deftrig_dfl_all_isdeferred;
|
||||
deftrig_all_isset = false;
|
||||
/*
|
||||
* If unspecified, constraints default to IMMEDIATE, per SQL
|
||||
*/
|
||||
deftrig_all_isdeferred = false;
|
||||
|
||||
deftrig_trigstates = NIL;
|
||||
foreach(l, deftrig_dfl_trigstates)
|
||||
|
@ -1793,189 +1794,125 @@ DeferredTriggerAbortXact(void)
|
|||
/* ----------
|
||||
* DeferredTriggerSetState()
|
||||
*
|
||||
* Called for the users SET CONSTRAINTS ... utility command.
|
||||
* Called for the SET CONSTRAINTS ... utility command.
|
||||
* ----------
|
||||
*/
|
||||
void
|
||||
DeferredTriggerSetState(ConstraintsSetStmt *stmt)
|
||||
{
|
||||
Relation tgrel;
|
||||
List *l;
|
||||
List *ls;
|
||||
List *loid = NIL;
|
||||
MemoryContext oldcxt;
|
||||
bool found;
|
||||
DeferredTriggerStatus state;
|
||||
|
||||
/*
|
||||
* If called outside a transaction block, we can safely return: this
|
||||
* command cannot effect any subsequent transactions, and there
|
||||
* are no "session-level" trigger settings.
|
||||
*/
|
||||
if (!IsTransactionBlock())
|
||||
return;
|
||||
|
||||
/*
|
||||
* Handle SET CONSTRAINTS ALL ...
|
||||
*/
|
||||
if (stmt->constraints == NIL)
|
||||
{
|
||||
if (!IsTransactionBlock())
|
||||
/*
|
||||
* Drop all per-transaction information about individual trigger
|
||||
* states.
|
||||
*/
|
||||
l = deftrig_trigstates;
|
||||
while (l != NIL)
|
||||
{
|
||||
/*
|
||||
* ... outside of a transaction block
|
||||
*
|
||||
* Drop all information about individual trigger states per
|
||||
* session.
|
||||
*/
|
||||
l = deftrig_dfl_trigstates;
|
||||
while (l != NIL)
|
||||
{
|
||||
List *next = lnext(l);
|
||||
List *next = lnext(l);
|
||||
|
||||
pfree(lfirst(l));
|
||||
pfree(l);
|
||||
l = next;
|
||||
}
|
||||
deftrig_dfl_trigstates = NIL;
|
||||
|
||||
/*
|
||||
* Set the session ALL state to known.
|
||||
*/
|
||||
deftrig_dfl_all_isset = true;
|
||||
deftrig_dfl_all_isdeferred = stmt->deferred;
|
||||
|
||||
return;
|
||||
pfree(lfirst(l));
|
||||
pfree(l);
|
||||
l = next;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* ... inside of a transaction block
|
||||
*
|
||||
* Drop all information about individual trigger states per
|
||||
* transaction.
|
||||
*/
|
||||
l = deftrig_trigstates;
|
||||
while (l != NIL)
|
||||
{
|
||||
List *next = lnext(l);
|
||||
|
||||
pfree(lfirst(l));
|
||||
pfree(l);
|
||||
l = next;
|
||||
}
|
||||
deftrig_trigstates = NIL;
|
||||
|
||||
/*
|
||||
* Set the per transaction ALL state to known.
|
||||
*/
|
||||
deftrig_all_isset = true;
|
||||
deftrig_all_isdeferred = stmt->deferred;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------
|
||||
* Handle SET CONSTRAINTS constraint-name [, ...]
|
||||
* First lookup all trigger Oid's for the constraint names.
|
||||
* ----------
|
||||
*/
|
||||
tgrel = heap_openr(TriggerRelationName, AccessShareLock);
|
||||
|
||||
foreach(l, stmt->constraints)
|
||||
{
|
||||
char *cname = strVal(lfirst(l));
|
||||
ScanKeyData skey;
|
||||
SysScanDesc tgscan;
|
||||
HeapTuple htup;
|
||||
deftrig_trigstates = NIL;
|
||||
|
||||
/*
|
||||
* Check that only named constraints are set explicitly
|
||||
* Set the per-transaction ALL state to known.
|
||||
*/
|
||||
if (strlen(cname) == 0)
|
||||
elog(ERROR, "unnamed constraints cannot be set explicitly");
|
||||
|
||||
/*
|
||||
* Setup to scan pg_trigger by tgconstrname ...
|
||||
*/
|
||||
ScanKeyEntryInitialize(&skey,
|
||||
(bits16) 0x0,
|
||||
(AttrNumber) Anum_pg_trigger_tgconstrname,
|
||||
(RegProcedure) F_NAMEEQ,
|
||||
PointerGetDatum(cname));
|
||||
|
||||
tgscan = systable_beginscan(tgrel, TriggerConstrNameIndex, true,
|
||||
SnapshotNow, 1, &skey);
|
||||
|
||||
/*
|
||||
* ... and search for the constraint trigger row
|
||||
*/
|
||||
found = false;
|
||||
|
||||
while (HeapTupleIsValid(htup = systable_getnext(tgscan)))
|
||||
{
|
||||
Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(htup);
|
||||
Oid constr_oid;
|
||||
|
||||
/*
|
||||
* If we found some, check that they fit the deferrability but
|
||||
* skip ON <event> RESTRICT ones, since they are silently
|
||||
* never deferrable.
|
||||
*/
|
||||
if (stmt->deferred && !pg_trigger->tgdeferrable &&
|
||||
pg_trigger->tgfoid != F_RI_FKEY_RESTRICT_UPD &&
|
||||
pg_trigger->tgfoid != F_RI_FKEY_RESTRICT_DEL)
|
||||
elog(ERROR, "Constraint '%s' is not deferrable",
|
||||
cname);
|
||||
|
||||
AssertTupleDescHasOid(tgrel->rd_att);
|
||||
constr_oid = HeapTupleGetOid(htup);
|
||||
loid = lappendi(loid, constr_oid);
|
||||
found = true;
|
||||
}
|
||||
|
||||
systable_endscan(tgscan);
|
||||
|
||||
/*
|
||||
* Not found ?
|
||||
*/
|
||||
if (!found)
|
||||
elog(ERROR, "Constraint '%s' does not exist", cname);
|
||||
}
|
||||
heap_close(tgrel, AccessShareLock);
|
||||
|
||||
if (!IsTransactionBlock())
|
||||
{
|
||||
/*
|
||||
* Outside of a transaction block set the trigger states of
|
||||
* individual triggers on session level.
|
||||
*/
|
||||
oldcxt = MemoryContextSwitchTo(deftrig_gcxt);
|
||||
|
||||
foreach(l, loid)
|
||||
{
|
||||
found = false;
|
||||
foreach(ls, deftrig_dfl_trigstates)
|
||||
{
|
||||
state = (DeferredTriggerStatus) lfirst(ls);
|
||||
if (state->dts_tgoid == (Oid) lfirsti(l))
|
||||
{
|
||||
state->dts_tgisdeferred = stmt->deferred;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
state = (DeferredTriggerStatus)
|
||||
palloc(sizeof(DeferredTriggerStatusData));
|
||||
state->dts_tgoid = (Oid) lfirsti(l);
|
||||
state->dts_tgisdeferred = stmt->deferred;
|
||||
|
||||
deftrig_dfl_trigstates =
|
||||
lappend(deftrig_dfl_trigstates, state);
|
||||
}
|
||||
}
|
||||
|
||||
MemoryContextSwitchTo(oldcxt);
|
||||
|
||||
return;
|
||||
deftrig_all_isset = true;
|
||||
deftrig_all_isdeferred = stmt->deferred;
|
||||
}
|
||||
else
|
||||
{
|
||||
Relation tgrel;
|
||||
MemoryContext oldcxt;
|
||||
bool found;
|
||||
DeferredTriggerStatus state;
|
||||
List *ls;
|
||||
List *loid = NIL;
|
||||
|
||||
/* ----------
|
||||
* Handle SET CONSTRAINTS constraint-name [, ...]
|
||||
* First lookup all trigger Oid's for the constraint names.
|
||||
* ----------
|
||||
*/
|
||||
tgrel = heap_openr(TriggerRelationName, AccessShareLock);
|
||||
|
||||
foreach(l, stmt->constraints)
|
||||
{
|
||||
char *cname = strVal(lfirst(l));
|
||||
ScanKeyData skey;
|
||||
SysScanDesc tgscan;
|
||||
HeapTuple htup;
|
||||
|
||||
/*
|
||||
* Check that only named constraints are set explicitly
|
||||
*/
|
||||
if (strlen(cname) == 0)
|
||||
elog(ERROR, "unnamed constraints cannot be set explicitly");
|
||||
|
||||
/*
|
||||
* Setup to scan pg_trigger by tgconstrname ...
|
||||
*/
|
||||
ScanKeyEntryInitialize(&skey, (bits16) 0x0,
|
||||
(AttrNumber) Anum_pg_trigger_tgconstrname,
|
||||
(RegProcedure) F_NAMEEQ,
|
||||
PointerGetDatum(cname));
|
||||
|
||||
tgscan = systable_beginscan(tgrel, TriggerConstrNameIndex, true,
|
||||
SnapshotNow, 1, &skey);
|
||||
|
||||
/*
|
||||
* ... and search for the constraint trigger row
|
||||
*/
|
||||
found = false;
|
||||
|
||||
while (HeapTupleIsValid(htup = systable_getnext(tgscan)))
|
||||
{
|
||||
Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(htup);
|
||||
Oid constr_oid;
|
||||
|
||||
/*
|
||||
* If we found some, check that they fit the deferrability but
|
||||
* skip ON <event> RESTRICT ones, since they are silently
|
||||
* never deferrable.
|
||||
*/
|
||||
if (stmt->deferred && !pg_trigger->tgdeferrable &&
|
||||
pg_trigger->tgfoid != F_RI_FKEY_RESTRICT_UPD &&
|
||||
pg_trigger->tgfoid != F_RI_FKEY_RESTRICT_DEL)
|
||||
elog(ERROR, "Constraint '%s' is not deferrable",
|
||||
cname);
|
||||
|
||||
AssertTupleDescHasOid(tgrel->rd_att);
|
||||
constr_oid = HeapTupleGetOid(htup);
|
||||
loid = lappendi(loid, constr_oid);
|
||||
found = true;
|
||||
}
|
||||
|
||||
systable_endscan(tgscan);
|
||||
|
||||
/*
|
||||
* Not found ?
|
||||
*/
|
||||
if (!found)
|
||||
elog(ERROR, "Constraint '%s' does not exist", cname);
|
||||
}
|
||||
heap_close(tgrel, AccessShareLock);
|
||||
|
||||
/*
|
||||
* Inside of a transaction block set the trigger states of
|
||||
* individual triggers on transaction level.
|
||||
|
@ -2008,9 +1945,17 @@ DeferredTriggerSetState(ConstraintsSetStmt *stmt)
|
|||
}
|
||||
|
||||
MemoryContextSwitchTo(oldcxt);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* SQL99 requires that when a constraint is set to IMMEDIATE, any
|
||||
* deferred checks against that constraint must be made when the
|
||||
* SET CONSTRAINTS command is executed -- i.e. the effects of the
|
||||
* SET CONSTRAINTS command applies retroactively. This happens "for
|
||||
* free" since we have already made the necessary modifications to
|
||||
* the constraints, and deferredTriggerEndQuery() is called by
|
||||
* finish_xact_command().
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.170 2002/08/15 16:36:05 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.171 2002/08/17 12:15:49 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -215,9 +215,8 @@ ProcessUtility(Node *parsetree,
|
|||
BeginTransactionBlock();
|
||||
|
||||
/*
|
||||
* Currently, the only option that can be set is
|
||||
* the transaction isolation level by START
|
||||
* TRANSACTION.
|
||||
* Currently, the only option that can be set by
|
||||
* START TRANSACTION is the isolation level.
|
||||
*/
|
||||
if (stmt->options)
|
||||
{
|
||||
|
|
|
@ -945,3 +945,80 @@ ERROR: Unable to identify an operator '=' for types 'inet' and 'integer'
|
|||
drop table pktable;
|
||||
ERROR: table "pktable" does not exist
|
||||
drop table pktable_base;
|
||||
--
|
||||
-- Deferrable constraints
|
||||
-- (right now, only FOREIGN KEY constraints can be deferred)
|
||||
--
|
||||
-- deferrable, explicitely deferred
|
||||
CREATE TABLE pktable (
|
||||
id INT4 PRIMARY KEY,
|
||||
other INT4
|
||||
);
|
||||
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index 'pktable_pkey' for table 'pktable'
|
||||
CREATE TABLE fktable (
|
||||
id INT4 PRIMARY KEY,
|
||||
fk INT4 REFERENCES pktable DEFERRABLE
|
||||
);
|
||||
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index 'fktable_pkey' for table 'fktable'
|
||||
NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
|
||||
-- default to immediate: should fail
|
||||
INSERT INTO fktable VALUES (5, 10);
|
||||
ERROR: $1 referential integrity violation - key referenced from fktable not found in pktable
|
||||
-- explicitely defer the constraint
|
||||
BEGIN;
|
||||
SET CONSTRAINTS ALL DEFERRED;
|
||||
INSERT INTO fktable VALUES (10, 15);
|
||||
INSERT INTO pktable VALUES (15, 0); -- make the FK insert valid
|
||||
COMMIT;
|
||||
DROP TABLE fktable, pktable;
|
||||
-- deferrable, initially deferred
|
||||
CREATE TABLE pktable (
|
||||
id INT4 PRIMARY KEY,
|
||||
other INT4
|
||||
);
|
||||
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index 'pktable_pkey' for table 'pktable'
|
||||
CREATE TABLE fktable (
|
||||
id INT4 PRIMARY KEY,
|
||||
fk INT4 REFERENCES pktable DEFERRABLE INITIALLY DEFERRED
|
||||
);
|
||||
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index 'fktable_pkey' for table 'fktable'
|
||||
NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
|
||||
-- default to deferred, should succeed
|
||||
BEGIN;
|
||||
INSERT INTO fktable VALUES (100, 200);
|
||||
INSERT INTO pktable VALUES (200, 500); -- make the FK insert valid
|
||||
COMMIT;
|
||||
-- default to deferred, explicitely make immediate
|
||||
BEGIN;
|
||||
SET CONSTRAINTS ALL IMMEDIATE;
|
||||
-- should fail
|
||||
INSERT INTO fktable VALUES (500, 1000);
|
||||
ERROR: $1 referential integrity violation - key referenced from fktable not found in pktable
|
||||
COMMIT;
|
||||
DROP TABLE fktable, pktable;
|
||||
-- tricky behavior: according to SQL99, if a deferred constraint is set
|
||||
-- to 'immediate' mode, it should be checked for validity *immediately*,
|
||||
-- not when the current transaction commits (i.e. the mode change applies
|
||||
-- retroactively)
|
||||
CREATE TABLE pktable (
|
||||
id INT4 PRIMARY KEY,
|
||||
other INT4
|
||||
);
|
||||
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index 'pktable_pkey' for table 'pktable'
|
||||
CREATE TABLE fktable (
|
||||
id INT4 PRIMARY KEY,
|
||||
fk INT4 REFERENCES pktable DEFERRABLE
|
||||
);
|
||||
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index 'fktable_pkey' for table 'fktable'
|
||||
NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
|
||||
BEGIN;
|
||||
SET CONSTRAINTS ALL DEFERRED;
|
||||
-- should succeed, for now
|
||||
INSERT INTO fktable VALUES (1000, 2000);
|
||||
-- should cause transaction abort, due to preceding error
|
||||
SET CONSTRAINTS ALL IMMEDIATE;
|
||||
ERROR: $1 referential integrity violation - key referenced from fktable not found in pktable
|
||||
INSERT INTO pktable VALUES (2000, 3); -- too late
|
||||
ERROR: current transaction is aborted, queries ignored until end of transaction block
|
||||
COMMIT;
|
||||
DROP TABLE fktable, pktable;
|
||||
|
|
|
@ -583,3 +583,94 @@ create table pktable(ptest1 inet, ptest2 inet, primary key(base1, ptest1), forei
|
|||
drop table pktable;
|
||||
drop table pktable_base;
|
||||
|
||||
--
|
||||
-- Deferrable constraints
|
||||
-- (right now, only FOREIGN KEY constraints can be deferred)
|
||||
--
|
||||
|
||||
-- deferrable, explicitely deferred
|
||||
CREATE TABLE pktable (
|
||||
id INT4 PRIMARY KEY,
|
||||
other INT4
|
||||
);
|
||||
|
||||
CREATE TABLE fktable (
|
||||
id INT4 PRIMARY KEY,
|
||||
fk INT4 REFERENCES pktable DEFERRABLE
|
||||
);
|
||||
|
||||
-- default to immediate: should fail
|
||||
INSERT INTO fktable VALUES (5, 10);
|
||||
|
||||
-- explicitely defer the constraint
|
||||
BEGIN;
|
||||
|
||||
SET CONSTRAINTS ALL DEFERRED;
|
||||
|
||||
INSERT INTO fktable VALUES (10, 15);
|
||||
INSERT INTO pktable VALUES (15, 0); -- make the FK insert valid
|
||||
|
||||
COMMIT;
|
||||
|
||||
DROP TABLE fktable, pktable;
|
||||
|
||||
-- deferrable, initially deferred
|
||||
CREATE TABLE pktable (
|
||||
id INT4 PRIMARY KEY,
|
||||
other INT4
|
||||
);
|
||||
|
||||
CREATE TABLE fktable (
|
||||
id INT4 PRIMARY KEY,
|
||||
fk INT4 REFERENCES pktable DEFERRABLE INITIALLY DEFERRED
|
||||
);
|
||||
|
||||
-- default to deferred, should succeed
|
||||
BEGIN;
|
||||
|
||||
INSERT INTO fktable VALUES (100, 200);
|
||||
INSERT INTO pktable VALUES (200, 500); -- make the FK insert valid
|
||||
|
||||
COMMIT;
|
||||
|
||||
-- default to deferred, explicitely make immediate
|
||||
BEGIN;
|
||||
|
||||
SET CONSTRAINTS ALL IMMEDIATE;
|
||||
|
||||
-- should fail
|
||||
INSERT INTO fktable VALUES (500, 1000);
|
||||
|
||||
COMMIT;
|
||||
|
||||
DROP TABLE fktable, pktable;
|
||||
|
||||
-- tricky behavior: according to SQL99, if a deferred constraint is set
|
||||
-- to 'immediate' mode, it should be checked for validity *immediately*,
|
||||
-- not when the current transaction commits (i.e. the mode change applies
|
||||
-- retroactively)
|
||||
CREATE TABLE pktable (
|
||||
id INT4 PRIMARY KEY,
|
||||
other INT4
|
||||
);
|
||||
|
||||
CREATE TABLE fktable (
|
||||
id INT4 PRIMARY KEY,
|
||||
fk INT4 REFERENCES pktable DEFERRABLE
|
||||
);
|
||||
|
||||
BEGIN;
|
||||
|
||||
SET CONSTRAINTS ALL DEFERRED;
|
||||
|
||||
-- should succeed, for now
|
||||
INSERT INTO fktable VALUES (1000, 2000);
|
||||
|
||||
-- should cause transaction abort, due to preceding error
|
||||
SET CONSTRAINTS ALL IMMEDIATE;
|
||||
|
||||
INSERT INTO pktable VALUES (2000, 3); -- too late
|
||||
|
||||
COMMIT;
|
||||
|
||||
DROP TABLE fktable, pktable;
|
||||
|
|
Loading…
Reference in a new issue