Skip to content

Commit 0c4b527

Browse files
committed
Add ALWAYS DEFERRED option for CONSTRAINTs
and CONSTRAINT TRIGGERs. This is important so that one can have triggers and constraints that must run after all of the user/client's statements in a transaction (i.e., at COMMIT time), so that the user/client may make no further changes (triggers, of course, still can).
1 parent 57cd2b6 commit 0c4b527

35 files changed

+490
-336
lines changed

doc/src/sgml/catalogs.sgml

+18-21
Original file line numberDiff line numberDiff line change
@@ -2239,17 +2239,15 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
22392239
</row>
22402240

22412241
<row>
2242-
<entry><structfield>condeferrable</structfield></entry>
2243-
<entry><type>bool</type></entry>
2244-
<entry></entry>
2245-
<entry>Is the constraint deferrable?</entry>
2246-
</row>
2247-
2248-
<row>
2249-
<entry><structfield>condeferred</structfield></entry>
2250-
<entry><type>bool</type></entry>
2242+
<entry><structfield>condeferral</structfield></entry>
2243+
<entry><type>char</type></entry>
22512244
<entry></entry>
2252-
<entry>Is the constraint deferred by default?</entry>
2245+
<entry>Constraint deferral option:
2246+
<literal>a</literal> = always deferred,
2247+
<literal>d</literal> = deferrable,
2248+
<literal>i</literal> = deferrable initially deferred,
2249+
<literal>n</literal> = not deferrable
2250+
</entry>
22532251
</row>
22542252

22552253
<row>
@@ -7044,17 +7042,16 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
70447042
</row>
70457043

70467044
<row>
7047-
<entry><structfield>tgdeferrable</structfield></entry>
7048-
<entry><type>bool</type></entry>
7049-
<entry></entry>
7050-
<entry>True if constraint trigger is deferrable</entry>
7051-
</row>
7052-
7053-
<row>
7054-
<entry><structfield>tginitdeferred</structfield></entry>
7055-
<entry><type>bool</type></entry>
7045+
<entry><structfield>tgdeferrral</structfield></entry>
7046+
<entry><type>char</type></entry>
70567047
<entry></entry>
7057-
<entry>True if constraint trigger is initially deferred</entry>
7048+
<entry>
7049+
<structfield>tgdeferral</structfield> is
7050+
<literal>a</literal>always deferred,
7051+
<literal>d</literal>deferrable,
7052+
<literal>i</literal>deferrable initially deferred,
7053+
<literal>n</literal>not deferrable.
7054+
</entry>
70587055
</row>
70597056

70607057
<row>
@@ -7119,7 +7116,7 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
71197116
<para>
71207117
When <structfield>tgconstraint</structfield> is nonzero,
71217118
<structfield>tgconstrrelid</structfield>, <structfield>tgconstrindid</structfield>,
7122-
<structfield>tgdeferrable</structfield>, and <structfield>tginitdeferred</structfield> are
7119+
and <structfield>tgdeferral</structfield> are
71237120
largely redundant with the referenced <structname>pg_constraint</structname> entry.
71247121
However, it is possible for a non-deferrable trigger to be associated
71257122
with a deferrable constraint: foreign key constraints can have some

doc/src/sgml/ref/alter_table.sgml

