From 20323d6f19601f5471eb4db7570af8d3342b627d Mon Sep 17 00:00:00 2001
From: Nicolas Williams <nico@cryptonector.com>
Date: Tue, 3 Oct 2017 00:33:09 -0500
Subject: [PATCH] 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).
---
 doc/src/sgml/catalogs.sgml                 |  39 ++++-----
 doc/src/sgml/ref/alter_table.sgml          |   4 +-
 doc/src/sgml/ref/create_table.sgml         |  10 ++-
 doc/src/sgml/ref/create_trigger.sgml       |   2 +-
 doc/src/sgml/trigger.sgml                  |   3 +-
 src/backend/bootstrap/bootparse.y          |   6 +-
 src/backend/catalog/heap.c                 |   3 +-
 src/backend/catalog/index.c                |  27 +++---
 src/backend/catalog/information_schema.sql |  12 ++-
 src/backend/catalog/pg_constraint.c        |  14 ++-
 src/backend/commands/indexcmds.c           |   6 +-
 src/backend/commands/tablecmds.c           |  59 +++++--------
 src/backend/commands/trigger.c             |  42 +++++----
 src/backend/commands/typecmds.c            |   5 +-
 src/backend/nodes/copyfuncs.c              |   9 +-
 src/backend/nodes/equalfuncs.c             |   9 +-
 src/backend/nodes/outfuncs.c               |  10 ++-
 src/backend/parser/gram.y                  | 136 +++++++++++++++++++----------
 src/backend/parser/parse_utilcmd.c         |  82 +++++++++++------
 src/backend/utils/adt/ruleutils.c          |  14 +--
 src/bin/pg_dump/pg_dump.c                  |  66 +++++++-------
 src/bin/pg_dump/pg_dump.h                  |   8 +-
 src/bin/psql/describe.c                    |  51 +++++------
 src/bin/psql/tab-complete.c                |   4 +-
 src/include/catalog/index.h                |   5 +-
 src/include/catalog/pg_constraint.h        |   6 +-
 src/include/catalog/pg_trigger.h           |   7 +-
 src/include/commands/trigger.h             |   1 +
 src/include/nodes/parsenodes.h             |  12 ++-
 src/include/utils/reltrigger.h             |   3 +-
 src/test/regress/expected/alter_table.out  |  60 ++++++-------
 src/test/regress/input/constraints.source  |  51 +++++++++++
 src/test/regress/output/constraints.source |  50 +++++++++++
 src/test/regress/sql/alter_table.sql       |   4 +-
 34 files changed, 487 insertions(+), 333 deletions(-)

diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 3ed9021..e82e39b 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -2239,17 +2239,15 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
      </row>
 
      <row>
-      <entry><structfield>condeferrable</structfield></entry>
-      <entry><type>bool</type></entry>
-      <entry></entry>
-      <entry>Is the constraint deferrable?</entry>
-     </row>
-
-     <row>
-      <entry><structfield>condeferred</structfield></entry>
-      <entry><type>bool</type></entry>
+      <entry><structfield>condeferral</structfield></entry>
+      <entry><type>char</type></entry>
       <entry></entry>
-      <entry>Is the constraint deferred by default?</entry>
+      <entry>Constraint deferral option:
+            <literal>a</literal> = always deferred,
+            <literal>d</literal> = deferrable,
+            <literal>d</literal> = deferrable initially deferred,
+            <literal>n</literal> = not deferrable
+          </entry>
      </row>
 
      <row>
@@ -7044,17 +7042,16 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
      </row>
 
      <row>
-      <entry><structfield>tgdeferrable</structfield></entry>
-      <entry><type>bool</type></entry>
-      <entry></entry>
-      <entry>True if constraint trigger is deferrable</entry>
-     </row>
-
-     <row>
-      <entry><structfield>tginitdeferred</structfield></entry>
-      <entry><type>bool</type></entry>
+      <entry><structfield>tgdeferrral</structfield></entry>
+      <entry><type>char</type></entry>
       <entry></entry>
-      <entry>True if constraint trigger is initially deferred</entry>
+      <entry>
+       <structfield>tgdeferral</structfield> is
+       <literal>a</literal>always deferred,
+       <literal>d</literal>deferrable,
+       <literal>i</literal>deferrable initially deferred,
+       <literal>n</literal>not deferrable.
+      </entry>
      </row>
 
      <row>
@@ -7119,7 +7116,7 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
    <para>
     When <structfield>tgconstraint</structfield> is nonzero,
     <structfield>tgconstrrelid</structfield>, <structfield>tgconstrindid</structfield>,
-    <structfield>tgdeferrable</structfield>, and <structfield>tginitdeferred</structfield> are
+    and <structfield>tgdeferral</structfield> are
     largely redundant with the referenced <structname>pg_constraint</structname> entry.
     However, it is possible for a non-deferrable trigger to be associated
     with a deferrable constraint: foreign key constraints can have some
diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml
index 1cce00e..fd28a0d 100644
--- a/doc/src/sgml/ref/alter_table.sgml
+++ b/doc/src/sgml/ref/alter_table.sgml
@@ -55,7 +55,7 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable>
     ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> SET STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN }
     ADD <replaceable class="parameter">table_constraint</replaceable> [ NOT VALID ]
     ADD <replaceable class="parameter">table_constraint_using_index</replaceable>
-    ALTER CONSTRAINT <replaceable class="parameter">constraint_name</replaceable> [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
+    ALTER CONSTRAINT <replaceable class="parameter">constraint_name</replaceable> [ DEFERRABLE | NOT DEFERRABLE | ALWAYS DEFERRED ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
     VALIDATE CONSTRAINT <replaceable class="parameter">constraint_name</replaceable>
     DROP CONSTRAINT [ IF EXISTS ]  <replaceable class="parameter">constraint_name</replaceable> [ RESTRICT | CASCADE ]
     DISABLE TRIGGER [ <replaceable class="parameter">trigger_name</replaceable> | ALL | USER ]
@@ -121,7 +121,7 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
 
     [ CONSTRAINT <replaceable class="parameter">constraint_name</replaceable> ]
     { UNIQUE | PRIMARY KEY } USING INDEX <replaceable class="parameter">index_name</replaceable>
-    [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
+    [ DEFERRABLE | NOT DEFERRABLE | ALWAYS DEFERRED ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
 
 <phrase><replaceable class="parameter">index_parameters</replaceable> in <literal>UNIQUE</literal>, <literal>PRIMARY KEY</literal>, and <literal>EXCLUDE</literal> constraints are:</phrase>
 
diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index 2a1eac9..780b981 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -67,7 +67,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
   PRIMARY KEY <replaceable class="parameter">index_parameters</replaceable> |
   REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
     [ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ] }
-[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
+[ DEFERRABLE | NOT DEFERRABLE | ALWAYS DEFERRED ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
 
 <phrase>and <replaceable class="parameter">table_constraint</replaceable> is:</phrase>
 
@@ -78,7 +78,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
   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> ) ] |
   FOREIGN KEY ( <replaceable class="parameter">column_name</replaceable> [, ... ] ) REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> [, ... ] ) ]
     [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ] [ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ] }
