mirror of
https://github.com/postgres/postgres.git
synced 2025-04-22 23:02:54 +03:00
Improve documentation of trigger firing queue handling, cleanup.
Neil Conway
This commit is contained in:
parent
2132ac89bf
commit
b813554dbd
@ -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">
|
<refentry id="SQL-SET-CONSTRAINTS">
|
||||||
<refmeta>
|
<refmeta>
|
||||||
<refentrytitle id="SQL-SET-CONSTRAINTS-title">SET CONSTRAINTS</refentrytitle>
|
<refentrytitle id="SQL-SET-CONSTRAINTS-title">SET CONSTRAINTS</refentrytitle>
|
||||||
@ -22,10 +22,32 @@ SET CONSTRAINTS { ALL | <replaceable class="parameter">constraint</replaceable>
|
|||||||
|
|
||||||
<para>
|
<para>
|
||||||
<command>SET CONSTRAINTS</command> sets the behavior of constraint
|
<command>SET CONSTRAINTS</command> sets the behavior of constraint
|
||||||
evaluation in the current transaction. In
|
evaluation in the current transaction. In <option>IMMEDIATE
|
||||||
<option>IMMEDIATE</option> mode, constraints are checked at the end
|
</option> mode, constraints are checked at the end of each
|
||||||
of each statement. In <option>DEFERRED</option> mode, constraints
|
statement. In <option>DEFERRED</option> mode, constraints are not
|
||||||
are not checked until transaction commit.
|
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>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
@ -52,7 +74,11 @@ SET CONSTRAINTS { ALL | <replaceable class="parameter">constraint</replaceable>
|
|||||||
|
|
||||||
<para>
|
<para>
|
||||||
<command>SET CONSTRAINTS</command> is defined in
|
<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>
|
</para>
|
||||||
</refsect2>
|
</refsect2>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
@ -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
|
||||||
* $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.
|
* 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 List *deftrig_dfl_trigstates = NIL;
|
||||||
|
|
||||||
static bool deftrig_all_isset;
|
static bool deftrig_all_isset = false;
|
||||||
static bool deftrig_all_isdeferred;
|
static bool deftrig_all_isdeferred = false;
|
||||||
static List *deftrig_trigstates;
|
static List *deftrig_trigstates;
|
||||||
|
|
||||||
/* ----------
|
/* ----------
|
||||||
@ -1702,8 +1700,11 @@ DeferredTriggerBeginXact(void)
|
|||||||
ALLOCSET_DEFAULT_MAXSIZE);
|
ALLOCSET_DEFAULT_MAXSIZE);
|
||||||
oldcxt = MemoryContextSwitchTo(deftrig_cxt);
|
oldcxt = MemoryContextSwitchTo(deftrig_cxt);
|
||||||
|
|
||||||
deftrig_all_isset = deftrig_dfl_all_isset;
|
deftrig_all_isset = false;
|
||||||
deftrig_all_isdeferred = deftrig_dfl_all_isdeferred;
|
/*
|
||||||
|
* If unspecified, constraints default to IMMEDIATE, per SQL
|
||||||
|
*/
|
||||||
|
deftrig_all_isdeferred = false;
|
||||||
|
|
||||||
deftrig_trigstates = NIL;
|
deftrig_trigstates = NIL;
|
||||||
foreach(l, deftrig_dfl_trigstates)
|
foreach(l, deftrig_dfl_trigstates)
|
||||||
@ -1793,189 +1794,125 @@ DeferredTriggerAbortXact(void)
|
|||||||
/* ----------
|
/* ----------
|
||||||
* DeferredTriggerSetState()
|
* DeferredTriggerSetState()
|
||||||
*
|
*
|
||||||
* Called for the users SET CONSTRAINTS ... utility command.
|
* Called for the SET CONSTRAINTS ... utility command.
|
||||||
* ----------
|
* ----------
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
DeferredTriggerSetState(ConstraintsSetStmt *stmt)
|
DeferredTriggerSetState(ConstraintsSetStmt *stmt)
|
||||||
{
|
{
|
||||||
Relation tgrel;
|
|
||||||
List *l;
|
List *l;
|
||||||
List *ls;
|
|
||||||
List *loid = NIL;
|
/*
|
||||||
MemoryContext oldcxt;
|
* If called outside a transaction block, we can safely return: this
|
||||||
bool found;
|
* command cannot effect any subsequent transactions, and there
|
||||||
DeferredTriggerStatus state;
|
* are no "session-level" trigger settings.
|
||||||
|
*/
|
||||||
|
if (!IsTransactionBlock())
|
||||||
|
return;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Handle SET CONSTRAINTS ALL ...
|
* Handle SET CONSTRAINTS ALL ...
|
||||||
*/
|
*/
|
||||||
if (stmt->constraints == NIL)
|
if (stmt->constraints == NIL)
|
||||||
{
|
{
|
||||||
if (!IsTransactionBlock())
|
/*
|
||||||
|
* Drop all per-transaction information about individual trigger
|
||||||
|
* states.
|
||||||
|
*/
|
||||||
|
l = deftrig_trigstates;
|
||||||
|
while (l != NIL)
|
||||||
{
|
{
|
||||||
/*
|
List *next = lnext(l);
|
||||||
* ... 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);
|
|
||||||
|
|
||||||
pfree(lfirst(l));
|
pfree(lfirst(l));
|
||||||
pfree(l);
|
pfree(l);
|
||||||
l = next;
|
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;
|
|
||||||
}
|
}
|
||||||
else
|
deftrig_trigstates = NIL;
|
||||||
{
|
|
||||||
/*
|
|
||||||
* ... 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;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check that only named constraints are set explicitly
|
* Set the per-transaction ALL state to known.
|
||||||
*/
|
*/
|
||||||
if (strlen(cname) == 0)
|
deftrig_all_isset = true;
|
||||||
elog(ERROR, "unnamed constraints cannot be set explicitly");
|
deftrig_all_isdeferred = stmt->deferred;
|
||||||
|
|
||||||
/*
|
|
||||||
* 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;
|
|
||||||
}
|
}
|
||||||
else
|
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
|
* Inside of a transaction block set the trigger states of
|
||||||
* individual triggers on transaction level.
|
* individual triggers on transaction level.
|
||||||
@ -2008,9 +1945,17 @@ DeferredTriggerSetState(ConstraintsSetStmt *stmt)
|
|||||||
}
|
}
|
||||||
|
|
||||||
MemoryContextSwitchTo(oldcxt);
|
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
|
* 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();
|
BeginTransactionBlock();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Currently, the only option that can be set is
|
* Currently, the only option that can be set by
|
||||||
* the transaction isolation level by START
|
* START TRANSACTION is the isolation level.
|
||||||
* TRANSACTION.
|
|
||||||
*/
|
*/
|
||||||
if (stmt->options)
|
if (stmt->options)
|
||||||
{
|
{
|
||||||
|
@ -945,3 +945,80 @@ ERROR: Unable to identify an operator '=' for types 'inet' and 'integer'
|
|||||||
drop table pktable;
|
drop table pktable;
|
||||||
ERROR: table "pktable" does not exist
|
ERROR: table "pktable" does not exist
|
||||||
drop table pktable_base;
|
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;
|
||||||
drop table pktable_base;
|
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…
x
Reference in New Issue
Block a user