+2-2
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable>
5555
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> SET STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN }
5656
ADD <replaceable class="parameter">table_constraint</replaceable> [ NOT VALID ]
5757
ADD <replaceable class="parameter">table_constraint_using_index</replaceable>
58-
ALTER CONSTRAINT <replaceable class="parameter">constraint_name</replaceable> [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
58+
ALTER CONSTRAINT <replaceable class="parameter">constraint_name</replaceable> [ DEFERRABLE | NOT DEFERRABLE | ALWAYS DEFERRED ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
5959
VALIDATE CONSTRAINT <replaceable class="parameter">constraint_name</replaceable>
6060
DROP CONSTRAINT [ IF EXISTS ] <replaceable class="parameter">constraint_name</replaceable> [ RESTRICT | CASCADE ]
6161
DISABLE TRIGGER [ <replaceable class="parameter">trigger_name</replaceable> | ALL | USER ]
@@ -121,7 +121,7 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
121121

122122
[ CONSTRAINT <replaceable class="parameter">constraint_name</replaceable> ]
123123
{ UNIQUE | PRIMARY KEY } USING INDEX <replaceable class="parameter">index_name</replaceable>
124-
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
124+
[ DEFERRABLE | NOT DEFERRABLE | ALWAYS DEFERRED ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
125125

126126
<phrase><replaceable class="parameter">index_parameters</replaceable> in <literal>UNIQUE</literal>, <literal>PRIMARY KEY</literal>, and <literal>EXCLUDE</literal> constraints are:</phrase>
127127

doc/src/sgml/ref/create_table.sgml

+7-3
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
6767
PRIMARY KEY <replaceable class="parameter">index_parameters</replaceable> |
6868
REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
6969
[ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ] }
70-
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
70+
[ DEFERRABLE | NOT DEFERRABLE | ALWAYS DEFERRED ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
7171

7272
<phrase>and <replaceable class="parameter">table_constraint</replaceable> is:</phrase>
7373

@@ -78,7 +78,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
7878
EXCLUDE [ USING <replaceable class="parameter">index_method</replaceable> ] ( <replaceable class="parameter">exclude_element</replaceable> WITH <replaceable class="parameter">operator</replaceable> [, ... ] ) <replaceable class="parameter">index_parameters</replaceable> [ WHERE ( <replaceable class="parameter">predicate</replaceable> ) ] |
7979
FOREIGN KEY ( <replaceable class="parameter">column_name</replaceable> [, ... ] ) REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> [, ... ] ) ]
8080
[ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ] [ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ] }
81-
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
81+
[ DEFERRABLE | NOT DEFERRABLE | ALWAYS DEFERRED ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
8282

8383
<phrase>and <replaceable class="parameter">like_option</replaceable> is:</phrase>
8484

@@ -1098,13 +1098,17 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
10981098
<varlistentry>
10991099
<term><literal>DEFERRABLE</literal></term>
11001100
<term><literal>NOT DEFERRABLE</literal></term>
1101+
<term><literal>ALWAYS DEFERRED</literal></term>
11011102
<listitem>
11021103
<para>
1103-
This controls whether the constraint can be deferred. A
1104+
This controls whether the constraint can be deferred or made immediate. A
11041105
constraint that is not deferrable will be checked immediately
11051106
after every command. Checking of constraints that are
11061107
deferrable can be postponed until the end of the transaction
11071108
(using the <xref linkend="sql-set-constraints"/> command).
1109+
Checking of constraints that are always deferred is always
1110+
postponed until the end of the transaction, and this may not be
1111+
altered with the <xref linkend="sql-set-constraints"/> command.
11081112
<literal>NOT DEFERRABLE</literal> is the default.
11091113
Currently, only <literal>UNIQUE</literal>, <literal>PRIMARY KEY</literal>,
11101114
<literal>EXCLUDE</literal>, and

doc/src/sgml/ref/create_trigger.sgml

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ PostgreSQL documentation
2929
CREATE [ CONSTRAINT ] TRIGGER <replaceable class="parameter">name</replaceable> { BEFORE | AFTER | INSTEAD OF } { <replaceable class="parameter">event</replaceable> [ OR ... ] }
3030
ON <replaceable class="parameter">table_name</replaceable>
3131
[ FROM <replaceable class="parameter">referenced_table_name</replaceable> ]
32-
[ NOT DEFERRABLE | [ DEFERRABLE ] [ INITIALLY IMMEDIATE | INITIALLY DEFERRED ] ]
32+
[ [ NOT DEFERRABLE | DEFERRABLE | ALWAYS DEFERRED ] [ INITIALLY IMMEDIATE | INITIALLY DEFERRED ] ]
3333
[ REFERENCING { { OLD | NEW } TABLE [ AS ] <replaceable class="parameter">transition_relation_name</replaceable> } [ ... ] ]
3434
[ FOR [ EACH ] { ROW | STATEMENT } ]
3535
[ WHEN ( <replaceable class="parameter">condition</replaceable> ) ]

doc/src/sgml/ref/set_constraints.sgml

+6-4
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,13 @@ SET CONSTRAINTS { ALL | <replaceable class="parameter">name</replaceable> [, ...
3434
</para>
3535

3636
<para>
37-
Upon creation, a constraint is given one of three
37+
Upon creation, a constraint is given one of four
3838
characteristics: <literal>DEFERRABLE INITIALLY DEFERRED</literal>,
39-
<literal>DEFERRABLE INITIALLY IMMEDIATE</literal>, or
40-
<literal>NOT DEFERRABLE</literal>. The third
41-
class is always <literal>IMMEDIATE</literal> and is not affected by the
39+
<literal>DEFERRABLE INITIALLY IMMEDIATE</literal>,
40+
<literal>NOT DEFERRABLE</literal>, or <literal>ALWAYS DEFERRED</literal>.
41+
The third
42+
class is always <literal>IMMEDIATE</literal>, while the fourth class is
43+
always <literal>DEFERRED</literal>, and neither affected by the
4244
<command>SET CONSTRAINTS</command> command. The first two classes start
4345
every transaction in the indicated mode, but their behavior can be changed
4446
within a transaction by <command>SET CONSTRAINTS</command>.

doc/src/sgml/trigger.sgml

+1-2
Original file line numberDiff line numberDiff line change
@@ -673,8 +673,7 @@ typedef struct Trigger
673673
Oid tgconstrrelid;
674674
Oid tgconstrindid;
675675
Oid tgconstraint;
676-
bool tgdeferrable;
677-
bool tginitdeferred;
676+
char tgdeferral;
678677
int16 tgnargs;
679678
int16 tgnattr;
680679
int16 *tgattr;

src/backend/bootstrap/bootparse.y

+2-4
Original file line numberDiff line numberDiff line change
@@ -307,8 +307,7 @@ Boot_DeclareIndexStmt:
307307
stmt->unique = false;
308308
stmt->primary = false;
309309
stmt->isconstraint = false;
310-
stmt->deferrable = false;
311-
stmt->initdeferred = false;
310+
stmt->deferral = 'n';
312311
stmt->transformed = false;
313312
stmt->concurrent = false;
314313
stmt->if_not_exists = false;
@@ -356,8 +355,7 @@ Boot_DeclareUniqueIndexStmt:
356355
stmt->unique = true;
357356
stmt->primary = false;
358357
stmt->isconstraint = false;
359-
stmt->deferrable = false;
360-
stmt->initdeferred = false;
358+
stmt->deferral = 'n';
361359
stmt->transformed = false;
362360
stmt->concurrent = false;
363361
stmt->if_not_exists = false;

src/backend/catalog/heap.c

+1-2
Original file line numberDiff line numberDiff line change
@@ -2341,8 +2341,7 @@ StoreRelCheck(Relation rel, const char *ccname, Node *expr,
23412341
CreateConstraintEntry(ccname, /* Constraint Name */
23422342
RelationGetNamespace(rel), /* namespace */
23432343
CONSTRAINT_CHECK, /* Constraint Type */
2344-
false, /* Is Deferrable */
2345-
false, /* Is Deferred */
2344+
'n', /* not deferrable */
23462345
is_validated,
23472346
InvalidOid, /* no parent constraint */
23482347
RelationGetRelid(rel), /* relation */

src/backend/catalog/index.c

+15-11
Original file line numberDiff line numberDiff line change
@@ -1215,6 +1215,7 @@ index_create(Relation heapRelation,
12151215
* INDEX_CONSTR_CREATE_MARK_AS_PRIMARY: index is a PRIMARY KEY
12161216
* INDEX_CONSTR_CREATE_DEFERRABLE: constraint is DEFERRABLE
12171217
* INDEX_CONSTR_CREATE_INIT_DEFERRED: constraint is INITIALLY DEFERRED
1218+
* INDEX_CONSTR_CREATE_ALWAYS_DEFERRED: constraint is ALWAYS DEFERRED
12181219
* INDEX_CONSTR_CREATE_UPDATE_INDEX: update the pg_index row
12191220
* INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS: remove existing dependencies
12201221
* of index on table's columns
@@ -1236,15 +1237,20 @@ index_constraint_create(Relation heapRelation,
12361237
ObjectAddress myself,
12371238
referenced;
12381239
Oid conOid;
1239-
bool deferrable;
1240-
bool initdeferred;
1240+
char deferral;
12411241
bool mark_as_primary;
12421242
bool islocal;
12431243
bool noinherit;
12441244
int inhcount;
12451245

1246-
deferrable = (constr_flags & INDEX_CONSTR_CREATE_DEFERRABLE) != 0;
1247-
initdeferred = (constr_flags & INDEX_CONSTR_CREATE_INIT_DEFERRED) != 0;
1246+
if (constr_flags & INDEX_CONSTR_CREATE_ALWAYS_DEFERRED)
1247+
deferral = 'a';
1248+
else if (constr_flags & INDEX_CONSTR_CREATE_INIT_DEFERRED)
1249+
deferral = 'i';
1250+
else if (constr_flags & INDEX_CONSTR_CREATE_DEFERRABLE)
1251+
deferral = 'd';
1252+
else
1253+
deferral = 'n';
12481254
mark_as_primary = (constr_flags & INDEX_CONSTR_CREATE_MARK_AS_PRIMARY) != 0;
12491255

12501256
/* constraint creation support doesn't work while bootstrapping */
@@ -1295,8 +1301,7 @@ index_constraint_create(Relation heapRelation,
12951301
conOid = CreateConstraintEntry(constraintName,
12961302
namespaceId,
12971303
constraintType,
1298-
deferrable,
1299-
initdeferred,
1304+
deferral,
13001305
true,
13011306
parentConstraintId,
13021307
RelationGetRelid(heapRelation),
@@ -1356,7 +1361,7 @@ index_constraint_create(Relation heapRelation,
13561361
* checking trigger. (The trigger will be given an internal dependency on
13571362
* the constraint by CreateTrigger.)
13581363
*/
1359-
if (deferrable)
1364+
if (deferral != 'n')
13601365
{
13611366
CreateTrigStmt *trigger;
13621367

@@ -1373,8 +1378,7 @@ index_constraint_create(Relation heapRelation,
13731378
trigger->columns = NIL;
13741379
trigger->whenClause = NULL;
13751380
trigger->isconstraint = true;
1376-
trigger->deferrable = true;
1377-
trigger->initdeferred = initdeferred;
1381+
trigger->deferral = deferral;
13781382
trigger->constrrel = NULL;
13791383

13801384
(void) CreateTrigger(trigger, NULL, RelationGetRelid(heapRelation),
@@ -1391,7 +1395,7 @@ index_constraint_create(Relation heapRelation,
13911395
* index at all.
13921396
*/
13931397
if ((constr_flags & INDEX_CONSTR_CREATE_UPDATE_INDEX) &&
1394-
(mark_as_primary || deferrable))
1398+
(mark_as_primary || deferral != 'n'))
13951399
{
13961400
Relation pg_index;
13971401
HeapTuple indexTuple;
@@ -1412,7 +1416,7 @@ index_constraint_create(Relation heapRelation,
14121416
dirty = true;
14131417
}
14141418

1415-
if (deferrable && indexForm->indimmediate)
1419+
if (deferral != 'n' && indexForm->indimmediate)
14161420
{
14171421
indexForm->indimmediate = false;
14181422
dirty = true;

src/backend/catalog/information_schema.sql

+7-5
Original file line numberDiff line numberDiff line change
@@ -891,10 +891,12 @@ CREATE VIEW domain_constraints AS
891891
CAST(current_database() AS sql_identifier) AS domain_catalog,
892892
CAST(n.nspname AS sql_identifier) AS domain_schema,
893893
CAST(t.typname AS sql_identifier) AS domain_name,
894-
CAST(CASE WHEN condeferrable THEN 'YES' ELSE 'NO' END
894+
CAST(CASE WHEN condeferral = 'n' THEN 'NO' ELSE 'YES' END
895895
AS yes_or_no) AS is_deferrable,
896-
CAST(CASE WHEN condeferred THEN 'YES' ELSE 'NO' END
897-
AS yes_or_no) AS initially_deferred
896+
CAST(CASE WHEN condeferral = 'i' OR condeferral = 'a' THEN 'YES' ELSE 'NO' END
897+
AS yes_or_no) AS initially_deferred,
898+
CAST(CASE WHEN condeferral = 'a' THEN 'YES' ELSE 'NO' END
899+
AS yes_or_no) AS always_deferred
898900
FROM pg_namespace rs, pg_namespace n, pg_constraint con, pg_type t
899901
WHERE rs.oid = con.connamespace
900902
AND n.oid = t.typnamespace
@@ -1780,9 +1782,9 @@ CREATE VIEW table_constraints AS
17801782
WHEN 'p' THEN 'PRIMARY KEY'
17811783
WHEN 'u' THEN 'UNIQUE' END
17821784
AS character_data) AS constraint_type,
1783-
CAST(CASE WHEN c.condeferrable THEN 'YES' ELSE 'NO' END AS yes_or_no)
1785+
CAST(CASE WHEN c.condeferral = 'n' THEN 'NO' ELSE 'YES' END AS yes_or_no)
17841786
AS is_deferrable,
1785-
CAST(CASE WHEN c.condeferred THEN 'YES' ELSE 'NO' END AS yes_or_no)
1787+
CAST(CASE WHEN c.condeferral = 'i' OR c.condeferral = 'a' THEN 'YES' ELSE 'NO' END AS yes_or_no)
17861788
AS initially_deferred,
17871789
CAST('YES' AS yes_or_no) AS enforced
17881790

src/backend/catalog/pg_constraint.c

+5-9
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,7 @@ Oid
5151
CreateConstraintEntry(const char *constraintName,
5252
Oid constraintNamespace,
5353
char constraintType,
54-
bool isDeferrable,
55-
bool isDeferred,
54+
char deferralOption,
5655
bool isValidated,
5756
Oid parentConstrId,
5857
Oid relId,
@@ -184,8 +183,7 @@ CreateConstraintEntry(const char *constraintName,
184183
values[Anum_pg_constraint_conname - 1] = NameGetDatum(&cname);
185184
values[Anum_pg_constraint_connamespace - 1] = ObjectIdGetDatum(constraintNamespace);
186185
values[Anum_pg_constraint_contype - 1] = CharGetDatum(constraintType);
187-
values[Anum_pg_constraint_condeferrable - 1] = BoolGetDatum(isDeferrable);
188-
values[Anum_pg_constraint_condeferred - 1] = BoolGetDatum(isDeferred);
186+
values[Anum_pg_constraint_condeferral - 1] = CharGetDatum(deferralOption);
189187
values[Anum_pg_constraint_convalidated - 1] = BoolGetDatum(isValidated);
190188
values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(relId);
191189
values[Anum_pg_constraint_contypid - 1] = ObjectIdGetDatum(domainId);
@@ -564,8 +562,7 @@ CloneForeignKeyConstraints(Oid parentId, Oid relationId, List **cloned)
564562
CreateConstraintEntry(NameStr(constrForm->conname),
565563
constrForm->connamespace,
566564
CONSTRAINT_FOREIGN,
567-
constrForm->condeferrable,
568-
constrForm->condeferred,
565+
constrForm->condeferral,
569566
constrForm->convalidated,
570567
HeapTupleGetOid(tuple),
571568
relationId,
@@ -597,8 +594,7 @@ CloneForeignKeyConstraints(Oid parentId, Oid relationId, List **cloned)
597594
/* for now this is all we need */
598595
fkconstraint->fk_upd_action = constrForm->confupdtype;
599596
fkconstraint->fk_del_action = constrForm->confdeltype;
600-
fkconstraint->deferrable = constrForm->condeferrable;
601-
fkconstraint->initdeferred = constrForm->condeferred;
597+
fkconstraint->deferral = constrForm->condeferral;
602598

603599
createForeignKeyTriggers(rel, constrForm->confrelid, fkconstraint,
604600
constrOid, constrForm->conindid, false);
@@ -1357,7 +1353,7 @@ get_primary_key_attnos(Oid relid, bool deferrableOk, Oid *constraintOid)
13571353
* ignore deferrable constraints, then we might as well give up
13581354
* searching, since there can only be a single primary key on a table.
13591355
*/
1360-
if (con->condeferrable && !deferrableOk)
1356+
if (con->condeferral != 'n' && !deferrableOk)
13611357
break;
13621358

13631359
/* Extract the conkey array, ie, attnums of PK's columns */

src/backend/commands/indexcmds.c

+4-2
Original file line numberDiff line numberDiff line change
@@ -841,10 +841,12 @@ DefineIndex(Oid relationId,
841841
if (partitioned && stmt->relation && !stmt->relation->inh)
842842
flags |= INDEX_CREATE_INVALID;
843843

844-
if (stmt->deferrable)
844+
if (stmt->deferral != 'n')
845845
constr_flags |= INDEX_CONSTR_CREATE_DEFERRABLE;
846-
if (stmt->initdeferred)
846+
if (stmt->deferral == 'i')
847847
constr_flags |= INDEX_CONSTR_CREATE_INIT_DEFERRED;
848+
if (stmt->deferral == 'a')
849+
constr_flags |= INDEX_CONSTR_CREATE_ALWAYS_DEFERRED;
848850

849851
indexRelationId =
850852
index_create(rel, indexRelationName, indexRelationId, parentIndexId,

0 commit comments

Comments
 (0)