-[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
+[ DEFERRABLE | NOT DEFERRABLE | ALWAYS DEFERRED ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
 
 <phrase>and <replaceable class="parameter">like_option</replaceable> is:</phrase>
 
@@ -1040,13 +1040,17 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
    <varlistentry>
     <term><literal>DEFERRABLE</literal></term>
     <term><literal>NOT DEFERRABLE</literal></term>
+    <term><literal>ALWAYS DEFERRED</literal></term>
     <listitem>
      <para>
-      This controls whether the constraint can be deferred.  A
+      This controls whether the constraint can be deferred or made immediate.  A
       constraint that is not deferrable will be checked immediately
       after every command.  Checking of constraints that are
       deferrable can be postponed until the end of the transaction
       (using the <xref linkend="sql-set-constraints"/> command).
+      Checking of constraints that are always deferred is always
+      postponed until the end of the transaction, and this may not be
+      altered with the <xref linkend="sql-set-constraints"/> command.
       <literal>NOT DEFERRABLE</literal> is the default.
       Currently, only <literal>UNIQUE</literal>, <literal>PRIMARY KEY</literal>,
       <literal>EXCLUDE</literal>, and
diff --git a/doc/src/sgml/ref/create_trigger.sgml b/doc/src/sgml/ref/create_trigger.sgml
index 7b971ee..d823091 100644
--- a/doc/src/sgml/ref/create_trigger.sgml
+++ b/doc/src/sgml/ref/create_trigger.sgml
@@ -29,7 +29,7 @@ PostgreSQL documentation
 CREATE [ CONSTRAINT ] TRIGGER <replaceable class="parameter">name</replaceable> { BEFORE | AFTER | INSTEAD OF } { <replaceable class="parameter">event</replaceable> [ OR ... ] }
     ON <replaceable class="parameter">table_name</replaceable>
     [ FROM <replaceable class="parameter">referenced_table_name</replaceable> ]
-    [ NOT DEFERRABLE | [ DEFERRABLE ] [ INITIALLY IMMEDIATE | INITIALLY DEFERRED ] ]
+    [ [ NOT DEFERRABLE | DEFERRABLE | ALWAYS DEFERRED ] [ INITIALLY IMMEDIATE | INITIALLY DEFERRED ] ]
     [ REFERENCING { { OLD | NEW } TABLE [ AS ] <replaceable class="parameter">transition_relation_name</replaceable> } [ ... ] ]
     [ FOR [ EACH ] { ROW | STATEMENT } ]
     [ WHEN ( <replaceable class="parameter">condition</replaceable> ) ]
diff --git a/doc/src/sgml/trigger.sgml b/doc/src/sgml/trigger.sgml
index c43dbc9..4f1234e30 100644
--- a/doc/src/sgml/trigger.sgml
+++ b/doc/src/sgml/trigger.sgml
@@ -673,8 +673,7 @@ typedef struct Trigger
     Oid         tgconstrrelid;
     Oid         tgconstrindid;
     Oid         tgconstraint;
-    bool        tgdeferrable;
-    bool        tginitdeferred;
+    char        tgdeferral;
     int16       tgnargs;
     int16       tgnattr;
     int16      *tgattr;
diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y
index 4c72989..587d035 100644
--- a/src/backend/bootstrap/bootparse.y
+++ b/src/backend/bootstrap/bootparse.y
@@ -307,8 +307,7 @@ Boot_DeclareIndexStmt:
 					stmt->unique = false;
 					stmt->primary = false;
 					stmt->isconstraint = false;
-					stmt->deferrable = false;
-					stmt->initdeferred = false;
+					stmt->deferral = 'n';
 					stmt->transformed = false;
 					stmt->concurrent = false;
 					stmt->if_not_exists = false;
@@ -356,8 +355,7 @@ Boot_DeclareUniqueIndexStmt:
 					stmt->unique = true;
 					stmt->primary = false;
 					stmt->isconstraint = false;
-					stmt->deferrable = false;
-					stmt->initdeferred = false;
+					stmt->deferral = 'n';
 					stmt->transformed = false;
 					stmt->concurrent = false;
 					stmt->if_not_exists = false;
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index d59bd5b..5f93595 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -2261,8 +2261,7 @@ StoreRelCheck(Relation rel, const char *ccname, Node *expr,
 		CreateConstraintEntry(ccname,	/* Constraint Name */
 							  RelationGetNamespace(rel),	/* namespace */
 							  CONSTRAINT_CHECK, /* Constraint Type */
-							  false,	/* Is Deferrable */
-							  false,	/* Is Deferred */
+							  'n',	/* not deferrable */
 							  is_validated,
 							  InvalidOid,	/* no parent constraint */
 							  RelationGetRelid(rel),	/* relation */
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 8b276bc..795a7a9 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1070,6 +1070,7 @@ index_create(Relation heapRelation,
 
 				recordDependencyOn(&myself, &referenced, deptype);
 			}
+			Assert((flags & INDEX_CREATE_ADD_CONSTRAINT) == 0);
 		}
 
 		/* Store dependency on parent index, if any */
@@ -1215,6 +1216,7 @@ index_create(Relation heapRelation,
  *		INDEX_CONSTR_CREATE_MARK_AS_PRIMARY: index is a PRIMARY KEY
  *		INDEX_CONSTR_CREATE_DEFERRABLE: constraint is DEFERRABLE
  *		INDEX_CONSTR_CREATE_INIT_DEFERRED: constraint is INITIALLY DEFERRED
+ *		INDEX_CONSTR_CREATE_ALWAYS_DEFERRED: constraint is ALWAYS DEFERRED
  *		INDEX_CONSTR_CREATE_UPDATE_INDEX: update the pg_index row
  *		INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS: remove existing dependencies
  *			of index on table's columns
@@ -1236,15 +1238,20 @@ index_constraint_create(Relation heapRelation,
 	ObjectAddress myself,
 				referenced;
 	Oid			conOid;
-	bool		deferrable;
-	bool		initdeferred;
+	char		deferral;
 	bool		mark_as_primary;
 	bool		islocal;
 	bool		noinherit;
 	int			inhcount;
 
-	deferrable = (constr_flags & INDEX_CONSTR_CREATE_DEFERRABLE) != 0;
-	initdeferred = (constr_flags & INDEX_CONSTR_CREATE_INIT_DEFERRED) != 0;
+	if (constr_flags & INDEX_CONSTR_CREATE_ALWAYS_DEFERRED)
+	    deferral = 'a';
+	else if (constr_flags & INDEX_CONSTR_CREATE_INIT_DEFERRED)
+	    deferral = 'i';
+	else if (constr_flags & INDEX_CONSTR_CREATE_DEFERRABLE)
+	    deferral = 'd';
+	else
+	    deferral = 'n';
 	mark_as_primary = (constr_flags & INDEX_CONSTR_CREATE_MARK_AS_PRIMARY) != 0;
 
 	/* constraint creation support doesn't work while bootstrapping */
@@ -1295,8 +1302,7 @@ index_constraint_create(Relation heapRelation,
 	conOid = CreateConstraintEntry(constraintName,
 								   namespaceId,
 								   constraintType,
-								   deferrable,
-								   initdeferred,
+								   deferral,
 								   true,
 								   parentConstraintId,
 								   RelationGetRelid(heapRelation),
@@ -1356,7 +1362,7 @@ index_constraint_create(Relation heapRelation,
 	 * checking trigger.  (The trigger will be given an internal dependency on
 	 * the constraint by CreateTrigger.)
 	 */
-	if (deferrable)
+	if (deferral != 'n')
 	{
 		CreateTrigStmt *trigger;
 
@@ -1373,8 +1379,7 @@ index_constraint_create(Relation heapRelation,
 		trigger->columns = NIL;
 		trigger->whenClause = NULL;
 		trigger->isconstraint = true;
-		trigger->deferrable = true;
-		trigger->initdeferred = initdeferred;
+		trigger->deferral = deferral;
 		trigger->constrrel = NULL;
 
 		(void) CreateTrigger(trigger, NULL, RelationGetRelid(heapRelation),
@@ -1391,7 +1396,7 @@ index_constraint_create(Relation heapRelation,
 	 * index at all.
 	 */
 	if ((constr_flags & INDEX_CONSTR_CREATE_UPDATE_INDEX) &&
-		(mark_as_primary || deferrable))
+		(mark_as_primary || deferral != 'n'))
 	{
 		Relation	pg_index;
 		HeapTuple	indexTuple;
@@ -1412,7 +1417,7 @@ index_constraint_create(Relation heapRelation,
 			dirty = true;
 		}
 
-		if (deferrable && indexForm->indimmediate)
+		if (deferral != 'n' && indexForm->indimmediate)
 		{
 			indexForm->indimmediate = false;
 			dirty = true;
diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql
index f4e69f4..bde6199 100644
--- a/src/backend/catalog/information_schema.sql
+++ b/src/backend/catalog/information_schema.sql
@@ -891,10 +891,14 @@ CREATE VIEW domain_constraints AS
            CAST(current_database() AS sql_identifier) AS domain_catalog,
            CAST(n.nspname AS sql_identifier) AS domain_schema,
            CAST(t.typname AS sql_identifier) AS domain_name,
-           CAST(CASE WHEN condeferrable THEN 'YES' ELSE 'NO' END
+           CAST(CASE WHEN condeferral = 'n' THEN 'NO' ELSE 'YES' END
              AS yes_or_no) AS is_deferrable,
-           CAST(CASE WHEN condeferred THEN 'YES' ELSE 'NO' END
+           CAST(CASE WHEN condeferral = 'i' OR condeferral = 'a' THEN 'YES' ELSE 'NO' END
              AS yes_or_no) AS initially_deferred
+	   /*
+	    * XXX Can we add is_always_deferred here?  Are there
+	    * standards considerations?
+	    */
     FROM pg_namespace rs, pg_namespace n, pg_constraint con, pg_type t
     WHERE rs.oid = con.connamespace
           AND n.oid = t.typnamespace
@@ -1780,9 +1784,9 @@ CREATE VIEW table_constraints AS
                             WHEN 'p' THEN 'PRIMARY KEY'
                             WHEN 'u' THEN 'UNIQUE' END
              AS character_data) AS constraint_type,
-           CAST(CASE WHEN c.condeferrable THEN 'YES' ELSE 'NO' END AS yes_or_no)
+           CAST(CASE WHEN c.condeferral = 'n' THEN 'NO' ELSE 'YES' END AS yes_or_no)
              AS is_deferrable,
-           CAST(CASE WHEN c.condeferred THEN 'YES' ELSE 'NO' END AS yes_or_no)
+           CAST(CASE WHEN c.condeferral = 'i' OR c.condeferral = 'a' THEN 'YES' ELSE 'NO' END AS yes_or_no)
              AS initially_deferred,
            CAST('YES' AS yes_or_no) AS enforced
 
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index 7a6d158..321a83e 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -51,8 +51,7 @@ Oid
 CreateConstraintEntry(const char *constraintName,
 					  Oid constraintNamespace,
 					  char constraintType,
-					  bool isDeferrable,
-					  bool isDeferred,
+					  char deferralOption,
 					  bool isValidated,
 					  Oid parentConstrId,
 					  Oid relId,
@@ -184,8 +183,7 @@ CreateConstraintEntry(const char *constraintName,
 	values[Anum_pg_constraint_conname - 1] = NameGetDatum(&cname);
 	values[Anum_pg_constraint_connamespace - 1] = ObjectIdGetDatum(constraintNamespace);
 	values[Anum_pg_constraint_contype - 1] = CharGetDatum(constraintType);
-	values[Anum_pg_constraint_condeferrable - 1] = BoolGetDatum(isDeferrable);
-	values[Anum_pg_constraint_condeferred - 1] = BoolGetDatum(isDeferred);
+	values[Anum_pg_constraint_condeferral - 1] = CharGetDatum(deferralOption);
 	values[Anum_pg_constraint_convalidated - 1] = BoolGetDatum(isValidated);
 	values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(relId);
 	values[Anum_pg_constraint_contypid - 1] = ObjectIdGetDatum(domainId);
@@ -564,8 +562,7 @@ CloneForeignKeyConstraints(Oid parentId, Oid relationId, List **cloned)
 			CreateConstraintEntry(NameStr(constrForm->conname),
 								  constrForm->connamespace,
 								  CONSTRAINT_FOREIGN,
-								  constrForm->condeferrable,
-								  constrForm->condeferred,
+								  constrForm->condeferral,
 								  constrForm->convalidated,
 								  HeapTupleGetOid(tuple),
 								  relationId,
@@ -597,8 +594,7 @@ CloneForeignKeyConstraints(Oid parentId, Oid relationId, List **cloned)
 		/* for now this is all we need */
 		fkconstraint->fk_upd_action = constrForm->confupdtype;
 		fkconstraint->fk_del_action = constrForm->confdeltype;
-		fkconstraint->deferrable = constrForm->condeferrable;
-		fkconstraint->initdeferred = constrForm->condeferred;
+		fkconstraint->deferral = constrForm->condeferral;
 
 		createForeignKeyTriggers(rel, constrForm->confrelid, fkconstraint,
 								 constrOid, constrForm->conindid, false);
@@ -1357,7 +1353,7 @@ get_primary_key_attnos(Oid relid, bool deferrableOk, Oid *constraintOid)
 		 * ignore deferrable constraints, then we might as well give up
 		 * searching, since there can only be a single primary key on a table.
 		 */
-		if (con->condeferrable && !deferrableOk)
+		if (con->condeferral != 'n' && !deferrableOk)
 			break;
 
 		/* Extract the conkey array, ie, attnums of PK's columns */
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 3a3223b..77ff99b 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -841,10 +841,12 @@ DefineIndex(Oid relationId,
 	if (partitioned && stmt->relation && !stmt->relation->inh)
 		flags |= INDEX_CREATE_INVALID;
 
-	if (stmt->deferrable)
+	if (stmt->deferral != 'n')
 		constr_flags |= INDEX_CONSTR_CREATE_DEFERRABLE;
-	if (stmt->initdeferred)
+	if (stmt->deferral == 'i')
 		constr_flags |= INDEX_CONSTR_CREATE_INIT_DEFERRED;
+	if (stmt->deferral == 'a')
+		constr_flags |= INDEX_CONSTR_CREATE_ALWAYS_DEFERRED;
 
 	indexRelationId =
 		index_create(rel, indexRelationName, indexRelationId, parentIndexId,
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 0e95037..52e2de1 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -7042,8 +7042,9 @@ ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
 	/* Create the catalog entries for the constraint */
 	flags = INDEX_CONSTR_CREATE_UPDATE_INDEX |
 		INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS |
-		(stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
-		(stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
+		(stmt->deferral == 'a' ? INDEX_CONSTR_CREATE_ALWAYS_DEFERRED : 0) |
+		(stmt->deferral == 'i' ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
+		(stmt->deferral != 'n' ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
 		(stmt->primary ? INDEX_CONSTR_CREATE_MARK_AS_PRIMARY : 0);
 
 	address = index_constraint_create(rel,
@@ -7635,8 +7636,7 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
 	constrOid = CreateConstraintEntry(fkconstraint->conname,
 									  RelationGetNamespace(rel),
 									  CONSTRAINT_FOREIGN,
-									  fkconstraint->deferrable,
-									  fkconstraint->initdeferred,
+									  fkconstraint->deferral,
 									  fkconstraint->initially_valid,
 									  parentConstr,
 									  RelationGetRelid(rel),
@@ -7798,8 +7798,7 @@ ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd,
 				 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
 						cmdcon->conname, RelationGetRelationName(rel))));
 
-	if (currcon->condeferrable != cmdcon->deferrable ||
-		currcon->condeferred != cmdcon->initdeferred)
+	if (currcon->condeferral != cmdcon->deferral)
 	{
 		HeapTuple	copyTuple;
 		HeapTuple	tgtuple;
@@ -7815,8 +7814,7 @@ ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd,
 		 */
 		copyTuple = heap_copytuple(contuple);
 		copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
-		copy_con->condeferrable = cmdcon->deferrable;
-		copy_con->condeferred = cmdcon->initdeferred;
+		copy_con->condeferral = cmdcon->deferral;
 		CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
 
 		InvokeObjectPostAlterHook(ConstraintRelationId,
@@ -7868,8 +7866,7 @@ ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd,
 			copyTuple = heap_copytuple(tgtuple);
 			copy_tg = (Form_pg_trigger) GETSTRUCT(copyTuple);
 
-			copy_tg->tgdeferrable = cmdcon->deferrable;
-			copy_tg->tginitdeferred = cmdcon->initdeferred;
+			copy_tg->tgdeferral = cmdcon->deferral;
 			CatalogTupleUpdate(tgrel, &copyTuple->t_self, copyTuple);
 
 			InvokeObjectPostAlterHook(TriggerRelationId,
@@ -8535,8 +8532,7 @@ validateForeignKeyConstraint(char *conname,
 	trig.tgconstrrelid = RelationGetRelid(pkrel);
 	trig.tgconstrindid = pkindOid;
 	trig.tgconstraint = constraintOid;
-	trig.tgdeferrable = false;
-	trig.tginitdeferred = false;
+	trig.tgdeferral = 'n';
 	/* we needn't fill in remaining fields */
 
 	/*
@@ -8624,8 +8620,7 @@ CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
 	fk_trigger->transitionRels = NIL;
 	fk_trigger->whenClause = NULL;
 	fk_trigger->isconstraint = true;
-	fk_trigger->deferrable = fkconstraint->deferrable;
-	fk_trigger->initdeferred = fkconstraint->initdeferred;
+	fk_trigger->deferral = fkconstraint->deferral;
 	fk_trigger->constrrel = NULL;
 	fk_trigger->args = NIL;
 
@@ -8665,28 +8660,23 @@ createForeignKeyActionTriggers(Relation rel, Oid refRelOid, Constraint *fkconstr
 	switch (fkconstraint->fk_del_action)
 	{
 		case FKCONSTR_ACTION_NOACTION:
-			fk_trigger->deferrable = fkconstraint->deferrable;
-			fk_trigger->initdeferred = fkconstraint->initdeferred;
+			fk_trigger->deferral = fkconstraint->deferral;
 			fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
 			break;
 		case FKCONSTR_ACTION_RESTRICT:
-			fk_trigger->deferrable = false;
-			fk_trigger->initdeferred = false;
+			fk_trigger->deferral = 'n';
 			fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
 			break;
 		case FKCONSTR_ACTION_CASCADE:
-			fk_trigger->deferrable = false;
-			fk_trigger->initdeferred = false;
+			fk_trigger->deferral = 'n';
 			fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
 			break;
 		case FKCONSTR_ACTION_SETNULL:
-			fk_trigger->deferrable = false;
-			fk_trigger->initdeferred = false;
+			fk_trigger->deferral = 'n';
 			fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
 			break;
 		case FKCONSTR_ACTION_SETDEFAULT:
-			fk_trigger->deferrable = false;
-			fk_trigger->initdeferred = false;
+			fk_trigger->deferral = 'n';
 			fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
 			break;
 		default:
@@ -8721,28 +8711,23 @@ createForeignKeyActionTriggers(Relation rel, Oid refRelOid, Constraint *fkconstr
 	switch (fkconstraint->fk_upd_action)
 	{
 		case FKCONSTR_ACTION_NOACTION:
-			fk_trigger->deferrable = fkconstraint->deferrable;
-			fk_trigger->initdeferred = fkconstraint->initdeferred;
+			fk_trigger->deferral = fkconstraint->deferral;
 			fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
 			break;
 		case FKCONSTR_ACTION_RESTRICT:
-			fk_trigger->deferrable = false;
-			fk_trigger->initdeferred = false;
+			fk_trigger->deferral = 'n';
 			fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
 			break;
 		case FKCONSTR_ACTION_CASCADE:
-			fk_trigger->deferrable = false;
-			fk_trigger->initdeferred = false;
+			fk_trigger->deferral = 'n';
 			fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
 			break;
 		case FKCONSTR_ACTION_SETNULL:
-			fk_trigger->deferrable = false;
-			fk_trigger->initdeferred = false;
+			fk_trigger->deferral = 'n';
 			fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
 			break;
 		case FKCONSTR_ACTION_SETDEFAULT:
-			fk_trigger->deferrable = false;
-			fk_trigger->initdeferred = false;
+			fk_trigger->deferral = 'n';
 			fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
 			break;
 		default:
@@ -11593,8 +11578,7 @@ constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
 	Form_pg_constraint acon = (Form_pg_constraint) GETSTRUCT(a);
 	Form_pg_constraint bcon = (Form_pg_constraint) GETSTRUCT(b);
 
-	if (acon->condeferrable != bcon->condeferrable ||
-		acon->condeferred != bcon->condeferred ||
+	if (acon->condeferral != bcon->condeferral ||
 		strcmp(decompile_conbin(a, tupleDesc),
 			   decompile_conbin(b, tupleDesc)) != 0)
 		return false;
@@ -14587,8 +14571,7 @@ CloneRowTriggersToPartition(Relation parent, Relation partition)
 		trigStmt->whenClause = NULL;	/* passed separately */
 		trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
 		trigStmt->transitionRels = NIL; /* not supported at present */
-		trigStmt->deferrable = trigForm->tgdeferrable;
-		trigStmt->initdeferred = trigForm->tginitdeferred;
+		trigStmt->deferral = trigForm->tgdeferral;
 		trigStmt->constrrel = NULL; /* passed separately */
 
 		CreateTrigger(trigStmt, NULL, RelationGetRelid(partition),
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 57519fe..41dc6a4 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -728,8 +728,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
 		constraintOid = CreateConstraintEntry(stmt->trigname,
 											  RelationGetNamespace(rel),
 											  CONSTRAINT_TRIGGER,
-											  stmt->deferrable,
-											  stmt->initdeferred,
+											  stmt->deferral,
 											  true,
 											  InvalidOid,	/* no parent */
 											  RelationGetRelid(rel),
@@ -833,8 +832,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
 	values[Anum_pg_trigger_tgconstrrelid - 1] = ObjectIdGetDatum(constrrelid);
 	values[Anum_pg_trigger_tgconstrindid - 1] = ObjectIdGetDatum(indexOid);
 	values[Anum_pg_trigger_tgconstraint - 1] = ObjectIdGetDatum(constraintOid);
-	values[Anum_pg_trigger_tgdeferrable - 1] = BoolGetDatum(stmt->deferrable);
-	values[Anum_pg_trigger_tginitdeferred - 1] = BoolGetDatum(stmt->initdeferred);
+	values[Anum_pg_trigger_tgdeferral - 1] = CharGetDatum(stmt->deferral);
 
 	if (stmt->args)
 	{
@@ -1449,8 +1447,7 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
 				/* can't get here because of earlier checks */
 				elog(ERROR, "confused about RI delete function");
 		}
-		fkcon->deferrable = stmt->deferrable;
-		fkcon->initdeferred = stmt->initdeferred;
+		fkcon->deferral = stmt->deferral;
 		fkcon->skip_validation = false;
 		fkcon->initially_valid = true;
 
@@ -1967,8 +1964,7 @@ RelationBuildTriggers(Relation relation)
 		build->tgconstrrelid = pg_trigger->tgconstrrelid;
 		build->tgconstrindid = pg_trigger->tgconstrindid;
 		build->tgconstraint = pg_trigger->tgconstraint;
-		build->tgdeferrable = pg_trigger->tgdeferrable;
-		build->tginitdeferred = pg_trigger->tginitdeferred;
+		build->tgdeferral = pg_trigger->tgdeferral;
 		build->tgnargs = pg_trigger->tgnargs;
 		/* tgattr is first var-width field, so OK to access directly */
 		build->tgnattr = pg_trigger->tgattr.dim1;
@@ -2273,9 +2269,7 @@ equalTriggerDescs(TriggerDesc *trigdesc1, TriggerDesc *trigdesc2)
 				return false;
 			if (trig1->tgconstraint != trig2->tgconstraint)
 				return false;
-			if (trig1->tgdeferrable != trig2->tgdeferrable)
-				return false;
-			if (trig1->tginitdeferred != trig2->tginitdeferred)
+			if (trig1->tgdeferral != trig2->tgdeferral)
 				return false;
 			if (trig1->tgnargs != trig2->tgnargs)
 				return false;
@@ -2370,7 +2364,8 @@ ExecCallTriggerFunc(TriggerData *trigdata,
 			 TRIGGER_FIRED_BY_DELETE(trigdata->tg_event)) &&
 			TRIGGER_FIRED_AFTER(trigdata->tg_event) &&
 			!(trigdata->tg_event & AFTER_TRIGGER_DEFERRABLE) &&
-			!(trigdata->tg_event & AFTER_TRIGGER_INITDEFERRED)) ||
+			!(trigdata->tg_event & AFTER_TRIGGER_INITDEFERRED) &&
+			!(trigdata->tg_event & AFTER_TRIGGER_ALWAYSDEFERRED)) ||
 		   (trigdata->tg_oldtable == NULL && trigdata->tg_newtable == NULL));
 
 	finfo += tgindx;
@@ -3632,6 +3627,7 @@ typedef struct AfterTriggerSharedData
 	TriggerEvent ats_event;		/* event type indicator, see trigger.h */
 	Oid			ats_tgoid;		/* the trigger's ID */
 	Oid			ats_relid;		/* the relation it's on */
+	bool			ats_alwaysdeferred;	/* whether this can be deferred */
 	CommandId	ats_firing_id;	/* ID for firing cycle */
 	struct AfterTriggersTableData *ats_table;	/* transition table access */
 } AfterTriggerSharedData;
@@ -3911,6 +3907,8 @@ afterTriggerCheckState(AfterTriggerShared evtshared)
 	 */
 	if ((evtshared->ats_event & AFTER_TRIGGER_DEFERRABLE) == 0)
 		return false;
+	if ((evtshared->ats_event & AFTER_TRIGGER_ALWAYSDEFERRED))
+		return true;
 
 	/*
 	 * If constraint state exists, SET CONSTRAINTS might have been executed
@@ -5420,14 +5418,19 @@ AfterTriggerSetState(ConstraintsSetStmt *stmt)
 				{
 					Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tup);
 
-					if (con->condeferrable)
-						conoidlist = lappend_oid(conoidlist,
-												 HeapTupleGetOid(tup));
-					else if (stmt->deferred)
+					if (stmt->deferred && con->condeferral == 'n')
 						ereport(ERROR,
 								(errcode(ERRCODE_WRONG_OBJECT_TYPE),
 								 errmsg("constraint \"%s\" is not deferrable",
 										constraint->relname)));
+					else if (!stmt->deferred && con->condeferral == 'a')
+						ereport(ERROR,
+								(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+								 errmsg("constraint \"%s\" is always deferred",
+										constraint->relname)));
+					else if (con->condeferral != 'n' && con->condeferral != 'a')
+						conoidlist = lappend_oid(conoidlist,
+												 HeapTupleGetOid(tup));
 					found = true;
 				}
 
@@ -5515,7 +5518,7 @@ AfterTriggerSetState(ConstraintsSetStmt *stmt)
 				 * deferrable RI constraint may have some non-deferrable
 				 * actions.
 				 */
-				if (pg_trigger->tgdeferrable)
+				if (pg_trigger->tgdeferral != 'n')
 					tgoidlist = lappend_oid(tgoidlist,
 											HeapTupleGetOid(htup));
 
@@ -5975,8 +5978,9 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
 		new_shared.ats_event =
 			(event & TRIGGER_EVENT_OPMASK) |
 			(row_trigger ? TRIGGER_EVENT_ROW : 0) |
-			(trigger->tgdeferrable ? AFTER_TRIGGER_DEFERRABLE : 0) |
-			(trigger->tginitdeferred ? AFTER_TRIGGER_INITDEFERRED : 0);
+			(trigger->tgdeferral != 'n' ? AFTER_TRIGGER_DEFERRABLE : 0) |
+			(trigger->tgdeferral == 'i' ? AFTER_TRIGGER_INITDEFERRED : 0) |
+			(trigger->tgdeferral == 'a' ? AFTER_TRIGGER_ALWAYSDEFERRED : 0);
 		new_shared.ats_tgoid = trigger->tgoid;
 		new_shared.ats_relid = RelationGetRelid(rel);
 		new_shared.ats_firing_id = 0;
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 175ecc8..156659a 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -1016,6 +1016,7 @@ DefineDomain(CreateDomainStmt *stmt)
 			case CONSTR_ATTR_DEFERRABLE:
 			case CONSTR_ATTR_NOT_DEFERRABLE:
 			case CONSTR_ATTR_DEFERRED:
+			case CONSTR_ATTR_ALWAYS_DEFERRED:
 			case CONSTR_ATTR_IMMEDIATE:
 				ereport(ERROR,
 						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
@@ -2602,6 +2603,7 @@ AlterDomainAddConstraint(List *names, Node *newConstraint,
 		case CONSTR_ATTR_DEFERRABLE:
 		case CONSTR_ATTR_NOT_DEFERRABLE:
 		case CONSTR_ATTR_DEFERRED:
+		case CONSTR_ATTR_ALWAYS_DEFERRED:
 		case CONSTR_ATTR_IMMEDIATE:
 			ereport(ERROR,
 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
@@ -3147,8 +3149,7 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
 		CreateConstraintEntry(constr->conname,	/* Constraint Name */
 							  domainNamespace,	/* namespace */
 							  CONSTRAINT_CHECK, /* Constraint Type */
-							  false,	/* Is Deferrable */
-							  false,	/* Is Deferred */
+							  'n',		    /* Deferral option */
 							  !constr->skip_validation, /* Is Validated */
 							  InvalidOid,	/* no parent constraint */
 							  InvalidOid,	/* not a relation constraint */
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 7c045a7..758ddb0 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2882,8 +2882,7 @@ _copyConstraint(const Constraint *from)
 
 	COPY_SCALAR_FIELD(contype);
 	COPY_STRING_FIELD(conname);
-	COPY_SCALAR_FIELD(deferrable);
-	COPY_SCALAR_FIELD(initdeferred);
+	COPY_SCALAR_FIELD(deferral);
 	COPY_LOCATION_FIELD(location);
 	COPY_SCALAR_FIELD(is_no_inherit);
 	COPY_NODE_FIELD(raw_expr);
@@ -3444,8 +3443,7 @@ _copyIndexStmt(const IndexStmt *from)
 	COPY_SCALAR_FIELD(unique);
 	COPY_SCALAR_FIELD(primary);
 	COPY_SCALAR_FIELD(isconstraint);
-	COPY_SCALAR_FIELD(deferrable);
-	COPY_SCALAR_FIELD(initdeferred);
+	COPY_SCALAR_FIELD(deferral);
 	COPY_SCALAR_FIELD(transformed);
 	COPY_SCALAR_FIELD(concurrent);
 	COPY_SCALAR_FIELD(if_not_exists);
@@ -4216,8 +4214,7 @@ _copyCreateTrigStmt(const CreateTrigStmt *from)
 	COPY_NODE_FIELD(whenClause);
 	COPY_SCALAR_FIELD(isconstraint);
 	COPY_NODE_FIELD(transitionRels);
-	COPY_SCALAR_FIELD(deferrable);
-	COPY_SCALAR_FIELD(initdeferred);
+	COPY_SCALAR_FIELD(deferral);
 	COPY_NODE_FIELD(constrrel);
 
 	return newnode;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 6a971d0..4ac1c23 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1338,8 +1338,7 @@ _equalIndexStmt(const IndexStmt *a, const IndexStmt *b)
 	COMPARE_SCALAR_FIELD(unique);
 	COMPARE_SCALAR_FIELD(primary);
 	COMPARE_SCALAR_FIELD(isconstraint);
-	COMPARE_SCALAR_FIELD(deferrable);
-	COMPARE_SCALAR_FIELD(initdeferred);
+	COMPARE_SCALAR_FIELD(deferral);
 	COMPARE_SCALAR_FIELD(transformed);
 	COMPARE_SCALAR_FIELD(concurrent);
 	COMPARE_SCALAR_FIELD(if_not_exists);
@@ -1992,8 +1991,7 @@ _equalCreateTrigStmt(const CreateTrigStmt *a, const CreateTrigStmt *b)
 	COMPARE_NODE_FIELD(whenClause);
 	COMPARE_SCALAR_FIELD(isconstraint);
 	COMPARE_NODE_FIELD(transitionRels);
-	COMPARE_SCALAR_FIELD(deferrable);
-	COMPARE_SCALAR_FIELD(initdeferred);
+	COMPARE_SCALAR_FIELD(deferral);
 	COMPARE_NODE_FIELD(constrrel);
 
 	return true;
@@ -2573,8 +2571,7 @@ _equalConstraint(const Constraint *a, const Constraint *b)
 {
 	COMPARE_SCALAR_FIELD(contype);
 	COMPARE_STRING_FIELD(conname);
-	COMPARE_SCALAR_FIELD(deferrable);
-	COMPARE_SCALAR_FIELD(initdeferred);
+	COMPARE_SCALAR_FIELD(deferral);
 	COMPARE_LOCATION_FIELD(location);
 	COMPARE_SCALAR_FIELD(is_no_inherit);
 	COMPARE_NODE_FIELD(raw_expr);
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 610f9ed..8490410 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2713,8 +2713,7 @@ _outIndexStmt(StringInfo str, const IndexStmt *node)
 	WRITE_BOOL_FIELD(unique);
 	WRITE_BOOL_FIELD(primary);
 	WRITE_BOOL_FIELD(isconstraint);
-	WRITE_BOOL_FIELD(deferrable);
-	WRITE_BOOL_FIELD(initdeferred);
+	WRITE_BOOL_FIELD(deferral);
 	WRITE_BOOL_FIELD(transformed);
 	WRITE_BOOL_FIELD(concurrent);
 	WRITE_BOOL_FIELD(if_not_exists);
@@ -3491,8 +3490,7 @@ _outConstraint(StringInfo str, const Constraint *node)
 	WRITE_NODE_TYPE("CONSTRAINT");
 
 	WRITE_STRING_FIELD(conname);
-	WRITE_BOOL_FIELD(deferrable);
-	WRITE_BOOL_FIELD(initdeferred);
+	WRITE_BOOL_FIELD(deferral);
 	WRITE_LOCATION_FIELD(location);
 
 	appendStringInfoString(str, " :contype ");
@@ -3579,6 +3577,10 @@ _outConstraint(StringInfo str, const Constraint *node)
 			appendStringInfoString(str, "ATTR_NOT_DEFERRABLE");
 			break;
 
+		case CONSTR_ATTR_ALWAYS_DEFERRED:
+			appendStringInfoString(str, "ATTR_ALWAYS_DEFERRED");
+			break;
+
 		case CONSTR_ATTR_DEFERRED:
 			appendStringInfoString(str, "ATTR_DEFERRED");
 			break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 90dfac2..dab721a 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -133,6 +133,7 @@ typedef struct ImportQual
 #define CAS_INITIALLY_DEFERRED		0x08
 #define CAS_NOT_VALID				0x10
 #define CAS_NO_INHERIT				0x20
+#define CAS_ALWAYS_DEFERRED		0x40
 
 
 #define parser_yyerror(msg)  scanner_yyerror(msg, yyscanner)
@@ -184,8 +185,8 @@ static void SplitColQualList(List *qualList,
 							 List **constraintList, CollateClause **collClause,
 							 core_yyscan_t yyscanner);
 static void processCASbits(int cas_bits, int location, const char *constrType,
-			   bool *deferrable, bool *initdeferred, bool *not_valid,
-			   bool *no_inherit, core_yyscan_t yyscanner);
+			   char *deferral,
+			   bool *not_valid, bool *no_inherit, core_yyscan_t yyscanner);
 static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 
 %}
@@ -734,6 +735,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
  * for RANGE, ROWS, GROUPS so that they can follow a_expr without creating
  * postfix-operator problems;
  * for GENERATED so that it can follow b_expr;
+ * for ALWAYS for column constraints ALWAYS DEFERRED;
  * and for NULL so that it can follow b_expr in ColQualList without creating
  * postfix-operator problems.
  *
@@ -752,7 +754,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
  * blame any funny behavior of UNBOUNDED on the SQL standard, though.
  */
 %nonassoc	UNBOUNDED		/* ideally should have same precedence as IDENT */
-%nonassoc	IDENT GENERATED NULL_P PARTITION RANGE ROWS GROUPS PRECEDING FOLLOWING CUBE ROLLUP
+%nonassoc	IDENT GENERATED NULL_P PARTITION RANGE ROWS GROUPS PRECEDING FOLLOWING CUBE ROLLUP ALWAYS
 %left		Op OPERATOR		/* multi-character ops and user-defined operators */
 %left		'+' '-'
 %left		'*' '/' '%'
@@ -2180,6 +2182,7 @@ alter_table_cmd:
 					c->generated_when = $6;
 					c->options = $9;
 					c->location = @5;
+					c->deferral = 'n';
 
 					n->subtype = AT_AddIdentity;
 					n->name = $3;
@@ -2279,9 +2282,9 @@ alter_table_cmd:
 					c->contype = CONSTR_FOREIGN; /* others not supported, yet */
 					c->conname = $3;
 					processCASbits($4, @4, "ALTER CONSTRAINT statement",
-									&c->deferrable,
-									&c->initdeferred,
-									NULL, NULL, yyscanner);
+									&c->deferral,
+									NULL, NULL,
+									yyscanner);
 					$$ = (Node *)n;
 				}
 			/* ALTER TABLE <name> VALIDATE CONSTRAINT ... */
@@ -3493,6 +3496,7 @@ ColConstraintElem:
 					Constraint *n = makeNode(Constraint);
 					n->contype = CONSTR_NOTNULL;
 					n->location = @1;
+					n->deferral = 'n';
 					$$ = (Node *)n;
 				}
 			| NULL_P
@@ -3500,6 +3504,7 @@ ColConstraintElem:
 					Constraint *n = makeNode(Constraint);
 					n->contype = CONSTR_NULL;
 					n->location = @1;
+					n->deferral = 'n';
 					$$ = (Node *)n;
 				}
 			| UNIQUE opt_definition OptConsTableSpace
@@ -3511,6 +3516,7 @@ ColConstraintElem:
 					n->options = $2;
 					n->indexname = NULL;
 					n->indexspace = $3;
+					n->deferral = 'n';
 					$$ = (Node *)n;
 				}
 			| PRIMARY KEY opt_definition OptConsTableSpace
@@ -3522,6 +3528,7 @@ ColConstraintElem:
 					n->options = $3;
 					n->indexname = NULL;
 					n->indexspace = $4;
+					n->deferral = 'n';
 					$$ = (Node *)n;
 				}
 			| CHECK '(' a_expr ')' opt_no_inherit
@@ -3534,6 +3541,7 @@ ColConstraintElem:
 					n->cooked_expr = NULL;
 					n->skip_validation = false;
 					n->initially_valid = true;
+					n->deferral = 'n';
 					$$ = (Node *)n;
 				}
 			| DEFAULT b_expr
@@ -3543,6 +3551,7 @@ ColConstraintElem:
 					n->location = @1;
 					n->raw_expr = $2;
 					n->cooked_expr = NULL;
+					n->deferral = 'n';
 					$$ = (Node *)n;
 				}
 			| GENERATED generated_when AS IDENTITY_P OptParenthesizedSeqOptList
@@ -3552,6 +3561,7 @@ ColConstraintElem:
 					n->generated_when = $2;
 					n->options = $5;
 					n->location = @1;
+					n->deferral = 'n';
 					$$ = (Node *)n;
 				}
 			| REFERENCES qualified_name opt_column_list key_match key_actions
@@ -3567,6 +3577,7 @@ ColConstraintElem:
 					n->fk_del_action	= (char) ($5 & 0xFF);
 					n->skip_validation  = false;
 					n->initially_valid  = true;
+					n->deferral = 'n';
 					$$ = (Node *)n;
 				}
 		;
@@ -3597,6 +3608,7 @@ ConstraintAttr:
 					Constraint *n = makeNode(Constraint);
 					n->contype = CONSTR_ATTR_DEFERRABLE;
 					n->location = @1;
+					n->deferral = 'd';
 					$$ = (Node *)n;
 				}
 			| NOT DEFERRABLE
@@ -3604,6 +3616,7 @@ ConstraintAttr:
 					Constraint *n = makeNode(Constraint);
 					n->contype = CONSTR_ATTR_NOT_DEFERRABLE;
 					n->location = @1;
+					n->deferral = 'n';
 					$$ = (Node *)n;
 				}
 			| INITIALLY DEFERRED
@@ -3611,6 +3624,7 @@ ConstraintAttr:
 					Constraint *n = makeNode(Constraint);
 					n->contype = CONSTR_ATTR_DEFERRED;
 					n->location = @1;
+					n->deferral = 'i';
 					$$ = (Node *)n;
 				}
 			| INITIALLY IMMEDIATE
@@ -3618,6 +3632,15 @@ ConstraintAttr:
 					Constraint *n = makeNode(Constraint);
 					n->contype = CONSTR_ATTR_IMMEDIATE;
 					n->location = @1;
+					n->deferral = 'd';
+					$$ = (Node *)n;
+				}
+			| ALWAYS DEFERRED
+				{
+					Constraint *n = makeNode(Constraint);
+					n->contype = CONSTR_ATTR_ALWAYS_DEFERRED;
+					n->location = @1;
+					n->deferral = 'a';
 					$$ = (Node *)n;
 				}
 		;
@@ -3675,8 +3698,10 @@ ConstraintElem:
 					n->raw_expr = $3;
 					n->cooked_expr = NULL;
 					processCASbits($5, @5, "CHECK",
-								   NULL, NULL, &n->skip_validation,
-								   &n->is_no_inherit, yyscanner);
+								   &n->deferral,
+								   &n->skip_validation,
+								   &n->is_no_inherit,
+								   yyscanner);
 					n->initially_valid = !n->skip_validation;
 					$$ = (Node *)n;
 				}
@@ -3692,8 +3717,9 @@ ConstraintElem:
 					n->indexname = NULL;
 					n->indexspace = $7;
 					processCASbits($8, @8, "UNIQUE",
-								   &n->deferrable, &n->initdeferred, NULL,
-								   NULL, yyscanner);
+								   &n->deferral,
+								   NULL, NULL,
+								   yyscanner);
 					$$ = (Node *)n;
 				}
 			| UNIQUE ExistingIndex ConstraintAttributeSpec
@@ -3707,8 +3733,9 @@ ConstraintElem:
 					n->indexname = $2;
 					n->indexspace = NULL;
 					processCASbits($3, @3, "UNIQUE",
-								   &n->deferrable, &n->initdeferred, NULL,
-								   NULL, yyscanner);
+								   &n->deferral,
+								   NULL, NULL,
+								   yyscanner);
 					$$ = (Node *)n;
 				}
 			| PRIMARY KEY '(' columnList ')' opt_c_include opt_definition OptConsTableSpace
@@ -3723,8 +3750,9 @@ ConstraintElem:
 					n->indexname = NULL;
 					n->indexspace = $8;
 					processCASbits($9, @9, "PRIMARY KEY",
-								   &n->deferrable, &n->initdeferred, NULL,
-								   NULL, yyscanner);
+								   &n->deferral,
+								   NULL, NULL,
+								   yyscanner);
 					$$ = (Node *)n;
 				}
 			| PRIMARY KEY ExistingIndex ConstraintAttributeSpec
@@ -3738,8 +3766,9 @@ ConstraintElem:
 					n->indexname = $3;
 					n->indexspace = NULL;
 					processCASbits($4, @4, "PRIMARY KEY",
-								   &n->deferrable, &n->initdeferred, NULL,
-								   NULL, yyscanner);
+								   &n->deferral,
+								   NULL, NULL,
+								   yyscanner);
 					$$ = (Node *)n;
 				}
 			| EXCLUDE access_method_clause '(' ExclusionConstraintList ')'
@@ -3757,8 +3786,9 @@ ConstraintElem:
 					n->indexspace		= $8;
 					n->where_clause		= $9;
 					processCASbits($10, @10, "EXCLUDE",
-								   &n->deferrable, &n->initdeferred, NULL,
-								   NULL, yyscanner);
+								   &n->deferral,
+								   NULL, NULL,
+								   yyscanner);
 					$$ = (Node *)n;
 				}
 			| FOREIGN KEY '(' columnList ')' REFERENCES qualified_name
@@ -3774,7 +3804,7 @@ ConstraintElem:
 					n->fk_upd_action	= (char) ($10 >> 8);
 					n->fk_del_action	= (char) ($10 & 0xFF);
 					processCASbits($11, @11, "FOREIGN KEY",
-								   &n->deferrable, &n->initdeferred,
+								   &n->deferral,
 								   &n->skip_validation, NULL,
 								   yyscanner);
 					n->initially_valid = !n->skip_validation;
@@ -5358,8 +5388,7 @@ CreateTrigStmt:
 					n->whenClause = $10;
 					n->transitionRels = $8;
 					n->isconstraint  = false;
-					n->deferrable	 = false;
-					n->initdeferred  = false;
+					n->deferral	 = 'n';
 					n->constrrel = NULL;
 					$$ = (Node *)n;
 				}
@@ -5381,8 +5410,9 @@ CreateTrigStmt:
 					n->transitionRels = NIL;
 					n->isconstraint  = true;
 					processCASbits($10, @10, "TRIGGER",
-								   &n->deferrable, &n->initdeferred, NULL,
-								   NULL, yyscanner);
+								   &n->deferral,
+								   NULL, NULL,
+								   yyscanner);
 					n->constrrel = $9;
 					$$ = (Node *)n;
 				}
@@ -5538,17 +5568,24 @@ ConstraintAttributeSpec:
 					int		newspec = $1 | $2;
 
 					/* special message for this case */
-					if ((newspec & (CAS_NOT_DEFERRABLE | CAS_INITIALLY_DEFERRED)) == (CAS_NOT_DEFERRABLE | CAS_INITIALLY_DEFERRED))
+					if ((newspec & CAS_NOT_DEFERRABLE) &&
+						(newspec & (CAS_INITIALLY_DEFERRED | CAS_ALWAYS_DEFERRED)))
 						ereport(ERROR,
 								(errcode(ERRCODE_SYNTAX_ERROR),
 								 errmsg("constraint declared INITIALLY DEFERRED must be DEFERRABLE"),
 								 parser_errposition(@2)));
 					/* generic message for other conflicts */
+					if ((newspec & CAS_ALWAYS_DEFERRED) &&
+						(newspec & (CAS_INITIALLY_IMMEDIATE)))
+						ereport(ERROR,
+								(errcode(ERRCODE_SYNTAX_ERROR),
+								 errmsg("conflicting constraint properties 1"),
+								 parser_errposition(@2)));
 					if ((newspec & (CAS_NOT_DEFERRABLE | CAS_DEFERRABLE)) == (CAS_NOT_DEFERRABLE | CAS_DEFERRABLE) ||
 						(newspec & (CAS_INITIALLY_IMMEDIATE | CAS_INITIALLY_DEFERRED)) == (CAS_INITIALLY_IMMEDIATE | CAS_INITIALLY_DEFERRED))
 						ereport(ERROR,
 								(errcode(ERRCODE_SYNTAX_ERROR),
-								 errmsg("conflicting constraint properties"),
+								 errmsg("conflicting constraint properties 2"),
 								 parser_errposition(@2)));
 					$$ = newspec;
 				}
@@ -5559,6 +5596,7 @@ ConstraintAttributeElem:
 			| DEFERRABLE					{ $$ = CAS_DEFERRABLE; }
 			| INITIALLY IMMEDIATE			{ $$ = CAS_INITIALLY_IMMEDIATE; }
 			| INITIALLY DEFERRED			{ $$ = CAS_INITIALLY_DEFERRED; }
+			| ALWAYS DEFERRED			{ $$ = CAS_ALWAYS_DEFERRED; }
 			| NOT VALID						{ $$ = CAS_NOT_VALID; }
 			| NO INHERIT					{ $$ = CAS_NO_INHERIT; }
 		;
@@ -5649,8 +5687,9 @@ CreateAssertStmt:
 					n->args = list_make1($6);
 					n->isconstraint  = true;
 					processCASbits($8, @8, "ASSERTION",
-								   &n->deferrable, &n->initdeferred, NULL,
-								   NULL, yyscanner);
+								   &n->deferral,
+								   NULL, NULL,
+								   yyscanner);
 
 					ereport(ERROR,
 							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
@@ -7396,8 +7435,7 @@ IndexStmt:	CREATE opt_unique INDEX opt_concurrently opt_index_name
 					n->oldNode = InvalidOid;
 					n->primary = false;
 					n->isconstraint = false;
-					n->deferrable = false;
-					n->initdeferred = false;
+					n->deferral = 'n';
 					n->transformed = false;
 					n->if_not_exists = false;
 					$$ = (Node *)n;
@@ -7424,8 +7462,7 @@ IndexStmt:	CREATE opt_unique INDEX opt_concurrently opt_index_name
 					n->oldNode = InvalidOid;
 					n->primary = false;
 					n->isconstraint = false;
-					n->deferrable = false;
-					n->initdeferred = false;
+					n->deferral = 'n';
 					n->transformed = false;
 					n->if_not_exists = true;
 					$$ = (Node *)n;
@@ -16197,34 +16234,41 @@ SplitColQualList(List *qualList,
  */
 static void
 processCASbits(int cas_bits, int location, const char *constrType,
-			   bool *deferrable, bool *initdeferred, bool *not_valid,
-			   bool *no_inherit, core_yyscan_t yyscanner)
+			   char *deferral,
+			   bool *not_valid, bool *no_inherit, core_yyscan_t yyscanner)
 {
 	/* defaults */
-	if (deferrable)
-		*deferrable = false;
-	if (initdeferred)
-		*initdeferred = false;
+	if (deferral)
+		*deferral = 'n';
 	if (not_valid)
 		*not_valid = false;
 
-	if (cas_bits & (CAS_DEFERRABLE | CAS_INITIALLY_DEFERRED))
+	if (cas_bits & CAS_ALWAYS_DEFERRED)
 	{
-		if (deferrable)
-			*deferrable = true;
+		if (deferral)
+			*deferral = 'a';
 		else
 			ereport(ERROR,
 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 					 /* translator: %s is CHECK, UNIQUE, or similar */
-					 errmsg("%s constraints cannot be marked DEFERRABLE",
+					 errmsg("%s constraints cannot be marked ALWAYS DEFERRED",
 							constrType),
 					 parser_errposition(location)));
-	}
-
-	if (cas_bits & CAS_INITIALLY_DEFERRED)
+	} else if (cas_bits & CAS_INITIALLY_DEFERRED)
+	{
+		if (deferral)
+			*deferral = 'i';
+		else
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 /* translator: %s is CHECK, UNIQUE, or similar */
+					 errmsg("%s constraints cannot be marked INITIALLY DEFERRED",
+							constrType),
+					 parser_errposition(location)));
+	} else if (cas_bits & CAS_DEFERRABLE)
 	{
-		if (initdeferred)
-			*initdeferred = true;
+		if (deferral)
+			*deferral = 'd';
 		else
 			ereport(ERROR,
 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 17b54b2..6efa303 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -616,6 +616,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
 		constraint = makeNode(Constraint);
 		constraint->contype = CONSTR_DEFAULT;
 		constraint->location = -1;
+		constraint->deferral = 'n';
 		constraint->raw_expr = (Node *) funccallnode;
 		constraint->cooked_expr = NULL;
 		column->constraints = lappend(column->constraints, constraint);
@@ -623,6 +624,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
 		constraint = makeNode(Constraint);
 		constraint->contype = CONSTR_NOTNULL;
 		constraint->location = -1;
+		constraint->deferral = 'n';
 		column->constraints = lappend(column->constraints, constraint);
 	}
 
@@ -760,6 +762,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
 
 			case CONSTR_ATTR_DEFERRABLE:
 			case CONSTR_ATTR_NOT_DEFERRABLE:
+			case CONSTR_ATTR_ALWAYS_DEFERRED:
 			case CONSTR_ATTR_DEFERRED:
 			case CONSTR_ATTR_IMMEDIATE:
 				/* transformConstraintAttrs took care of these */
@@ -870,6 +873,7 @@ transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint)
 		case CONSTR_DEFAULT:
 		case CONSTR_ATTR_DEFERRABLE:
 		case CONSTR_ATTR_NOT_DEFERRABLE:
+		case CONSTR_ATTR_ALWAYS_DEFERRED:
 		case CONSTR_ATTR_DEFERRED:
 		case CONSTR_ATTR_IMMEDIATE:
 			elog(ERROR, "invalid context for constraint type %d",
@@ -1120,6 +1124,7 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
 
 			n->contype = CONSTR_CHECK;
 			n->location = -1;
+			n->deferral = 'n';
 			n->conname = pstrdup(ccname);
 			n->raw_expr = NULL;
 			n->cooked_expr = nodeToString(ccbin_node);
@@ -1348,6 +1353,7 @@ generateClonedIndexStmt(RangeVar *heapRel, Oid heapRelid, Relation source_idx,
 	index->relation = heapRel;
 	index->relationId = heapRelid;
 	index->accessMethod = pstrdup(NameStr(amrec->amname));
+	index->deferral = 'n';
 	if (OidIsValid(idxrelrec->reltablespace))
 		index->tableSpace = get_tablespace_name(idxrelrec->reltablespace);
 	else
@@ -1396,8 +1402,7 @@ generateClonedIndexStmt(RangeVar *heapRel, Oid heapRelid, Relation source_idx,
 			conrec = (Form_pg_constraint) GETSTRUCT(ht_constr);
 
 			index->isconstraint = true;
-			index->deferrable = conrec->condeferrable;
-			index->initdeferred = conrec->condeferred;
+			index->deferral = conrec->condeferral;
 
 			/* If it's an exclusion constraint, we need the operator names */
 			if (idxrec->indisexclusion)
@@ -1865,8 +1870,7 @@ transformIndexConstraints(CreateStmtContext *cxt)
 				equal(index->whereClause, priorindex->whereClause) &&
 				equal(index->excludeOpNames, priorindex->excludeOpNames) &&
 				strcmp(index->accessMethod, priorindex->accessMethod) == 0 &&
-				index->deferrable == priorindex->deferrable &&
-				index->initdeferred == priorindex->initdeferred)
+				index->deferral == priorindex->deferral)
 			{
 				priorindex->unique |= index->unique;
 
@@ -1919,8 +1923,7 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
 		 */
 	}
 	index->isconstraint = true;
-	index->deferrable = constraint->deferrable;
-	index->initdeferred = constraint->initdeferred;
+	index->deferral = constraint->deferral;
 
 	if (constraint->conname != NULL)
 		index->idxname = pstrdup(constraint->conname);
@@ -2035,7 +2038,7 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
 		 * non-constraint index couldn't be deferred anyway, so this case
 		 * should never occur; no need to sweat, but let's check it.)
 		 */
-		if (!index_form->indimmediate && !constraint->deferrable)
+		if (!index_form->indimmediate && constraint->deferral == 'n')
 			ereport(ERROR,
 					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
 					 errmsg("\"%s\" is a deferrable index", index_name),
@@ -3259,7 +3262,9 @@ static void
 transformConstraintAttrs(CreateStmtContext *cxt, List *constraintList)
 {
 	Constraint *lastprimarycon = NULL;
-	bool		saw_deferrability = false;
+	bool		saw_deferrable = false;
+	bool		saw_notdeferrable = false;
+	bool		saw_alwaysdeferred = false;
 	bool		saw_initially = false;
 	ListCell   *clist;
 
@@ -3285,13 +3290,13 @@ transformConstraintAttrs(CreateStmtContext *cxt, List *constraintList)
 							(errcode(ERRCODE_SYNTAX_ERROR),
 							 errmsg("misplaced DEFERRABLE clause"),
 							 parser_errposition(cxt->pstate, con->location)));
-				if (saw_deferrability)
+				if (saw_deferrable || saw_notdeferrable)
 					ereport(ERROR,
 							(errcode(ERRCODE_SYNTAX_ERROR),
 							 errmsg("multiple DEFERRABLE/NOT DEFERRABLE clauses not allowed"),
 							 parser_errposition(cxt->pstate, con->location)));
-				saw_deferrability = true;
-				lastprimarycon->deferrable = true;
+				saw_deferrable = true;
+				lastprimarycon->deferral = 'd';
 				break;
 
 			case CONSTR_ATTR_NOT_DEFERRABLE:
@@ -3300,45 +3305,61 @@ transformConstraintAttrs(CreateStmtContext *cxt, List *constraintList)
 							(errcode(ERRCODE_SYNTAX_ERROR),
 							 errmsg("misplaced NOT DEFERRABLE clause"),
 							 parser_errposition(cxt->pstate, con->location)));
-				if (saw_deferrability)
+				if (saw_deferrable || saw_notdeferrable)
 					ereport(ERROR,
 							(errcode(ERRCODE_SYNTAX_ERROR),
 							 errmsg("multiple DEFERRABLE/NOT DEFERRABLE clauses not allowed"),
 							 parser_errposition(cxt->pstate, con->location)));
-				saw_deferrability = true;
-				lastprimarycon->deferrable = false;
+				if (saw_alwaysdeferred)
+					ereport(ERROR,
+							(errcode(ERRCODE_SYNTAX_ERROR),
+							 errmsg("multiple ALWAYS DEFERRED/NOT DEFERRABLE clauses not allowed"),
+							 parser_errposition(cxt->pstate, con->location)));
+				saw_notdeferrable = true;
+				lastprimarycon->deferral = 'n';
 				if (saw_initially &&
-					lastprimarycon->initdeferred)
+					lastprimarycon->deferral == 'i')
 					ereport(ERROR,
 							(errcode(ERRCODE_SYNTAX_ERROR),
 							 errmsg("constraint declared INITIALLY DEFERRED must be DEFERRABLE"),
 							 parser_errposition(cxt->pstate, con->location)));
 				break;
 
-			case CONSTR_ATTR_DEFERRED:
+			case CONSTR_ATTR_ALWAYS_DEFERRED:
 				if (!SUPPORTS_ATTRS(lastprimarycon))
 					ereport(ERROR,
 							(errcode(ERRCODE_SYNTAX_ERROR),
-							 errmsg("misplaced INITIALLY DEFERRED clause"),
+							 errmsg("misplaced ALWAYS DEFERRED clause"),
 							 parser_errposition(cxt->pstate, con->location)));
-				if (saw_initially)
+				if (saw_alwaysdeferred || saw_notdeferrable)
 					ereport(ERROR,
 							(errcode(ERRCODE_SYNTAX_ERROR),
-							 errmsg("multiple INITIALLY IMMEDIATE/DEFERRED clauses not allowed"),
+							 errmsg("multiple ALWAYS DEFERRED/NOT DEFERRABLE clauses not allowed"),
 							 parser_errposition(cxt->pstate, con->location)));
 				saw_initially = true;
-				lastprimarycon->initdeferred = true;
+				saw_alwaysdeferred = true;
+				lastprimarycon->deferral = 'a';
+				if (saw_initially &&
+					lastprimarycon->deferral != 'a')
+					ereport(ERROR,
+							(errcode(ERRCODE_SYNTAX_ERROR),
+							 errmsg("constraint declared INITIALLY IMMEDIATE must not be ALWAYS DEFERRED"),
+							 parser_errposition(cxt->pstate, con->location)));
+				break;
 
-				/*
-				 * If only INITIALLY DEFERRED appears, assume DEFERRABLE
-				 */
-				if (!saw_deferrability)
-					lastprimarycon->deferrable = true;
-				else if (!lastprimarycon->deferrable)
+			case CONSTR_ATTR_DEFERRED:
+				if (!SUPPORTS_ATTRS(lastprimarycon))
 					ereport(ERROR,
 							(errcode(ERRCODE_SYNTAX_ERROR),
-							 errmsg("constraint declared INITIALLY DEFERRED must be DEFERRABLE"),
+							 errmsg("misplaced INITIALLY DEFERRED clause"),
 							 parser_errposition(cxt->pstate, con->location)));
+				if (saw_initially)
+					ereport(ERROR,
+							(errcode(ERRCODE_SYNTAX_ERROR),
+							 errmsg("multiple INITIALLY IMMEDIATE/DEFERRED clauses not allowed"),
+							 parser_errposition(cxt->pstate, con->location)));
+				saw_initially = true;
+				lastprimarycon->deferral = 'i';
 				break;
 
 			case CONSTR_ATTR_IMMEDIATE:
@@ -3353,14 +3374,17 @@ transformConstraintAttrs(CreateStmtContext *cxt, List *constraintList)
 							 errmsg("multiple INITIALLY IMMEDIATE/DEFERRED clauses not allowed"),
 							 parser_errposition(cxt->pstate, con->location)));
 				saw_initially = true;
-				lastprimarycon->initdeferred = false;
+				lastprimarycon->deferral = 'd';
 				break;
 
 			default:
 				/* Otherwise it's not an attribute */
 				lastprimarycon = con;
+				lastprimarycon->deferral = 'n';
 				/* reset flags for new primary node */
-				saw_deferrability = false;
+				saw_deferrable = false;
+				saw_notdeferrable = false;
+				saw_alwaysdeferred = false;
 				saw_initially = false;
 				break;
 		}
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 065238b..3eafdf5 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -941,13 +941,15 @@ pg_get_triggerdef_worker(Oid trigid, bool pretty)
 		if (OidIsValid(trigrec->tgconstrrelid))
 			appendStringInfo(&buf, "FROM %s ",
 							 generate_relation_name(trigrec->tgconstrrelid, NIL));
-		if (!trigrec->tgdeferrable)
+		if (trigrec->tgdeferral == 'n')
 			appendStringInfoString(&buf, "NOT ");
 		appendStringInfoString(&buf, "DEFERRABLE INITIALLY ");
-		if (trigrec->tginitdeferred)
+		if (trigrec->tgdeferral == 'i' || trigrec->tgdeferral == 'a')
 			appendStringInfoString(&buf, "DEFERRED ");
-		else
+		else if (trigrec->tgdeferral == 'd')
 			appendStringInfoString(&buf, "IMMEDIATE ");
+		if (trigrec->tgdeferral == 'a')
+			appendStringInfoString(&buf, "ALWAYS DEFERRED");
 	}
 
 	value = fastgetattr(ht_trig, Anum_pg_trigger_tgoldtable,
@@ -2206,10 +2208,12 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
 			break;
 	}
 
-	if (conForm->condeferrable)
+	if (conForm->condeferral != 'n')
 		appendStringInfoString(&buf, " DEFERRABLE");
-	if (conForm->condeferred)
+	if (conForm->condeferral == 'i')
 		appendStringInfoString(&buf, " INITIALLY DEFERRED");
+	if (conForm->condeferral == 'a')
+		appendStringInfoString(&buf, " ALWAYS DEFERRED");
 	if (!conForm->convalidated)
 		appendStringInfoString(&buf, " NOT VALID");
 
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index ea2f022..6505580 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -6729,8 +6729,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 				i_indisreplident,
 				i_contype,
 				i_conname,
-				i_condeferrable,
-				i_condeferred,
+				i_condeferral,
 				i_contableoid,
 				i_conoid,
 				i_condef,
@@ -6785,7 +6784,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 							  "i.indkey, i.indisclustered, "
 							  "i.indisreplident, t.relpages, "
 							  "c.contype, c.conname, "
-							  "c.condeferrable, c.condeferred, "
+							  "c.condeferral, "
 							  "c.tableoid AS contableoid, "
 							  "c.oid AS conoid, "
 							  "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, "
@@ -6855,7 +6854,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 							  "i.indkey, i.indisclustered, "
 							  "false AS indisreplident, t.relpages, "
 							  "c.contype, c.conname, "
-							  "c.condeferrable, c.condeferred, "
+							  "c.condeferral, "
 							  "c.tableoid AS contableoid, "
 							  "c.oid AS conoid, "
 							  "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, "
@@ -6884,7 +6883,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 							  "i.indkey, i.indisclustered, "
 							  "false AS indisreplident, t.relpages, "
 							  "c.contype, c.conname, "
-							  "c.condeferrable, c.condeferred, "
+							  "c.condeferral, "
 							  "c.tableoid AS contableoid, "
 							  "c.oid AS conoid, "
 							  "null AS condef, "
@@ -6916,7 +6915,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 							  "i.indkey, i.indisclustered, "
 							  "false AS indisreplident, t.relpages, "
 							  "c.contype, c.conname, "
-							  "c.condeferrable, c.condeferred, "
+							  "c.condeferral, "
 							  "c.tableoid AS contableoid, "
 							  "c.oid AS conoid, "
 							  "null AS condef, "
@@ -6953,8 +6952,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 		i_relpages = PQfnumber(res, "relpages");
 		i_contype = PQfnumber(res, "contype");
 		i_conname = PQfnumber(res, "conname");
-		i_condeferrable = PQfnumber(res, "condeferrable");
-		i_condeferred = PQfnumber(res, "condeferred");
+		i_condeferral = PQfnumber(res, "condeferral");
 		i_contableoid = PQfnumber(res, "contableoid");
 		i_conoid = PQfnumber(res, "conoid");
 		i_condef = PQfnumber(res, "condef");
@@ -7014,8 +7012,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 					constrinfo[j].condef = NULL;
 				constrinfo[j].confrelid = InvalidOid;
 				constrinfo[j].conindex = indxinfo[j].dobj.dumpId;
-				constrinfo[j].condeferrable = *(PQgetvalue(res, j, i_condeferrable)) == 't';
-				constrinfo[j].condeferred = *(PQgetvalue(res, j, i_condeferred)) == 't';
+				constrinfo[j].condeferral = *(PQgetvalue(res, j, i_condeferral));
 				constrinfo[j].conislocal = true;
 				constrinfo[j].separate = true;
 
@@ -7184,8 +7181,7 @@ getConstraints(Archive *fout, TableInfo tblinfo[], int numTables)
 			constrinfo[j].condef = pg_strdup(PQgetvalue(res, j, i_condef));
 			constrinfo[j].confrelid = atooid(PQgetvalue(res, j, i_confrelid));
 			constrinfo[j].conindex = 0;
-			constrinfo[j].condeferrable = false;
-			constrinfo[j].condeferred = false;
+			constrinfo[j].condeferral = 'n';
 			constrinfo[j].conislocal = true;
 			constrinfo[j].separate = true;
 		}
@@ -7264,8 +7260,7 @@ getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
 		constrinfo[i].condef = pg_strdup(PQgetvalue(res, i, i_consrc));
 		constrinfo[i].confrelid = InvalidOid;
 		constrinfo[i].conindex = 0;
-		constrinfo[i].condeferrable = false;
-		constrinfo[i].condeferred = false;
+		constrinfo[i].condeferral = 'n';
 		constrinfo[i].conislocal = true;
 
 		constrinfo[i].separate = !validated;
@@ -7425,8 +7420,7 @@ getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
 				i_tgconstrrelid,
 				i_tgconstrrelname,
 				i_tgenabled,
-				i_tgdeferrable,
-				i_tginitdeferred,
+				i_tgdeferral,
 				i_tgdef;
 	int			ntups;
 
@@ -7470,8 +7464,8 @@ getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
 							  "SELECT tgname, "
 							  "tgfoid::pg_catalog.regproc AS tgfname, "
 							  "tgtype, tgnargs, tgargs, tgenabled, "
-							  "tgisconstraint, tgconstrname, tgdeferrable, "
-							  "tgconstrrelid, tginitdeferred, tableoid, oid, "
+							  "tgisconstraint, tgconstrname, tgdeferral, "
+							  "tgconstrrelid, tableoid, oid, "
 							  "tgconstrrelid::pg_catalog.regclass AS tgconstrrelname "
 							  "FROM pg_catalog.pg_trigger t "
 							  "WHERE tgrelid = '%u'::pg_catalog.oid "
@@ -7489,8 +7483,8 @@ getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
 							  "SELECT tgname, "
 							  "tgfoid::pg_catalog.regproc AS tgfname, "
 							  "tgtype, tgnargs, tgargs, tgenabled, "
-							  "tgisconstraint, tgconstrname, tgdeferrable, "
-							  "tgconstrrelid, tginitdeferred, tableoid, oid, "
+							  "tgisconstraint, tgconstrname, tgdeferral, "
+							  "tgconstrrelid, tableoid, oid, "
 							  "tgconstrrelid::pg_catalog.regclass AS tgconstrrelname "
 							  "FROM pg_catalog.pg_trigger t "
 							  "WHERE tgrelid = '%u'::pg_catalog.oid "
@@ -7518,8 +7512,7 @@ getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
 		i_tgconstrrelid = PQfnumber(res, "tgconstrrelid");
 		i_tgconstrrelname = PQfnumber(res, "tgconstrrelname");
 		i_tgenabled = PQfnumber(res, "tgenabled");
-		i_tgdeferrable = PQfnumber(res, "tgdeferrable");
-		i_tginitdeferred = PQfnumber(res, "tginitdeferred");
+		i_tgdeferral = PQfnumber(res, "tgdeferral");
 		i_tgdef = PQfnumber(res, "tgdef");
 
 		tginfo = (TriggerInfo *) pg_malloc(ntups * sizeof(TriggerInfo));
@@ -7547,8 +7540,7 @@ getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
 				tginfo[j].tgnargs = 0;
 				tginfo[j].tgargs = NULL;
 				tginfo[j].tgisconstraint = false;
-				tginfo[j].tgdeferrable = false;
-				tginfo[j].tginitdeferred = false;
+				tginfo[j].tgdeferral = 'n';
 				tginfo[j].tgconstrname = NULL;
 				tginfo[j].tgconstrrelid = InvalidOid;
 				tginfo[j].tgconstrrelname = NULL;
@@ -7562,8 +7554,7 @@ getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
 				tginfo[j].tgnargs = atoi(PQgetvalue(res, j, i_tgnargs));
 				tginfo[j].tgargs = pg_strdup(PQgetvalue(res, j, i_tgargs));
 				tginfo[j].tgisconstraint = *(PQgetvalue(res, j, i_tgisconstraint)) == 't';
-				tginfo[j].tgdeferrable = *(PQgetvalue(res, j, i_tgdeferrable)) == 't';
-				tginfo[j].tginitdeferred = *(PQgetvalue(res, j, i_tginitdeferred)) == 't';
+				tginfo[j].tgdeferral = *(PQgetvalue(res, j, i_tgdeferral));
 
 				if (tginfo[j].tgisconstraint)
 				{
@@ -8492,8 +8483,7 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
 				constrs[j].condef = pg_strdup(PQgetvalue(res, j, 3));
 				constrs[j].confrelid = InvalidOid;
 				constrs[j].conindex = 0;
-				constrs[j].condeferrable = false;
-				constrs[j].condeferred = false;
+				constrs[j].condeferral = 'n';
 				constrs[j].conislocal = (PQgetvalue(res, j, 4)[0] == 't');
 
 				/*
@@ -16386,13 +16376,18 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo)
 				appendPQExpBufferChar(q, ')');
 			}
 
-			if (coninfo->condeferrable)
+			if (coninfo->condeferral != 'n')
 			{
 				appendPQExpBufferStr(q, " DEFERRABLE");
-				if (coninfo->condeferred)
+				if (coninfo->condeferral == 'i' || coninfo->condeferral == 'a')
 					appendPQExpBufferStr(q, " INITIALLY DEFERRED");
 			}
 
+			if (coninfo->condeferral == 'a')
+			{
+				appendPQExpBufferStr(q, " ALWAYS DEFERRED");
+			}
+
 			appendPQExpBufferStr(q, ";\n");
 		}
 
@@ -17026,13 +17021,16 @@ dumpTrigger(Archive *fout, TriggerInfo *tginfo)
 				appendPQExpBuffer(query, "    FROM %s\n    ",
 								  tginfo->tgconstrrelname);
 			}
-			if (!tginfo->tgdeferrable)
+			if (tginfo->tgdeferral == 'n')
 				appendPQExpBufferStr(query, "NOT ");
 			appendPQExpBufferStr(query, "DEFERRABLE INITIALLY ");
-			if (tginfo->tginitdeferred)
-				appendPQExpBufferStr(query, "DEFERRED\n");
+			if (tginfo->tgdeferral == 'i' || tginfo->tgdeferral == 'a')
+				appendPQExpBufferStr(query, "DEFERRED");
 			else
-				appendPQExpBufferStr(query, "IMMEDIATE\n");
+				appendPQExpBufferStr(query, "IMMEDIATE");
+			if (tginfo->tgdeferral == 'a')
+				appendPQExpBufferStr(query, " ALWAYS DEFERRED");
+			appendPQExpBufferStr(query, "\n");
 		}
 
 		if (TRIGGER_FOR_ROW(tginfo->tgtype))
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index e96c662..dca62c7 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -409,8 +409,7 @@ typedef struct _triggerInfo
 	Oid			tgconstrrelid;
 	char	   *tgconstrrelname;
 	char		tgenabled;
-	bool		tgdeferrable;
-	bool		tginitdeferred;
+	char		tgdeferral;
 	char	   *tgdef;
 } TriggerInfo;
 
@@ -430,7 +429,7 @@ typedef struct _evttriggerInfo
  * use a different objType for foreign key constraints, to make it easier
  * to sort them the way we want.
  *
- * Note: condeferrable and condeferred are currently only valid for
+ * Note: condeferral is currently only valid for
  * unique/primary-key constraints.  Otherwise that info is in condef.
  */
 typedef struct _constraintInfo
@@ -442,8 +441,7 @@ typedef struct _constraintInfo
 	char	   *condef;			/* definition, if CHECK or FOREIGN KEY */
 	Oid			confrelid;		/* referenced table, if FOREIGN KEY */
 	DumpId		conindex;		/* identifies associated index if any */
-	bool		condeferrable;	/* true if constraint is DEFERRABLE */
-	bool		condeferred;	/* true if constraint is INITIALLY DEFERRED */
+	char		condeferral;	/* 'n' for not deferrable, 'd' for deferrable, 'i' for initially deferred, 'a' for always deferred*/
 	bool		conislocal;		/* true if constraint has local definition */
 	bool		separate;		/* true if must dump as separate item */
 } ConstraintInfo;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index e5b3c1e..6ebc7b4 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -2077,21 +2077,15 @@ describeOneTableDetails(const char *schemaname,
 			appendPQExpBufferStr(&buf, "true AS indisvalid,\n");
 		if (pset.sversion >= 90000)
 			appendPQExpBufferStr(&buf,
-								 "  (NOT i.indimmediate) AND "
-								 "EXISTS (SELECT 1 FROM pg_catalog.pg_constraint "
+								 "(SELECT CASE WHEN i.indimmediate THEN 'n' ELSE condeferral END FROM pg_catalog.pg_constraint "
 								 "WHERE conrelid = i.indrelid AND "
 								 "conindid = i.indexrelid AND "
-								 "contype IN ('p','u','x') AND "
-								 "condeferrable) AS condeferrable,\n"
-								 "  (NOT i.indimmediate) AND "
-								 "EXISTS (SELECT 1 FROM pg_catalog.pg_constraint "
-								 "WHERE conrelid = i.indrelid AND "
-								 "conindid = i.indexrelid AND "
-								 "contype IN ('p','u','x') AND "
-								 "condeferred) AS condeferred,\n");
+								 "contype IN ('p','u','x') "
+								 "UNION SELECT 'n' ORDER BY 1 ASC) AS condeferral,\n"
+								 );
 		else
 			appendPQExpBufferStr(&buf,
-								 "  false AS condeferrable, false AS condeferred,\n");
+								 "  'n' AS condeferral,\n");
 
 		if (pset.sversion >= 90400)
 			appendPQExpBuffer(&buf, "i.indisreplident,\n");
@@ -2119,12 +2113,11 @@ describeOneTableDetails(const char *schemaname,
 			char	   *indisprimary = PQgetvalue(result, 0, 1);
 			char	   *indisclustered = PQgetvalue(result, 0, 2);
 			char	   *indisvalid = PQgetvalue(result, 0, 3);
-			char	   *deferrable = PQgetvalue(result, 0, 4);
-			char	   *deferred = PQgetvalue(result, 0, 5);
-			char	   *indisreplident = PQgetvalue(result, 0, 6);
-			char	   *indamname = PQgetvalue(result, 0, 7);
-			char	   *indtable = PQgetvalue(result, 0, 8);
-			char	   *indpred = PQgetvalue(result, 0, 9);
+			char	   *deferral = PQgetvalue(result, 0, 4);
+			char	   *indisreplident = PQgetvalue(result, 0, 5);
+			char	   *indamname = PQgetvalue(result, 0, 6);
+			char	   *indtable = PQgetvalue(result, 0, 7);
+			char	   *indpred = PQgetvalue(result, 0, 8);
 
 			if (strcmp(indisprimary, "t") == 0)
 				printfPQExpBuffer(&tmpbuf, _("primary key, "));
@@ -2147,12 +2140,15 @@ describeOneTableDetails(const char *schemaname,
 			if (strcmp(indisvalid, "t") != 0)
 				appendPQExpBufferStr(&tmpbuf, _(", invalid"));
 
-			if (strcmp(deferrable, "t") == 0)
+			if (*deferral != 'n')
 				appendPQExpBufferStr(&tmpbuf, _(", deferrable"));
 
-			if (strcmp(deferred, "t") == 0)
+			if (*deferral == 'i' || *deferral == 'a')
 				appendPQExpBufferStr(&tmpbuf, _(", initially deferred"));
 
+			if (*deferral == 'a')
+				appendPQExpBufferStr(&tmpbuf, _(", always deferred"));
+
 			if (strcmp(indisreplident, "t") == 0)
 				appendPQExpBuffer(&tmpbuf, _(", replica identity"));
 
@@ -2185,11 +2181,11 @@ describeOneTableDetails(const char *schemaname,
 			if (pset.sversion >= 90000)
 				appendPQExpBufferStr(&buf,
 									 "pg_catalog.pg_get_constraintdef(con.oid, true), "
-									 "contype, condeferrable, condeferred");
+									 "contype, coalesce(condeferral, 'n')");
 			else
 				appendPQExpBufferStr(&buf,
 									 "null AS constraintdef, null AS contype, "
-									 "false AS condeferrable, false AS condeferred");
+									 "'n' AS condeferral");
 			if (pset.sversion >= 90400)
 				appendPQExpBufferStr(&buf, ", i.indisreplident");
 			else
@@ -2230,6 +2226,7 @@ describeOneTableDetails(const char *schemaname,
 					{
 						const char *indexdef;
 						const char *usingpos;
+						char deferral;
 
 						/* Label as primary key or unique (but not both) */
 						if (strcmp(PQgetvalue(result, i, 1), "t") == 0)
@@ -2250,11 +2247,15 @@ describeOneTableDetails(const char *schemaname,
 						appendPQExpBuffer(&buf, " %s", indexdef);
 
 						/* Need these for deferrable PK/UNIQUE indexes */
-						if (strcmp(PQgetvalue(result, i, 8), "t") == 0)
+						deferral = *PQgetvalue(result, i, 8);
+						if (deferral != 'n')
 							appendPQExpBufferStr(&buf, " DEFERRABLE");
 
-						if (strcmp(PQgetvalue(result, i, 9), "t") == 0)
+						if (deferral == 'i')
 							appendPQExpBufferStr(&buf, " INITIALLY DEFERRED");
+
+						if (deferral == 'a')
+							appendPQExpBufferStr(&buf, " ALWAYS DEFERRED");
 					}
 
 					/* Add these for all cases */
@@ -2264,7 +2265,7 @@ describeOneTableDetails(const char *schemaname,
 					if (strcmp(PQgetvalue(result, i, 4), "t") != 0)
 						appendPQExpBufferStr(&buf, " INVALID");
 
-					if (strcmp(PQgetvalue(result, i, 10), "t") == 0)
+					if (strcmp(PQgetvalue(result, i, 9), "t") == 0)
 						appendPQExpBuffer(&buf, " REPLICA IDENTITY");
 
 					printTableAddFooter(&cont, buf.data);
@@ -2272,7 +2273,7 @@ describeOneTableDetails(const char *schemaname,
 					/* Print tablespace of the index on the same line */
 					if (pset.sversion >= 80000)
 						add_tablespace_footer(&cont, RELKIND_INDEX,
-											  atooid(PQgetvalue(result, i, 11)),
+											  atooid(PQgetvalue(result, i, 10)),
 											  false);
 				}
 			}
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 7bb47ea..acb7a7a 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -2766,10 +2766,10 @@ psql_completion(const char *text, int start, int end)
 	else if (TailMatches7("CREATE", "TRIGGER", MatchAny, "INSTEAD", "OF", MatchAny, "ON"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);
 	else if (HeadMatches2("CREATE", "TRIGGER") && TailMatches2("ON", MatchAny))
-		COMPLETE_WITH_LIST7("NOT DEFERRABLE", "DEFERRABLE", "INITIALLY",
+		COMPLETE_WITH_LIST8("ALWAYS DEFERRABLE", "NOT DEFERRABLE", "DEFERRABLE", "INITIALLY",
 							"REFERENCING", "FOR", "WHEN (", "EXECUTE PROCEDURE");
 	else if (HeadMatches2("CREATE", "TRIGGER") &&
-			 (TailMatches1("DEFERRABLE") || TailMatches2("INITIALLY", "IMMEDIATE|DEFERRED")))
+			 (TailMatches2("ALLWAYS", "DEFERRABLE") || TailMatches1("DEFERRABLE") || TailMatches2("INITIALLY", "IMMEDIATE|DEFERRED")))
 		COMPLETE_WITH_LIST4("REFERENCING", "FOR", "WHEN (", "EXECUTE PROCEDURE");
 	else if (HeadMatches2("CREATE", "TRIGGER") && TailMatches1("REFERENCING"))
 		COMPLETE_WITH_LIST2("OLD TABLE", "NEW TABLE");
diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h
index f20c5f7..cb1614e 100644
--- a/src/include/catalog/index.h
+++ b/src/include/catalog/index.h
@@ -73,8 +73,9 @@ extern Oid index_create(Relation heapRelation,
 #define	INDEX_CONSTR_CREATE_MARK_AS_PRIMARY	(1 << 0)
 #define	INDEX_CONSTR_CREATE_DEFERRABLE		(1 << 1)
 #define	INDEX_CONSTR_CREATE_INIT_DEFERRED	(1 << 2)
-#define	INDEX_CONSTR_CREATE_UPDATE_INDEX	(1 << 3)
-#define	INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS	(1 << 4)
+#define	INDEX_CONSTR_CREATE_ALWAYS_DEFERRED	(1 << 3)
+#define	INDEX_CONSTR_CREATE_UPDATE_INDEX	(1 << 4)
+#define	INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS	(1 << 5)
 
 extern ObjectAddress index_constraint_create(Relation heapRelation,
 						Oid indexRelationId,
diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h
index 7c1c0e1..4190e59 100644
--- a/src/include/catalog/pg_constraint.h
+++ b/src/include/catalog/pg_constraint.h
@@ -43,8 +43,7 @@ CATALOG(pg_constraint,2606,ConstraintRelationId)
 	NameData	conname;		/* name of this constraint */
 	Oid			connamespace;	/* OID of namespace containing constraint */
 	char		contype;		/* constraint type; see codes below */
-	bool		condeferrable;	/* deferrable constraint? */
-	bool		condeferred;	/* deferred by default? */
+	char		condeferral;	/* constraint deferral option */
 	bool		convalidated;	/* constraint has been validated? */
 
 	/*
@@ -204,8 +203,7 @@ typedef struct ClonedConstraint
 extern Oid CreateConstraintEntry(const char *constraintName,
 					  Oid constraintNamespace,
 					  char constraintType,
-					  bool isDeferrable,
-					  bool isDeferred,
+					  char deferralOption,
 					  bool isValidated,
 					  Oid parentConstrId,
 					  Oid relId,
diff --git a/src/include/catalog/pg_trigger.h b/src/include/catalog/pg_trigger.h
index 951d7d8..82eb8f3 100644
--- a/src/include/catalog/pg_trigger.h
+++ b/src/include/catalog/pg_trigger.h
@@ -25,8 +25,8 @@
  *		pg_trigger definition.  cpp turns this into
  *		typedef struct FormData_pg_trigger
  *
- * Note: when tgconstraint is nonzero, tgconstrrelid, tgconstrindid,
- * tgdeferrable, and tginitdeferred are largely redundant with the referenced
+ * Note: when tgconstraint is nonzero, tgconstrrelid, tgconstrindid, and
+ * tgdeferral are largely redundant with the referenced
  * pg_constraint entry.  However, it is possible for a non-deferrable trigger
  * to be associated with a deferrable constraint.
  * ----------------
@@ -44,8 +44,7 @@ CATALOG(pg_trigger,2620,TriggerRelationId)
 	Oid			tgconstrrelid;	/* constraint's FROM table, if any */
 	Oid			tgconstrindid;	/* constraint's supporting index, if any */
 	Oid			tgconstraint;	/* associated pg_constraint entry, if any */
-	bool		tgdeferrable;	/* constraint trigger is deferrable */
-	bool		tginitdeferred; /* constraint trigger is deferred initially */
+	char		tgdeferral;	/* constraint trigger deferral option */
 	int16		tgnargs;		/* # of extra arguments in tgargs */
 
 	/*
diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h
index a5b8610..4cd93ef 100644
--- a/src/include/commands/trigger.h
+++ b/src/include/commands/trigger.h
@@ -112,6 +112,7 @@ typedef struct TransitionCaptureState
 
 #define AFTER_TRIGGER_DEFERRABLE		0x00000020
 #define AFTER_TRIGGER_INITDEFERRED		0x00000040
+#define AFTER_TRIGGER_ALWAYSDEFERRED		0x00000080
 
 #define TRIGGER_FIRED_BY_INSERT(event) \
 	(((event) & TRIGGER_EVENT_OPMASK) == TRIGGER_EVENT_INSERT)
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 6390f7e..e4b4736 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2071,7 +2071,8 @@ typedef enum ConstrType			/* types of constraints */
 	CONSTR_ATTR_DEFERRABLE,		/* attributes for previous constraint node */
 	CONSTR_ATTR_NOT_DEFERRABLE,
 	CONSTR_ATTR_DEFERRED,
-	CONSTR_ATTR_IMMEDIATE
+	CONSTR_ATTR_IMMEDIATE,
+	CONSTR_ATTR_ALWAYS_DEFERRED
 } ConstrType;
 
 /* Foreign key action codes */
@@ -2093,8 +2094,7 @@ typedef struct Constraint
 
 	/* Fields used for most/all constraint types: */
 	char	   *conname;		/* Constraint name, or NULL if unnamed */
-	bool		deferrable;		/* DEFERRABLE? */
-	bool		initdeferred;	/* INITIALLY DEFERRED? */
+	char		deferral;		/* deferral option */
 	int			location;		/* token location, or -1 if unknown */
 
 	/* Fields used for constraints with expressions (CHECK and DEFAULT): */
@@ -2380,8 +2380,7 @@ typedef struct CreateTrigStmt
 	/* explicitly named transition data */
 	List	   *transitionRels; /* TriggerTransition nodes, or NIL if none */
 	/* The remaining fields are only used for constraint triggers */
-	bool		deferrable;		/* [NOT] DEFERRABLE */
-	bool		initdeferred;	/* INITIALLY {DEFERRED|IMMEDIATE} */
+	char		deferral;		/* deferral option */
 	RangeVar   *constrrel;		/* opposite relation, if RI trigger */
 } CreateTrigStmt;
 
@@ -2731,8 +2730,7 @@ typedef struct IndexStmt
 	bool		unique;			/* is index unique? */
 	bool		primary;		/* is index a primary key? */
 	bool		isconstraint;	/* is it for a pkey/unique constraint? */
-	bool		deferrable;		/* is the constraint DEFERRABLE? */
-	bool		initdeferred;	/* is the constraint INITIALLY DEFERRED? */
+	char		deferral;		/* constraint deferral option */
 	bool		transformed;	/* true when transformIndexStmt is finished */
 	bool		concurrent;		/* should this be a concurrent index build? */
 	bool		if_not_exists;	/* just do nothing if index already exists? */
diff --git a/src/include/utils/reltrigger.h b/src/include/utils/reltrigger.h
index 9b4dc7f..655ab8a 100644
--- a/src/include/utils/reltrigger.h
+++ b/src/include/utils/reltrigger.h
@@ -32,8 +32,7 @@ typedef struct Trigger
 	Oid			tgconstrrelid;
 	Oid			tgconstrindid;
 	Oid			tgconstraint;
-	bool		tgdeferrable;
-	bool		tginitdeferred;
+	char		tgdeferral;
 	int16		tgnargs;
 	int16		tgnattr;
 	int16	   *tgattr;
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 702bf9f..6732997 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -662,44 +662,44 @@ ALTER TABLE FKTABLE ALTER CONSTRAINT fkdd2 DEFERRABLE INITIALLY DEFERRED;
 ALTER TABLE FKTABLE ADD CONSTRAINT fkdi2 FOREIGN KEY(ftest1) REFERENCES pktable
   ON DELETE CASCADE ON UPDATE NO ACTION NOT DEFERRABLE;
 ALTER TABLE FKTABLE ALTER CONSTRAINT fkdi2 DEFERRABLE INITIALLY IMMEDIATE;
-SELECT conname, tgfoid::regproc, tgtype, tgdeferrable, tginitdeferred
+SELECT conname, tgfoid::regproc, tgtype, tgdeferral
 FROM pg_trigger JOIN pg_constraint con ON con.oid = tgconstraint
 WHERE tgrelid = 'pktable'::regclass
 ORDER BY 1,2,3;
- conname |         tgfoid         | tgtype | tgdeferrable | tginitdeferred 
----------+------------------------+--------+--------------+----------------
- fkdd    | "RI_FKey_cascade_del"  |      9 | f            | f
- fkdd    | "RI_FKey_noaction_upd" |     17 | t            | t
- fkdd2   | "RI_FKey_cascade_del"  |      9 | f            | f
- fkdd2   | "RI_FKey_noaction_upd" |     17 | t            | t
- fkdi    | "RI_FKey_cascade_del"  |      9 | f            | f
- fkdi    | "RI_FKey_noaction_upd" |     17 | t            | f
- fkdi2   | "RI_FKey_cascade_del"  |      9 | f            | f
- fkdi2   | "RI_FKey_noaction_upd" |     17 | t            | f
- fknd    | "RI_FKey_cascade_del"  |      9 | f            | f
- fknd    | "RI_FKey_noaction_upd" |     17 | f            | f
- fknd2   | "RI_FKey_cascade_del"  |      9 | f            | f
- fknd2   | "RI_FKey_noaction_upd" |     17 | f            | f
+ conname |         tgfoid         | tgtype | tgdeferral 
+---------+------------------------+--------+------------
+ fkdd    | "RI_FKey_cascade_del"  |      9 | n
+ fkdd    | "RI_FKey_noaction_upd" |     17 | i
+ fkdd2   | "RI_FKey_cascade_del"  |      9 | n
+ fkdd2   | "RI_FKey_noaction_upd" |     17 | i
+ fkdi    | "RI_FKey_cascade_del"  |      9 | n
+ fkdi    | "RI_FKey_noaction_upd" |     17 | d
+ fkdi2   | "RI_FKey_cascade_del"  |      9 | n
+ fkdi2   | "RI_FKey_noaction_upd" |     17 | d
+ fknd    | "RI_FKey_cascade_del"  |      9 | n
+ fknd    | "RI_FKey_noaction_upd" |     17 | n
+ fknd2   | "RI_FKey_cascade_del"  |      9 | n
+ fknd2   | "RI_FKey_noaction_upd" |     17 | n
 (12 rows)
 
-SELECT conname, tgfoid::regproc, tgtype, tgdeferrable, tginitdeferred
+SELECT conname, tgfoid::regproc, tgtype, tgdeferral
 FROM pg_trigger JOIN pg_constraint con ON con.oid = tgconstraint
 WHERE tgrelid = 'fktable'::regclass
 ORDER BY 1,2,3;
- conname |       tgfoid        | tgtype | tgdeferrable | tginitdeferred 
----------+---------------------+--------+--------------+----------------
- fkdd    | "RI_FKey_check_ins" |      5 | t            | t
- fkdd    | "RI_FKey_check_upd" |     17 | t            | t
- fkdd2   | "RI_FKey_check_ins" |      5 | t            | t
- fkdd2   | "RI_FKey_check_upd" |     17 | t            | t
- fkdi    | "RI_FKey_check_ins" |      5 | t            | f
- fkdi    | "RI_FKey_check_upd" |     17 | t            | f
- fkdi2   | "RI_FKey_check_ins" |      5 | t            | f
- fkdi2   | "RI_FKey_check_upd" |     17 | t            | f
- fknd    | "RI_FKey_check_ins" |      5 | f            | f
- fknd    | "RI_FKey_check_upd" |     17 | f            | f
- fknd2   | "RI_FKey_check_ins" |      5 | f            | f
- fknd2   | "RI_FKey_check_upd" |     17 | f            | f
+ conname |       tgfoid        | tgtype | tgdeferral 
+---------+---------------------+--------+------------
+ fkdd    | "RI_FKey_check_ins" |      5 | i
+ fkdd    | "RI_FKey_check_upd" |     17 | i
+ fkdd2   | "RI_FKey_check_ins" |      5 | i
+ fkdd2   | "RI_FKey_check_upd" |     17 | i
+ fkdi    | "RI_FKey_check_ins" |      5 | d
+ fkdi    | "RI_FKey_check_upd" |     17 | d
+ fkdi2   | "RI_FKey_check_ins" |      5 | d
+ fkdi2   | "RI_FKey_check_upd" |     17 | d
+ fknd    | "RI_FKey_check_ins" |      5 | n
+ fknd    | "RI_FKey_check_upd" |     17 | n
+ fknd2   | "RI_FKey_check_ins" |      5 | n
+ fknd2   | "RI_FKey_check_upd" |     17 | n
 (12 rows)
 
 -- temp tables should go away by themselves, need not drop them.
diff --git a/src/test/regress/input/constraints.source b/src/test/regress/input/constraints.source
index 98dd421..68530fb 100644
--- a/src/test/regress/input/constraints.source
+++ b/src/test/regress/input/constraints.source
@@ -437,8 +437,59 @@ COMMIT;
 
 SELECT * FROM unique_tbl;
 
+-- test ALWAYS DEFERRED
+ALTER TABLE unique_tbl DROP CONSTRAINT unique_tbl_i_key;
+ALTER TABLE unique_tbl ADD CONSTRAINT unique_tbl_i_key
+	UNIQUE (i) ALWAYS DEFERRED;
+
+BEGIN;
+-- should fail at commit time
+UPDATE unique_tbl SET i = 2 WHERE i = 1;
+COMMIT; -- should fail
+
+BEGIN;
+-- should fail at commit time
+SET CONSTRAINTS ALL IMMEDIATE;
+UPDATE unique_tbl SET i = 2 WHERE i = 1;
+COMMIT; -- should fail
+
+BEGIN;
+SET CONSTRAINTS unique_tbl_i_key IMMEDIATE; -- should fail
+ROLLBACK;
+
 DROP TABLE unique_tbl;
 
+CREATE FUNCTION deferred_trigger_test() RETURNS TRIGGER AS $$
+  BEGIN
+    RAISE EXCEPTION 'deferred_trigger_test() ran';
+  END
+$$ language plpgsql;
+
+CREATE TABLE deferred_trigger_test_tbl (a TEXT);
+
+CREATE CONSTRAINT TRIGGER deferred_trigger_test_constraint
+AFTER INSERT OR UPDATE OR DELETE
+ON deferred_trigger_test_tbl
+ALWAYS DEFERRED
+FOR EACH ROW
+EXECUTE PROCEDURE deferred_trigger_test();
+
+BEGIN;
+INSERT INTO deferred_trigger_test_tbl (a) SELECT 'foo';
+COMMIT; -- should fail
+
+BEGIN;
+SET CONSTRAINTS ALL IMMEDIATE;
+INSERT INTO deferred_trigger_test_tbl (a) SELECT 'foo';
+COMMIT; -- should fail
+
+BEGIN;
+SET CONSTRAINTS deferred_trigger_test_constraint IMMEDIATE; -- should fail
+ROLLBACK;
+
+DROP TABLE deferred_trigger_test_tbl CASCADE;
+DROP FUNCTION deferred_trigger_test();
+
 --
 -- EXCLUDE constraints
 --
diff --git a/src/test/regress/output/constraints.source b/src/test/regress/output/constraints.source
index a6a1df1..eefe8c0 100644
--- a/src/test/regress/output/constraints.source
+++ b/src/test/regress/output/constraints.source
@@ -618,7 +618,57 @@ SELECT * FROM unique_tbl;
  3 | threex
 (5 rows)
 
+-- test ALWAYS DEFERRED
+ALTER TABLE unique_tbl DROP CONSTRAINT unique_tbl_i_key;
+ALTER TABLE unique_tbl ADD CONSTRAINT unique_tbl_i_key
+	UNIQUE (i) ALWAYS DEFERRED;
+BEGIN;
+-- should fail at commit time
+UPDATE unique_tbl SET i = 2 WHERE i = 1;
+COMMIT; -- should fail
+ERROR:  duplicate key value violates unique constraint "unique_tbl_i_key"
+DETAIL:  Key (i)=(2) already exists.
+BEGIN;
+-- should fail at commit time
+SET CONSTRAINTS ALL IMMEDIATE;
+UPDATE unique_tbl SET i = 2 WHERE i = 1;
+COMMIT; -- should fail
+ERROR:  duplicate key value violates unique constraint "unique_tbl_i_key"
+DETAIL:  Key (i)=(2) already exists.
+BEGIN;
+SET CONSTRAINTS unique_tbl_i_key IMMEDIATE; -- should fail
+ERROR:  constraint "unique_tbl_i_key" is always deferred
+ROLLBACK;
 DROP TABLE unique_tbl;
+CREATE FUNCTION deferred_trigger_test() RETURNS TRIGGER AS $$
+  BEGIN
+    RAISE EXCEPTION 'deferred_trigger_test() ran';
+  END
+$$ language plpgsql;
+CREATE TABLE deferred_trigger_test_tbl (a TEXT);
+CREATE CONSTRAINT TRIGGER deferred_trigger_test_constraint
+AFTER INSERT OR UPDATE OR DELETE
+ON deferred_trigger_test_tbl
+ALWAYS DEFERRED
+FOR EACH ROW
+EXECUTE PROCEDURE deferred_trigger_test();
+BEGIN;
+INSERT INTO deferred_trigger_test_tbl (a) SELECT 'foo';
+COMMIT; -- should fail
+ERROR:  deferred_trigger_test() ran
+CONTEXT:  PL/pgSQL function deferred_trigger_test() line 3 at RAISE
+BEGIN;
+SET CONSTRAINTS ALL IMMEDIATE;
+INSERT INTO deferred_trigger_test_tbl (a) SELECT 'foo';
+COMMIT; -- should fail
+ERROR:  deferred_trigger_test() ran
+CONTEXT:  PL/pgSQL function deferred_trigger_test() line 3 at RAISE
+BEGIN;
+SET CONSTRAINTS deferred_trigger_test_constraint IMMEDIATE; -- should fail
+ERROR:  constraint "deferred_trigger_test_constraint" is always deferred
+ROLLBACK;
+DROP TABLE deferred_trigger_test_tbl CASCADE;
+DROP FUNCTION deferred_trigger_test();
 --
 -- EXCLUDE constraints
 --
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index d508a69..c41249f 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -502,11 +502,11 @@ ALTER TABLE FKTABLE ADD CONSTRAINT fkdi2 FOREIGN KEY(ftest1) REFERENCES pktable
   ON DELETE CASCADE ON UPDATE NO ACTION NOT DEFERRABLE;
 ALTER TABLE FKTABLE ALTER CONSTRAINT fkdi2 DEFERRABLE INITIALLY IMMEDIATE;
 
-SELECT conname, tgfoid::regproc, tgtype, tgdeferrable, tginitdeferred
+SELECT conname, tgfoid::regproc, tgtype, tgdeferral
 FROM pg_trigger JOIN pg_constraint con ON con.oid = tgconstraint
 WHERE tgrelid = 'pktable'::regclass
 ORDER BY 1,2,3;
-SELECT conname, tgfoid::regproc, tgtype, tgdeferrable, tginitdeferred
+SELECT conname, tgfoid::regproc, tgtype, tgdeferral
 FROM pg_trigger JOIN pg_constraint con ON con.oid = tgconstraint
 WHERE tgrelid = 'fktable'::regclass
 ORDER BY 1,2,3;
-- 
2.7.4

